Forwarded: Re: WARNING: refcount bug in trace_suspend_resume (2)
syzbot <[email protected]>
| Newsgroups | gmane.linux.kernel |
|---|---|
| Message-ID | <[email protected]> |
For archival purposes, forwarding an incoming command email to [email protected], [email protected]. *** Subject: Re: WARNING: refcount bug in trace_suspend_resume (2) Author: [email protected] #syz test: https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-next From dc76c5bf35d3e11a56c1402294d11297165fec21 Mon Sep 17 00:00:00 2001 From: Nguyen Quang Le Kien <[email protected]> Date: Tue, 11 Aug 2026 12:46:21 +0800 Subject: [PATCH] usb: gadget: fix refcount race in gadget_dev_open() gadget_dev_open() accesses dev->lock and calls get_dev() without holding sb_mutex, which is held by gadgetfs_kill_sb() when it calls put_dev(the_device) and frees the device. This creates a race where open() reads dev->lock after kill_sb() has already freed the dev_data object, resulting in a refcount underflow and use-after-free. Fix by wrapping gadget_dev_open() with scoped_guard(mutex, &sb_mutex) to serialise against gadgetfs_kill_sb(), and check the_device inside the guard before dereferencing dev. Reported-by: [email protected] Closes: https://syzkaller.appspot.com/bug?extid=8496ab5e117502750445 Signed-off-by: Nguyen Quang Le Kien <[email protected]> --- drivers/usb/gadget/legacy/inode.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index d87a8ab51..719ea5c18 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -1916,15 +1916,19 @@ gadget_dev_open (struct inode *inode, struct file *fd) struct dev_data *dev = inode->i_private; int value = -EBUSY; - spin_lock_irq(&dev->lock); - if (dev->state == STATE_DEV_DISABLED) { - dev->ev_next = 0; - dev->state = STATE_DEV_OPENED; - fd->private_data = dev; - get_dev (dev); - value = 0; + scoped_guard(mutex, &sb_mutex) { + if (!the_device) + return -ENODEV; + spin_lock_irq(&dev->lock); + if (dev->state == STATE_DEV_DISABLED) { + dev->ev_next = 0; + dev->state = STATE_DEV_OPENED; + fd->private_data = dev; + get_dev (dev); + value = 0; + } + spin_unlock_irq(&dev->lock); } - spin_unlock_irq(&dev->lock); return value; } -- 2.34.1