Takashi Iwai 93ac02
From 19e6317d24c25ee737c65d1ffb7483bdda4bb54a Mon Sep 17 00:00:00 2001
Takashi Iwai 93ac02
From: Pete Zaitcev <zaitcev@redhat.com>
Takashi Iwai 93ac02
Date: Wed, 4 Dec 2019 20:39:41 -0600
Takashi Iwai 93ac02
Subject: [PATCH] usb: mon: Fix a deadlock in usbmon between mmap and read
Takashi Iwai 93ac02
Git-commit: 19e6317d24c25ee737c65d1ffb7483bdda4bb54a
Takashi Iwai 93ac02
Patch-mainline: v5.5-rc2
Takashi Iwai 93ac02
References: bsc#1051510
Takashi Iwai 93ac02
Takashi Iwai 93ac02
The problem arises because our read() function grabs a lock of the
Takashi Iwai 93ac02
circular buffer, finds something of interest, then invokes copy_to_user()
Takashi Iwai 93ac02
straight from the buffer, which in turn takes mm->mmap_sem. In the same
Takashi Iwai 93ac02
time, the callback mon_bin_vma_fault() is invoked under mm->mmap_sem.
Takashi Iwai 93ac02
It attempts to take the fetch lock and deadlocks.
Takashi Iwai 93ac02
Takashi Iwai 93ac02
This patch does away with protecting of our page list with any
Takashi Iwai 93ac02
semaphores, and instead relies on the kernel not close the device
Takashi Iwai 93ac02
while mmap is active in a process.
Takashi Iwai 93ac02
Takashi Iwai 93ac02
In addition, we prohibit re-sizing of a buffer while mmap is active.
Takashi Iwai 93ac02
This way, when (now unlocked) fault is processed, it works with the
Takashi Iwai 93ac02
page that is intended to be mapped-in, and not some other random page.
Takashi Iwai 93ac02
Note that this may have an ABI impact, but hopefully no legitimate
Takashi Iwai 93ac02
program is this wrong.
Takashi Iwai 93ac02
Takashi Iwai 93ac02
Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Takashi Iwai 93ac02
Reported-by: syzbot+56f9673bb4cdcbeb0e92@syzkaller.appspotmail.com
Takashi Iwai 93ac02
Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
Takashi Iwai 93ac02
Fixes: 46eb14a6e158 ("USB: fix usbmon BUG trigger")
Takashi Iwai 93ac02
Cc: <stable@vger.kernel.org>
Takashi Iwai 93ac02
Link: https://lore.kernel.org/r/20191204203941.3503452b@suzdal.zaitcev.lan
Takashi Iwai 93ac02
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Takashi Iwai 93ac02
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 93ac02
Takashi Iwai 93ac02
---
Takashi Iwai 93ac02
 drivers/usb/mon/mon_bin.c | 32 +++++++++++++++++++++-----------
Takashi Iwai 93ac02
 1 file changed, 21 insertions(+), 11 deletions(-)
Takashi Iwai 93ac02
Takashi Iwai 93ac02
diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c
Takashi Iwai 93ac02
index ac2b4fcc265f..f48a23adbc35 100644
Takashi Iwai 93ac02
--- a/drivers/usb/mon/mon_bin.c
Takashi Iwai 93ac02
+++ b/drivers/usb/mon/mon_bin.c
Takashi Iwai 93ac02
@@ -1039,12 +1039,18 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg
Takashi Iwai 93ac02
 
Takashi Iwai 93ac02
 		mutex_lock(&rp->fetch_lock);
Takashi Iwai 93ac02
 		spin_lock_irqsave(&rp->b_lock, flags);
