[PATCH] usb: gadget: fix refcount race in gadget_dev_open()
From: Nguyen Quang Le Kien
Date: Tue Aug 11 2026 - 00:46:21 EST
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: syzbot+8496ab5e117502750445@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=8496ab5e117502750445
Signed-off-by: Nguyen Quang Le Kien <khiemtranzo532001@xxxxxxxxx>
---
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