From david-b@pacbell.net Tue Jan 16 22:56:45 2007 From: David Brownell Date: Tue, 16 Jan 2007 22:56:26 -0800 Subject: USB: gadgetfs race fix To: Greg KH , linux-usb-devel@lists.sourceforge.net Message-ID: <200701162256.26666.david-b@pacbell.net> Content-Disposition: inline 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 Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/inode.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) --- gregkh-2.6.orig/drivers/usb/gadget/inode.c +++ gregkh-2.6/drivers/usb/gadget/inode.c @@ -933,28 +933,24 @@ static void clean_req (struct usb_ep *ep 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 @@ ep0_read (struct file *fd, char __user * 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 @@ ep0_write (struct file *fd, const char _ 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;