aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2006-07-01 22:05:01 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-09-27 14:58:49 -0400
commit4a2a8a2cce86b9d001378cc25acb5c61e6ca7d63 (patch)
tree01f2d9eb9a23bd14575ace882b3b3cc8b60f62e6 /drivers/usb/core
parentda308e8da700637d6271dddda08128094691b980 (diff)
usbfs: private mutex for open, release, and remove
The usbfs code doesn't provide sufficient mutual exclusion among open, release, and remove. Release vs. remove is okay because they both acquire the device lock, but open is not exclusive with either one. All three routines modify the udev->filelist linked list, so they must not run concurrently. Apparently someone gave this a minimum amount of thought in the past by explicitly acquiring the BKL at the start of the usbdev_open routine. Oddly enough, there's a comment pointing out that locking is unnecessary because chrdev_open already has acquired the BKL. But this ignores the point that the files in /proc/bus/usb/* are not char device files; they are regular files and so they don't get any special locking. Furthermore it's necessary to acquire the same lock in the release and remove routines, which the code does not do. Yet another problem arises because the same file_operations structure is accessible through both the /proc/bus/usb/* and /dev/usb/usbdev* file nodes. Even when one of them has been removed, it's still possible for userspace to open the other. So simple locking around the individual remove routines is insufficient; we need to lock the entire usb_notify_remove_device notifier chain. Rather than rely on the BKL, this patch (as723) introduces a new private mutex for the purpose. Holding the BKL while invoking a notifier chain doesn't seem like a good idea. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r--drivers/usb/core/devio.c26
-rw-r--r--drivers/usb/core/notify.c3
-rw-r--r--drivers/usb/core/usb.h1
3 files changed, 19 insertions, 11 deletions
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 32e03000420c..d8b0476237f3 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -59,6 +59,9 @@
59#define USB_DEVICE_MAX USB_MAXBUS * 128 59#define USB_DEVICE_MAX USB_MAXBUS * 128
60static struct class *usb_device_class; 60static struct class *usb_device_class;
61 61
62/* Mutual exclusion for removal, open, and release */
63DEFINE_MUTEX(usbfs_mutex);
64
62struct async { 65struct async {
63 struct list_head asynclist; 66 struct list_head asynclist;
64 struct dev_state *ps; 67 struct dev_state *ps;
@@ -541,15 +544,13 @@ static int usbdev_open(struct inode *inode, struct file *file)
541 struct dev_state *ps; 544 struct dev_state *ps;
542 int ret; 545 int ret;
543 546
544 /* 547 /* Protect against simultaneous removal or release */
545 * no locking necessary here, as chrdev_open has the kernel lock 548 mutex_lock(&usbfs_mutex);
546 * (still acquire the kernel lock for safety) 549
547 */
548 ret = -ENOMEM; 550 ret = -ENOMEM;
549 if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL))) 551 if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
550 goto out_nolock; 552 goto out;
551 553
552 lock_kernel();
553 ret = -ENOENT; 554 ret = -ENOENT;
554 /* check if we are called from a real node or usbfs */ 555 /* check if we are called from a real node or usbfs */
555 if (imajor(inode) == USB_DEVICE_MAJOR) 556 if (imajor(inode) == USB_DEVICE_MAJOR)
@@ -579,9 +580,8 @@ static int usbdev_open(struct inode *inode, struct file *file)
579 list_add_tail(&ps->list, &dev->filelist); 580 list_add_tail(&ps->list, &dev->filelist);
580 file->private_data = ps; 581 file->private_data = ps;
581 out: 582 out:
582 unlock_kernel(); 583 mutex_unlock(&usbfs_mutex);
583 out_nolock: 584 return ret;
584 return ret;
585} 585}
586 586
587static int usbdev_release(struct inode *inode, struct file *file) 587static int usbdev_release(struct inode *inode, struct file *file)
@@ -591,7 +591,12 @@ static int usbdev_release(struct inode *inode, struct file *file)
591 unsigned int ifnum; 591 unsigned int ifnum;
592 592
593 usb_lock_device(dev); 593 usb_lock_device(dev);
594
595 /* Protect against simultaneous open */
596 mutex_lock(&usbfs_mutex);
594 list_del_init(&ps->list); 597 list_del_init(&ps->list);
598 mutex_unlock(&usbfs_mutex);
599
595 for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); 600 for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
596 ifnum++) { 601 ifnum++) {
597 if (test_bit(ifnum, &ps->ifclaimed)) 602 if (test_bit(ifnum, &ps->ifclaimed))
@@ -600,9 +605,8 @@ static int usbdev_release(struct inode *inode, struct file *file)
600 destroy_all_async(ps); 605 destroy_all_async(ps);
601 usb_unlock_device(dev); 606 usb_unlock_device(dev);
602 usb_put_dev(dev); 607 usb_put_dev(dev);
603 ps->dev = NULL;
604 kfree(ps); 608 kfree(ps);
605 return 0; 609 return 0;
606} 610}
607 611
608static int proc_control(struct dev_state *ps, void __user *arg) 612static int proc_control(struct dev_state *ps, void __user *arg)
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index b042676af0a5..6b36897ca151 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -50,8 +50,11 @@ void usb_notify_add_device(struct usb_device *udev)
50 50
51void usb_notify_remove_device(struct usb_device *udev) 51void usb_notify_remove_device(struct usb_device *udev)
52{ 52{
53 /* Protect against simultaneous usbfs open */
54 mutex_lock(&usbfs_mutex);
53 blocking_notifier_call_chain(&usb_notifier_list, 55 blocking_notifier_call_chain(&usb_notifier_list,
54 USB_DEVICE_REMOVE, udev); 56 USB_DEVICE_REMOVE, udev);
57 mutex_unlock(&usbfs_mutex);
55} 58}
56 59
57void usb_notify_add_bus(struct usb_bus *ubus) 60void usb_notify_add_bus(struct usb_bus *ubus)
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 49f69236b420..1217fbbe5829 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -59,6 +59,7 @@ static inline int is_active(struct usb_interface *f)
59extern const char *usbcore_name; 59extern const char *usbcore_name;
60 60
61/* usbfs stuff */ 61/* usbfs stuff */
62extern struct mutex usbfs_mutex;
62extern struct usb_driver usbfs_driver; 63extern struct usb_driver usbfs_driver;
63extern struct file_operations usbfs_devices_fops; 64extern struct file_operations usbfs_devices_fops;
64extern struct file_operations usbfs_device_file_operations; 65extern struct file_operations usbfs_device_file_operations;