usbfs: fix race between open and unregister
authorAlan Stern <stern@rowland.harvard.edu>
Tue, 24 Jun 2008 18:47:19 +0000 (14:47 -0400)
committerGreg Kroah-Hartman <gregkh@suse.de>
Mon, 21 Jul 2008 22:16:42 +0000 (15:16 -0700)
This patch (as1106) fixes a race between opening and unregistering
device files in usbfs.  The current code drops its reference to the
device and then reacquires it, ignoring the possibility that the
device structure might have been removed in the meantime.  It also
doesn't check whether the device is already in the NOTATTACHED state
when the file is opened.

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/devio.c

index 57bedcebf96c07884eeace55603e6784ff85d525..c44e98f6099e9d54bfb846f942d515b17e7cf485 100644 (file)
@@ -562,7 +562,6 @@ static struct usb_device *usbdev_lookup_by_devt(dev_t devt)
        dev = bus_find_device(&usb_bus_type, NULL, (void *) devt, match_devt);
        if (!dev)
                return NULL;
-       put_device(dev);
        return container_of(dev, struct usb_device, dev);
 }
 
@@ -591,16 +590,21 @@ static int usbdev_open(struct inode *inode, struct file *file)
                dev = usbdev_lookup_by_devt(inode->i_rdev);
 #ifdef CONFIG_USB_DEVICEFS
        /* procfs file */
-       if (!dev)
+       if (!dev) {
                dev = inode->i_private;
+               if (dev && dev->usbfs_dentry &&
+                                       dev->usbfs_dentry->d_inode == inode)
+                       usb_get_dev(dev);
+               else
+                       dev = NULL;
+       }
 #endif
-       if (!dev)
+       if (!dev || dev->state == USB_STATE_NOTATTACHED)
                goto out;
        ret = usb_autoresume_device(dev);
        if (ret)
                goto out;
 
-       usb_get_dev(dev);
        ret = 0;
        ps->dev = dev;
        ps->file = file;
@@ -620,8 +624,10 @@ static int usbdev_open(struct inode *inode, struct file *file)
        list_add_tail(&ps->list, &dev->filelist);
        file->private_data = ps;
  out:
-       if (ret)
+       if (ret) {
                kfree(ps);
+               usb_put_dev(dev);
+       }
        mutex_unlock(&usbfs_mutex);
        unlock_kernel();
        return ret;