Browse Source

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>
David Brownell 18 năm trước cách đây
mục cha
commit
5b89db02a5
1 tập tin đã thay đổi với 11 bổ sung7 xóa
  1. 11 7
      drivers/usb/gadget/inode.c

+ 11 - 7
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;