aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/misc/legousbtower.c
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2007-05-22 11:46:41 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2007-07-12 19:29:48 -0400
commitd4ead16f50f9ad30bdc7276ec8fee7a24c72f294 (patch)
treee1905abbc393cc4d73180dd7b9e1cf860378b590 /drivers/usb/misc/legousbtower.c
parent55e5fdfa541ec7bf1b1613624ed4dd8cdacaa841 (diff)
USB: prevent char device open/deregister race
This patch (as908) adds central protection in usbcore for the prototypical race between opening and unregistering a char device. The spinlock used to protect the minor-numbers array is replaced with an rwsem, which can remain locked across a call to a driver's open() method. This guarantees that open() and deregister() will be mutually exclusive. The private locks currently used in several individual drivers for this purpose are no longer necessary, and the patch removes them. The following USB drivers are affected: usblcd, idmouse, auerswald, legousbtower, sisusbvga/sisusb, ldusb, adutux, iowarrior, and usb-skeleton. As a side effect of this change, usb_deregister_dev() must not be called while holding a lock that is acquired by open(). Unfortunately a number of drivers do this, but luckily the solution is simple: call usb_deregister_dev() before acquiring the lock. In addition to these changes (and their consequent code simplifications), the patch fixes a use-after-free bug in adutux and a race between open() and release() in iowarrior. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/misc/legousbtower.c')
-rw-r--r--drivers/usb/misc/legousbtower.c24
1 files changed, 6 insertions, 18 deletions
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 1713e19a7899..2ed0daea894c 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -254,9 +254,6 @@ static int tower_probe (struct usb_interface *interface, const struct usb_devic
254static void tower_disconnect (struct usb_interface *interface); 254static void tower_disconnect (struct usb_interface *interface);
255 255
256 256
257/* prevent races between open() and disconnect */
258static DEFINE_MUTEX (disconnect_mutex);
259
260/* file operations needed when we register this driver */ 257/* file operations needed when we register this driver */
261static const struct file_operations tower_fops = { 258static const struct file_operations tower_fops = {
262 .owner = THIS_MODULE, 259 .owner = THIS_MODULE,
@@ -344,28 +341,26 @@ static int tower_open (struct inode *inode, struct file *file)
344 nonseekable_open(inode, file); 341 nonseekable_open(inode, file);
345 subminor = iminor(inode); 342 subminor = iminor(inode);
346 343
347 mutex_lock (&disconnect_mutex);
348
349 interface = usb_find_interface (&tower_driver, subminor); 344 interface = usb_find_interface (&tower_driver, subminor);
350 345
351 if (!interface) { 346 if (!interface) {
352 err ("%s - error, can't find device for minor %d", 347 err ("%s - error, can't find device for minor %d",
353 __FUNCTION__, subminor); 348 __FUNCTION__, subminor);
354 retval = -ENODEV; 349 retval = -ENODEV;
355 goto unlock_disconnect_exit; 350 goto exit;
356 } 351 }
357 352
358 dev = usb_get_intfdata(interface); 353 dev = usb_get_intfdata(interface);
359 354
360 if (!dev) { 355 if (!dev) {
361 retval = -ENODEV; 356 retval = -ENODEV;
362 goto unlock_disconnect_exit; 357 goto exit;
363 } 358 }
364 359
365 /* lock this device */ 360 /* lock this device */
366 if (down_interruptible (&dev->sem)) { 361 if (down_interruptible (&dev->sem)) {
367 retval = -ERESTARTSYS; 362 retval = -ERESTARTSYS;
368 goto unlock_disconnect_exit; 363 goto exit;
369 } 364 }
370 365
371 /* allow opening only once */ 366 /* allow opening only once */
@@ -421,9 +416,7 @@ static int tower_open (struct inode *inode, struct file *file)
421unlock_exit: 416unlock_exit:
422 up (&dev->sem); 417 up (&dev->sem);
423 418
424unlock_disconnect_exit: 419exit:
425 mutex_unlock (&disconnect_mutex);
426
427 dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval); 420 dbg(2, "%s: leave, return value %d ", __FUNCTION__, retval);
428 421
429 return retval; 422 return retval;
@@ -993,19 +986,16 @@ static void tower_disconnect (struct usb_interface *interface)
993 986
994 dbg(2, "%s: enter", __FUNCTION__); 987 dbg(2, "%s: enter", __FUNCTION__);
995 988
996 mutex_lock (&disconnect_mutex);
997
998 dev = usb_get_intfdata (interface); 989 dev = usb_get_intfdata (interface);
999 usb_set_intfdata (interface, NULL); 990 usb_set_intfdata (interface, NULL);
1000 991
1001
1002 down (&dev->sem);
1003
1004 minor = dev->minor; 992 minor = dev->minor;
1005 993
1006 /* give back our minor */ 994 /* give back our minor */
1007 usb_deregister_dev (interface, &tower_class); 995 usb_deregister_dev (interface, &tower_class);
1008 996
997 down (&dev->sem);
998
1009 /* if the device is not opened, then we clean up right now */ 999 /* if the device is not opened, then we clean up right now */
1010 if (!dev->open_count) { 1000 if (!dev->open_count) {
1011 up (&dev->sem); 1001 up (&dev->sem);
@@ -1015,8 +1005,6 @@ static void tower_disconnect (struct usb_interface *interface)
1015 up (&dev->sem); 1005 up (&dev->sem);
1016 } 1006 }
1017 1007
1018 mutex_unlock (&disconnect_mutex);
1019
1020 info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE)); 1008 info("LEGO USB Tower #%d now disconnected", (minor - LEGO_USB_TOWER_MINOR_BASE));
1021 1009
1022 dbg(2, "%s: leave", __FUNCTION__); 1010 dbg(2, "%s: leave", __FUNCTION__);