summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2007-01-16 22:56:26 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-07 15:44:38 -0800
commit5b89db02a5a7c8bad3c6fb7888778082a441b385 (patch)
tree0d9f11ff8cb328fd79dfb77dd8a773cfbb47a366
parent0864c7a9286b02319d3db2103bada1c2269c1e1e (diff)
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>
-rw-r--r--drivers/usb/gadget/inode.c18
1 files changed, 11 insertions, 7 deletions
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index ea8e3160d05..e5ce4f0bb7c 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -933,28 +933,24 @@ static void clean_req (struct usb_ep *ep, struct usb_request *req)
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 *buf, size_t len, loff_t *ptr)
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 __user *buf, size_t len, loff_t *ptr)
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;