Received: by 2002:a25:7ec1:0:0:0:0:0 with SMTP id z184csp846538ybc; Fri, 22 Nov 2019 14:14:50 -0800 (PST) X-Google-Smtp-Source: APXvYqyQ/33dKKJUNB6p8XE8lj4U8wontf5EZ3Q5M6aR667aBPyAuwMG9f6CiqjiyjxP4KkPiqpt X-Received: by 2002:a17:906:1d59:: with SMTP id o25mr24345611ejh.17.1574460890297; Fri, 22 Nov 2019 14:14:50 -0800 (PST) ARC-Seal: i=1; a=rsa-sha256; t=1574460890; cv=none; d=google.com; s=arc-20160816; b=GSjhaC15coFlYp/y/qkUJOnbmj94F/FoUfKnziluor6eRqeBmkUJvtuEWWvzUnupLM z/LZbPs+Yiejyu1igjROMh6Kbk5AnG3CHZe/oyHwr+t/9Vae8I5GCeI0uV1zGRrn66qU mjcb2lUkZUArCIy/t7WZko+AkS2dMLLzUMIa71Dgcb4mmXX/UUxyx9e8BTDBwuOXHUPs JkRa7wa3WYoBJMgCUvIyRO/UImItBVms4JjiMV6erwtQxp+icEVBdi25hEdy/FoTpg0i OwiUm7LyYHCo+o9OryqSFTfYlH+Vji1nl9nRRx3VMWb7Q3/TUD+umwgKwECoCkNSG9vt OE1Q== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:message-id:in-reply-to :subject:cc:to:from:date; bh=HgwIluIUK9yMjl6uRW5T1qK8PGthwUq/5XDiEsMCT/4=; b=VakKQh0C4/+8ER3JqbzgZ5LaCOnb5AWPE8X1mWMcwlevtNPaItKhRFYTR6EoAs9WWp 39zCFn0xGG+KbUN9VUUlTLEYt2rGc6ECpoeQ5m1jeveqSQm9AS3ipdvSffBy6ZlII16X ZwtJ5P7VlyPdIGFLRCRIezyMd6Qwz2VHgE5m+AgB8GjfXP38ugTKCMzKoK5K1FTQf3RP RaCSlkbJM7RI2UABLJaCo5NUDt+b+6qTfEafPYFG0mBIjpUsYSfrpJlPspp+JxKQeaJP EMzJRJKJJmH0BpJM+0gTzekKMxiFqfMW3aXUeUzgBgZ/+3rxjim7ED/aP4fn+mnRdmof Sz8A== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id x17si5375869edq.340.2019.11.22.14.14.25; Fri, 22 Nov 2019 14:14:50 -0800 (PST) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726776AbfKVWNV (ORCPT + 99 others); Fri, 22 Nov 2019 17:13:21 -0500 Received: from iolanthe.rowland.org ([192.131.102.54]:60356 "HELO iolanthe.rowland.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with SMTP id S1726638AbfKVWNV (ORCPT ); Fri, 22 Nov 2019 17:13:21 -0500 Received: (qmail 8878 invoked by uid 2102); 22 Nov 2019 17:13:20 -0500 Received: from localhost (sendmail-bs@127.0.0.1) by localhost with SMTP; 22 Nov 2019 17:13:20 -0500 Date: Fri, 22 Nov 2019 17:13:20 -0500 (EST) From: Alan Stern X-X-Sender: stern@iolanthe.rowland.org To: Pete Zaitcev cc: syzbot , , , , , , Kernel development list , USB list , , , Subject: Re: possible deadlock in mon_bin_vma_fault In-Reply-To: <20191122145243.6ece9bed@suzdal.zaitcev.lan> Message-ID: MIME-Version: 1.0 Content-Type: TEXT/PLAIN; charset=US-ASCII Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org On Fri, 22 Nov 2019, Pete Zaitcev wrote: > > It would be more elegant to do the rp->mmap_active test before calling > > kcalloc and mon_alloc_buf. But of course that's a pretty minor thing. > > Indeed it feels wrong that so much work gets discarded. However, memory > allocations can block, right? In the same time, our main objective here is > to make sure that when a page fault happens, we fill in the page that VMA > is intended to refer, and not one that was re-allocated. Therefore, I'm > trying to avoid a situation where: > > 1. thread A checks mmap_active, finds it at zero and proceeds into the > reallocation ioctl > 2. thread A sleeps in get_free_page() > 3. thread B runs mmap() and succeeds > 4. thread A obtains its pages and proceeds to substitute the buffer > 5. thread B (or any other) pagefaults and ends with the new, unexpected page > > The code is not pretty, but I don't see an alternative. Heck, I would > love you to find more races if you can. The alternative is to have the routines for mmap() hold fetch_lock instead of b_lock. mmap() is allowed to sleep, so that would be okay. Then you would also hold fetch_lock while checking mmap_active and doing the memory allocations. That would prevent any races -- in your example above, thread A would acquire fetch_lock in step 1, so thread B would block in step 3 until step 4 was finished. Hence B would end up mapping the correct pages. In practice, I don't see this being a routine problem. How often do multiple threads independently try to mmap the same usbmon buffer? Still, let's see syzbot reacts to your current patch. The line below is how you ask syzbot to test a candidate patch. Alan Stern #syz test: linux-4.19.y f6e27dbb1afa commit 5252eb4c8297fedbf1c5f1e67da44efe00e6ef6b Author: Pete Zaitcev Date: Thu Nov 21 17:24:00 2019 -0600 usb: Fix a deadlock in usbmon between mmap and read Signed-off-by: Pete Zaitcev Reported-by: syzbot+56f9673bb4cdcbeb0e92@syzkaller.appspotmail.com diff --git a/drivers/usb/mon/mon_bin.c b/drivers/usb/mon/mon_bin.c index ac2b4fcc265f..f48a23adbc35 100644 --- a/drivers/usb/mon/mon_bin.c +++ b/drivers/usb/mon/mon_bin.c @@ -1039,12 +1039,18 @@ static long mon_bin_ioctl(struct file *file, unsigned int cmd, unsigned long arg mutex_lock(&rp->fetch_lock); spin_lock_irqsave(&rp->b_lock, flags); - mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); - kfree(rp->b_vec); - rp->b_vec = vec; - rp->b_size = size; - rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0; - rp->cnt_lost = 0; + if (rp->mmap_active) { + mon_free_buff(vec, size/CHUNK_SIZE); + kfree(vec); + ret = -EBUSY; + } else { + mon_free_buff(rp->b_vec, rp->b_size/CHUNK_SIZE); + kfree(rp->b_vec); + rp->b_vec = vec; + rp->b_size = size; + rp->b_read = rp->b_in = rp->b_out = rp->b_cnt = 0; + rp->cnt_lost = 0; + } spin_unlock_irqrestore(&rp->b_lock, flags); mutex_unlock(&rp->fetch_lock); } @@ -1216,13 +1222,21 @@ mon_bin_poll(struct file *file, struct poll_table_struct *wait) static void mon_bin_vma_open(struct vm_area_struct *vma) { struct mon_reader_bin *rp = vma->vm_private_data; + unsigned long flags; + + spin_lock_irqsave(&rp->b_lock, flags); rp->mmap_active++; + spin_unlock_irqrestore(&rp->b_lock, flags); } static void mon_bin_vma_close(struct vm_area_struct *vma) { + unsigned long flags; + struct mon_reader_bin *rp = vma->vm_private_data; + spin_lock_irqsave(&rp->b_lock, flags); rp->mmap_active--; + spin_unlock_irqrestore(&rp->b_lock, flags); } /* @@ -1234,16 +1248,12 @@ static vm_fault_t mon_bin_vma_fault(struct vm_fault *vmf) unsigned long offset, chunk_idx; struct page *pageptr; - mutex_lock(&rp->fetch_lock); offset = vmf->pgoff << PAGE_SHIFT; - if (offset >= rp->b_size) { - mutex_unlock(&rp->fetch_lock); + if (offset >= rp->b_size) return VM_FAULT_SIGBUS; - } chunk_idx = offset / CHUNK_SIZE; pageptr = rp->b_vec[chunk_idx].pg; get_page(pageptr); - mutex_unlock(&rp->fetch_lock); vmf->page = pageptr; return 0; }