Skip to content

Commit d20fff0

Browse files
AlanSterngregkh
authored andcommitted
USB: gadgetfs: Fix crash caused by inadequate synchronization
commit 520b72fc64debf8a86c3853b8e486aa5982188f0 upstream. The gadgetfs driver (drivers/usb/gadget/legacy/inode.c) was written before the UDC and composite frameworks were adopted; it is a legacy driver. As such, it expects that once bound to a UDC controller, it will not be unbound until it unregisters itself. However, the UDC framework does unbind function drivers while they are still registered. When this happens, it can cause the gadgetfs driver to misbehave or crash. For example, userspace can cause a crash by opening the device file and doing an ioctl call before setting up a configuration (found by Andrey Konovalov using the syzkaller fuzzer). This patch adds checks and synchronization to prevent these bad behaviors. It adds a udc_usage counter that the driver increments at times when it is using a gadget interface without holding the private spinlock. The unbind routine waits for this counter to go to 0 before returning, thereby ensuring that the UDC is no longer in use. The patch also adds a check in the dev_ioctl() routine to make sure the driver is bound to a UDC before dereferencing the gadget pointer, and it makes destroy_ep_files() synchronize with the endpoint I/O routines, to prevent the user from accessing an endpoint data structure after it has been removed. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-by: Andrey Konovalov <andreyknvl@google.com> Tested-by: Andrey Konovalov <andreyknvl@google.com> Acked-by: Felipe Balbi <felipe.balbi@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1 parent c2eb312 commit d20fff0

1 file changed

Lines changed: 36 additions & 5 deletions

File tree

drivers/usb/gadget/legacy/inode.c

Lines changed: 36 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
#include <linux/mmu_context.h>
2828
#include <linux/aio.h>
2929
#include <linux/uio.h>
30-
30+
#include <linux/delay.h>
3131
#include <linux/device.h>
3232
#include <linux/moduleparam.h>
3333

@@ -116,6 +116,7 @@ enum ep0_state {
116116
struct dev_data {
117117
spinlock_t lock;
118118
atomic_t count;
119+
int udc_usage;
119120
enum ep0_state state; /* P: lock */
120121
struct usb_gadgetfs_event event [N_EVENT];
121122
unsigned ev_next;
@@ -512,9 +513,9 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
512513
INIT_WORK(&priv->work, ep_user_copy_worker);
513514
schedule_work(&priv->work);
514515
}
515-
spin_unlock(&epdata->dev->lock);
516516

517517
usb_ep_free_request(ep, req);
518+
spin_unlock(&epdata->dev->lock);
518519
put_ep(epdata);
519520
}
520521

@@ -938,9 +939,11 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
938939
struct usb_request *req = dev->req;
939940

940941
if ((retval = setup_req (ep, req, 0)) == 0) {
942+
++dev->udc_usage;
941943
spin_unlock_irq (&dev->lock);
942944
retval = usb_ep_queue (ep, req, GFP_KERNEL);
943945
spin_lock_irq (&dev->lock);
946+
--dev->udc_usage;
944947
}
945948
dev->state = STATE_DEV_CONNECTED;
946949

@@ -1130,6 +1133,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
11301133
retval = setup_req (dev->gadget->ep0, dev->req, len);
11311134
if (retval == 0) {
11321135
dev->state = STATE_DEV_CONNECTED;
1136+
++dev->udc_usage;
11331137
spin_unlock_irq (&dev->lock);
11341138
if (copy_from_user (dev->req->buf, buf, len))
11351139
retval = -EFAULT;
@@ -1141,6 +1145,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
11411145
GFP_KERNEL);
11421146
}
11431147
spin_lock_irq(&dev->lock);
1148+
--dev->udc_usage;
11441149
if (retval < 0) {
11451150
clean_req (dev->gadget->ep0, dev->req);
11461151
} else
@@ -1239,9 +1244,21 @@ static long dev_ioctl (struct file *fd, unsigned code, unsigned long value)
12391244
struct usb_gadget *gadget = dev->gadget;
12401245
long ret = -ENOTTY;
12411246

1242-
if (gadget->ops->ioctl)
1247+
spin_lock_irq(&dev->lock);
1248+
if (dev->state == STATE_DEV_OPENED ||
1249+
dev->state == STATE_DEV_UNBOUND) {
1250+
/* Not bound to a UDC */
1251+
} else if (gadget->ops->ioctl) {
1252+
++dev->udc_usage;
1253+
spin_unlock_irq(&dev->lock);
1254+
12431255
ret = gadget->ops->ioctl (gadget, code, value);
12441256

1257+
spin_lock_irq(&dev->lock);
1258+
--dev->udc_usage;
1259+
}
1260+
spin_unlock_irq(&dev->lock);
1261+
12451262
return ret;
12461263
}
12471264

@@ -1459,10 +1476,12 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
14591476
if (value < 0)
14601477
break;
14611478

1479+
++dev->udc_usage;
14621480
spin_unlock (&dev->lock);
14631481
value = usb_ep_queue (gadget->ep0, dev->req,
14641482
GFP_KERNEL);
14651483
spin_lock (&dev->lock);
1484+
--dev->udc_usage;
14661485
if (value < 0) {
14671486
clean_req (gadget->ep0, dev->req);
14681487
break;
@@ -1486,8 +1505,12 @@ gadgetfs_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
14861505
req->length = value;
14871506
req->zero = value < w_length;
14881507

1508+
++dev->udc_usage;
14891509
spin_unlock (&dev->lock);
14901510
value = usb_ep_queue (gadget->ep0, req, GFP_KERNEL);
1511+
spin_lock(&dev->lock);
1512+
--dev->udc_usage;
1513+
spin_unlock(&dev->lock);
14911514
if (value < 0) {
14921515
DBG (dev, "ep_queue --> %d\n", value);
14931516
req->status = 0;
@@ -1514,21 +1537,24 @@ static void destroy_ep_files (struct dev_data *dev)
15141537
/* break link to FS */
15151538
ep = list_first_entry (&dev->epfiles, struct ep_data, epfiles);
15161539
list_del_init (&ep->epfiles);
1540+
spin_unlock_irq (&dev->lock);
1541+
15171542
dentry = ep->dentry;
15181543
ep->dentry = NULL;
15191544
parent = d_inode(dentry->d_parent);
15201545

15211546
/* break link to controller */
1547+
mutex_lock(&ep->lock);
15221548
if (ep->state == STATE_EP_ENABLED)
15231549
(void) usb_ep_disable (ep->ep);
15241550
ep->state = STATE_EP_UNBOUND;
15251551
usb_ep_free_request (ep->ep, ep->req);
15261552
ep->ep = NULL;
1553+
mutex_unlock(&ep->lock);
1554+
15271555
wake_up (&ep->wait);
15281556
put_ep (ep);
15291557

1530-
spin_unlock_irq (&dev->lock);
1531-
15321558
/* break link to dcache */
15331559
mutex_lock (&parent->i_mutex);
15341560
d_delete (dentry);
@@ -1599,6 +1625,11 @@ gadgetfs_unbind (struct usb_gadget *gadget)
15991625

16001626
spin_lock_irq (&dev->lock);
16011627
dev->state = STATE_DEV_UNBOUND;
1628+
while (dev->udc_usage > 0) {
1629+
spin_unlock_irq(&dev->lock);
1630+
usleep_range(1000, 2000);
1631+
spin_lock_irq(&dev->lock);
1632+
}
16021633
spin_unlock_irq (&dev->lock);
16031634

16041635
destroy_ep_files (dev);

0 commit comments

Comments
 (0)