USB: gadgetfs race fix
This resolves a race in gadgetfs associated with changing device/ep0
when processing control requests. The fix is to change that state
earlier, when the control response is issued, so there's no window
in which userspace could see the wrong state; and enlarge the scope
of the spinlock during the ep0 request completion handler.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index ea8e316..e5ce4f0 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -933,28 +933,24 @@
static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
{
struct dev_data *dev = ep->driver_data;
+ unsigned long flags;
int free = 1;
/* for control OUT, data must still get to userspace */
+ spin_lock_irqsave(&dev->lock, flags);
if (!dev->setup_in) {
dev->setup_out_error = (req->status != 0);
if (!dev->setup_out_error)
free = 0;
dev->setup_out_ready = 1;
ep0_readable (dev);
- } else {
- unsigned long flags;
-
- spin_lock_irqsave(&dev->lock, flags);
- if (dev->state == STATE_DEV_SETUP)
- dev->state = STATE_DEV_CONNECTED;
- spin_unlock_irqrestore(&dev->lock, flags);
}
/* clean up as appropriate */
if (free && req->buf != &dev->rbuf)
clean_req (ep, req);
req->complete = epio_complete;
+ spin_unlock_irqrestore(&dev->lock, flags);
}
static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
@@ -1036,6 +1032,13 @@
spin_lock_irq (&dev->lock);
if (retval)
goto done;
+
+ if (dev->state != STATE_DEV_SETUP) {
+ retval = -ECANCELED;
+ goto done;
+ }
+ dev->state = STATE_DEV_CONNECTED;
+
if (dev->setup_out_error)
retval = -EIO;
else {
@@ -1187,6 +1190,7 @@
if (dev->setup_in) {
retval = setup_req (dev->gadget->ep0, dev->req, len);
if (retval == 0) {
+ dev->state = STATE_DEV_CONNECTED;
spin_unlock_irq (&dev->lock);
if (copy_from_user (dev->req->buf, buf, len))
retval = -EFAULT;