Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
4a2a8a2c da308e8d

+19 -11
+15 -11
drivers/usb/core/devio.c
··· 59 59 #define USB_DEVICE_MAX USB_MAXBUS * 128 60 60 static struct class *usb_device_class; 61 61 62 + /* Mutual exclusion for removal, open, and release */ 63 + DEFINE_MUTEX(usbfs_mutex); 64 + 62 65 struct async { 63 66 struct list_head asynclist; 64 67 struct dev_state *ps; ··· 544 541 struct dev_state *ps; 545 542 int ret; 546 543 547 - /* 548 - * no locking necessary here, as chrdev_open has the kernel lock 549 - * (still acquire the kernel lock for safety) 550 - */ 544 + /* Protect against simultaneous removal or release */ 545 + mutex_lock(&usbfs_mutex); 546 + 551 547 ret = -ENOMEM; 552 548 if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL))) 553 - goto out_nolock; 549 + goto out; 554 550 555 - lock_kernel(); 556 551 ret = -ENOENT; 557 552 /* check if we are called from a real node or usbfs */ 558 553 if (imajor(inode) == USB_DEVICE_MAJOR) ··· 580 579 list_add_tail(&ps->list, &dev->filelist); 581 580 file->private_data = ps; 582 581 out: 583 - unlock_kernel(); 584 - out_nolock: 585 - return ret; 582 + mutex_unlock(&usbfs_mutex); 583 + return ret; 586 584 } 587 585 588 586 static int usbdev_release(struct inode *inode, struct file *file) ··· 591 591 unsigned int ifnum; 592 592 593 593 usb_lock_device(dev); 594 + 595 + /* Protect against simultaneous open */ 596 + mutex_lock(&usbfs_mutex); 594 597 list_del_init(&ps->list); 598 + mutex_unlock(&usbfs_mutex); 599 + 595 600 for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed); 596 601 ifnum++) { 597 602 if (test_bit(ifnum, &ps->ifclaimed)) ··· 605 600 destroy_all_async(ps); 606 601 usb_unlock_device(dev); 607 602 usb_put_dev(dev); 608 - ps->dev = NULL; 609 603 kfree(ps); 610 - return 0; 604 + return 0; 611 605 } 612 606 613 607 static int proc_control(struct dev_state *ps, void __user *arg)
+3
drivers/usb/core/notify.c
··· 50 50 51 51 void usb_notify_remove_device(struct usb_device *udev) 52 52 { 53 + /* Protect against simultaneous usbfs open */ 54 + mutex_lock(&usbfs_mutex); 53 55 blocking_notifier_call_chain(&usb_notifier_list, 54 56 USB_DEVICE_REMOVE, udev); 57 + mutex_unlock(&usbfs_mutex); 55 58 } 56 59 57 60 void usb_notify_add_bus(struct usb_bus *ubus)
+1
drivers/usb/core/usb.h
··· 59 59 extern const char *usbcore_name; 60 60 61 61 /* usbfs stuff */ 62 + extern struct mutex usbfs_mutex; 62 63 extern struct usb_driver usbfs_driver; 63 64 extern struct file_operations usbfs_devices_fops; 64 65 extern struct file_operations usbfs_device_file_operations;