aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/gadget/inode.c
diff options
context:
space:
mode:
authorDavid Brownell <david-b@pacbell.net>2007-01-17 01:56:26 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2007-02-07 18:44:38 -0500
commit5b89db02a5a7c8bad3c6fb7888778082a441b385 (patch)
tree0d9f11ff8cb328fd79dfb77dd8a773cfbb47a366 /drivers/usb/gadget/inode.c
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>
Diffstat (limited to 'drivers/usb/gadget/inode.c')
-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 ea8e3160d05e..e5ce4f0bb7cc 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)
933static void ep0_complete (struct usb_ep *ep, struct usb_request *req) 933static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
934{ 934{
935 struct dev_data *dev = ep->driver_data; 935 struct dev_data *dev = ep->driver_data;
936 unsigned long flags;
936 int free = 1; 937 int free = 1;
937 938
938 /* for control OUT, data must still get to userspace */ 939 /* for control OUT, data must still get to userspace */
940 spin_lock_irqsave(&dev->lock, flags);
939 if (!dev->setup_in) { 941 if (!dev->setup_in) {
940 dev->setup_out_error = (req->status != 0); 942 dev->setup_out_error = (req->status != 0);
941 if (!dev->setup_out_error) 943 if (!dev->setup_out_error)
942 free = 0; 944 free = 0;
943 dev->setup_out_ready = 1; 945 dev->setup_out_ready = 1;
944 ep0_readable (dev); 946 ep0_readable (dev);
945 } else {
946 unsigned long flags;
947
948 spin_lock_irqsave(&dev->lock, flags);
949 if (dev->state == STATE_DEV_SETUP)
950 dev->state = STATE_DEV_CONNECTED;
951 spin_unlock_irqrestore(&dev->lock, flags);
952 } 947 }
953 948
954 /* clean up as appropriate */ 949 /* clean up as appropriate */
955 if (free && req->buf != &dev->rbuf) 950 if (free && req->buf != &dev->rbuf)
956 clean_req (ep, req); 951 clean_req (ep, req);
957 req->complete = epio_complete; 952 req->complete = epio_complete;
953 spin_unlock_irqrestore(&dev->lock, flags);
958} 954}
959 955
960static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len) 956static 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)
1036 spin_lock_irq (&dev->lock); 1032 spin_lock_irq (&dev->lock);
1037 if (retval) 1033 if (retval)
1038 goto done; 1034 goto done;
1035
1036 if (dev->state != STATE_DEV_SETUP) {
1037 retval = -ECANCELED;
1038 goto done;
1039 }
1040 dev->state = STATE_DEV_CONNECTED;
1041
1039 if (dev->setup_out_error) 1042 if (dev->setup_out_error)
1040 retval = -EIO; 1043 retval = -EIO;
1041 else { 1044 else {
@@ -1187,6 +1190,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
1187 if (dev->setup_in) { 1190 if (dev->setup_in) {
1188 retval = setup_req (dev->gadget->ep0, dev->req, len); 1191 retval = setup_req (dev->gadget->ep0, dev->req, len);
1189 if (retval == 0) { 1192 if (retval == 0) {
1193 dev->state = STATE_DEV_CONNECTED;
1190 spin_unlock_irq (&dev->lock); 1194 spin_unlock_irq (&dev->lock);
1191 if (copy_from_user (dev->req->buf, buf, len)) 1195 if (copy_from_user (dev->req->buf, buf, len))
1192 retval = -EFAULT; 1196 retval = -EFAULT;