Takashi Iwai 93ac02
-		mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE);
Takashi Iwai 93ac02
-		kfree(rp->b_vec);
Takashi Iwai 93ac02
-		rp->b_vec  = vec;
Takashi Iwai 93ac02
-		rp->b_size = size;
Takashi Iwai 93ac02
-		rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0;
Takashi Iwai 93ac02
-		rp->cnt_lost = 0;
Takashi Iwai 93ac02
+		if (rp->mmap_active) {
Takashi Iwai 93ac02
+			mon_free_buff(vec, size/CHUNK_SIZE);
Takashi Iwai 93ac02
+			kfree(vec);
Takashi Iwai 93ac02
+			ret = -EBUSY;
Takashi Iwai 93ac02
+		} else {
Takashi Iwai 93ac02
+			mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE);
Takashi Iwai 93ac02
+			kfree(rp->b_vec);
Takashi Iwai 93ac02
+			rp->b_vec  = vec;
Takashi Iwai 93ac02
+			rp->b_size = size;
Takashi Iwai 93ac02
+			rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0;
Takashi Iwai 93ac02
+			rp->cnt_lost = 0;
Takashi Iwai 93ac02
+		}
Takashi Iwai 93ac02
 		spin_unlock_irqrestore(&rp->b_lock, flags);
Takashi Iwai 93ac02
 		mutex_unlock(&rp->fetch_lock);
Takashi Iwai 93ac02
 		}
Takashi Iwai 93ac02
@@ -1216,13 +1222,21 @@ mon_bin_poll(struct file *file, struct poll_table_struct *wait)
Takashi Iwai 93ac02
 static void mon_bin_vma_open(struct vm_area_struct *vma)
Takashi Iwai 93ac02
 {
Takashi Iwai 93ac02
 	struct mon_reader_bin *rp = vma->vm_private_data;
Takashi Iwai 93ac02
+	unsigned long flags;
Takashi Iwai 93ac02
+
Takashi Iwai 93ac02
+	spin_lock_irqsave(&rp->b_lock, flags);
Takashi Iwai 93ac02
 	rp->mmap_active++;
Takashi Iwai 93ac02
+	spin_unlock_irqrestore(&rp->b_lock, flags);
Takashi Iwai 93ac02
 }
Takashi Iwai 93ac02
 
Takashi Iwai 93ac02
 static void mon_bin_vma_close(struct vm_area_struct *vma)
Takashi Iwai 93ac02
 {
Takashi Iwai 93ac02
+	unsigned long flags;
Takashi Iwai 93ac02
+
Takashi Iwai 93ac02
 	struct mon_reader_bin *rp = vma->vm_private_data;
Takashi Iwai 93ac02
+	spin_lock_irqsave(&rp->b_lock, flags);
Takashi Iwai 93ac02
 	rp->mmap_active--;
Takashi Iwai 93ac02
+	spin_unlock_irqrestore(&rp->b_lock, flags);
Takashi Iwai 93ac02
 }
Takashi Iwai 93ac02
 
Takashi Iwai 93ac02
 /*
Takashi Iwai 93ac02
@@ -1234,16 +1248,12 @@ static vm_fault_t mon_bin_vma_fault(struct vm_fault *vmf)
Takashi Iwai 93ac02
 	unsigned long offset, chunk_idx;
Takashi Iwai 93ac02
 	struct page *pageptr;
Takashi Iwai 93ac02
 
Takashi Iwai 93ac02
-	mutex_lock(&rp->fetch_lock);
Takashi Iwai 93ac02
 	offset = vmf->pgoff << PAGE_SHIFT;
Takashi Iwai 93ac02
-	if (offset >= rp->b_size) {
Takashi Iwai 93ac02
-		mutex_unlock(&rp->fetch_lock);
Takashi Iwai 93ac02
+	if (offset >= rp->b_size)
Takashi Iwai 93ac02
 		return VM_FAULT_SIGBUS;
Takashi Iwai 93ac02
-	}
Takashi Iwai 93ac02
 	chunk_idx = offset / CHUNK_SIZE;
Takashi Iwai 93ac02
 	pageptr = rp->b_vec[chunk_idx].pg;
Takashi Iwai 93ac02
 	get_page(pageptr);
Takashi Iwai 93ac02
-	mutex_unlock(&rp->fetch_lock);
Takashi Iwai 93ac02
 	vmf->page = pageptr;
Takashi Iwai 93ac02
 	return 0;
Takashi Iwai 93ac02
 }
Takashi Iwai 93ac02
-- 
Takashi Iwai 93ac02
2.16.4
Takashi Iwai 93ac02