浏览代码

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 年之前
父节点
当前提交
5b89db02a5
共有 1 个文件被更改,包括 11 次插入7 次删除
  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)
 static void ep0_complete (struct usb_ep *ep, struct usb_request *req)
 {
 {
 	struct dev_data		*dev = ep->driver_data;
 	struct dev_data		*dev = ep->driver_data;
+	unsigned long		flags;
 	int			free = 1;
 	int			free = 1;
 
 
 	/* for control OUT, data must still get to userspace */
 	/* for control OUT, data must still get to userspace */
+	spin_lock_irqsave(&dev->lock, flags);
 	if (!dev->setup_in) {
 	if (!dev->setup_in) {
 		dev->setup_out_error = (req->status != 0);
 		dev->setup_out_error = (req->status != 0);
 		if (!dev->setup_out_error)
 		if (!dev->setup_out_error)
 			free = 0;
 			free = 0;
 		dev->setup_out_ready = 1;
 		dev->setup_out_ready = 1;
 		ep0_readable (dev);
 		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 */
 	/* clean up as appropriate */
 	if (free && req->buf != &dev->rbuf)
 	if (free && req->buf != &dev->rbuf)
 		clean_req (ep, req);
 		clean_req (ep, req);
 	req->complete = epio_complete;
 	req->complete = epio_complete;
+	spin_unlock_irqrestore(&dev->lock, flags);
 }
 }
 
 
 static int setup_req (struct usb_ep *ep, struct usb_request *req, u16 len)
 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);
 			spin_lock_irq (&dev->lock);
 			if (retval)
 			if (retval)
 				goto done;
 				goto done;
+
+			if (dev->state != STATE_DEV_SETUP) {
+				retval = -ECANCELED;
+				goto done;
+			}
+			dev->state = STATE_DEV_CONNECTED;
+
 			if (dev->setup_out_error)
 			if (dev->setup_out_error)
 				retval = -EIO;
 				retval = -EIO;
 			else {
 			else {
@@ -1187,6 +1190,7 @@ ep0_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
 		if (dev->setup_in) {
 		if (dev->setup_in) {
 			retval = setup_req (dev->gadget->ep0, dev->req, len);
 			retval = setup_req (dev->gadget->ep0, dev->req, len);
 			if (retval == 0) {
 			if (retval == 0) {
+				dev->state = STATE_DEV_CONNECTED;
 				spin_unlock_irq (&dev->lock);
 				spin_unlock_irq (&dev->lock);
 				if (copy_from_user (dev->req->buf, buf, len))
 				if (copy_from_user (dev->req->buf, buf, len))
 					retval = -EFAULT;
 					retval = -EFAULT;