aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/misc/adutux.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/adutux.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/adutux.c')
-rw-r--r--drivers/usb/misc/adutux.c31
1 files changed, 11 insertions, 20 deletions
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
index 77145f9db043..d72c42e5f22d 100644
--- a/drivers/usb/misc/adutux.c
+++ b/drivers/usb/misc/adutux.c
@@ -108,8 +108,6 @@ struct adu_device {
108 struct urb* interrupt_out_urb; 108 struct urb* interrupt_out_urb;
109}; 109};
110 110
111/* prevent races between open() and disconnect */
112static DEFINE_MUTEX(disconnect_mutex);
113static struct usb_driver adu_driver; 111static struct usb_driver adu_driver;
114 112
115static void adu_debug_data(int level, const char *function, int size, 113static void adu_debug_data(int level, const char *function, int size,
@@ -256,8 +254,6 @@ static int adu_open(struct inode *inode, struct file *file)
256 254
257 subminor = iminor(inode); 255 subminor = iminor(inode);
258 256
259 mutex_lock(&disconnect_mutex);
260
261 interface = usb_find_interface(&adu_driver, subminor); 257 interface = usb_find_interface(&adu_driver, subminor);
262 if (!interface) { 258 if (!interface) {
263 err("%s - error, can't find device for minor %d", 259 err("%s - error, can't find device for minor %d",
@@ -306,7 +302,6 @@ static int adu_open(struct inode *inode, struct file *file)
306 up(&dev->sem); 302 up(&dev->sem);
307 303
308exit_no_device: 304exit_no_device:
309 mutex_unlock(&disconnect_mutex);
310 dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval); 305 dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval);
311 306
312 return retval; 307 return retval;
@@ -318,12 +313,6 @@ static int adu_release_internal(struct adu_device *dev)
318 313
319 dbg(2," %s : enter", __FUNCTION__); 314 dbg(2," %s : enter", __FUNCTION__);
320 315
321 if (dev->udev == NULL) {
322 /* the device was unplugged before the file was released */
323 adu_delete(dev);
324 goto exit;
325 }
326
327 /* decrement our usage count for the device */ 316 /* decrement our usage count for the device */
328 --dev->open_count; 317 --dev->open_count;
329 dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); 318 dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
@@ -332,7 +321,6 @@ static int adu_release_internal(struct adu_device *dev)
332 dev->open_count = 0; 321 dev->open_count = 0;
333 } 322 }
334 323
335exit:
336 dbg(2," %s : leave", __FUNCTION__); 324 dbg(2," %s : leave", __FUNCTION__);
337 return retval; 325 return retval;
338} 326}
@@ -367,8 +355,15 @@ static int adu_release(struct inode *inode, struct file *file)
367 goto exit; 355 goto exit;
368 } 356 }
369 357
370 /* do the work */ 358 if (dev->udev == NULL) {
371 retval = adu_release_internal(dev); 359 /* the device was unplugged before the file was released */
360 up(&dev->sem);
361 adu_delete(dev);
362 dev = NULL;
363 } else {
364 /* do the work */
365 retval = adu_release_internal(dev);
366 }
372 367
373exit: 368exit:
374 if (dev) 369 if (dev)
@@ -831,19 +826,17 @@ static void adu_disconnect(struct usb_interface *interface)
831 826
832 dbg(2," %s : enter", __FUNCTION__); 827 dbg(2," %s : enter", __FUNCTION__);
833 828
834 mutex_lock(&disconnect_mutex); /* not interruptible */
835
836 dev = usb_get_intfdata(interface); 829 dev = usb_get_intfdata(interface);
837 usb_set_intfdata(interface, NULL); 830 usb_set_intfdata(interface, NULL);
838 831
839 down(&dev->sem); /* not interruptible */
840
841 minor = dev->minor; 832 minor = dev->minor;
842 833
843 /* give back our minor */ 834 /* give back our minor */
844 usb_deregister_dev(interface, &adu_class); 835 usb_deregister_dev(interface, &adu_class);
845 dev->minor = 0; 836 dev->minor = 0;
846 837
838 down(&dev->sem); /* not interruptible */
839
847 /* if the device is not opened, then we clean up right now */ 840 /* if the device is not opened, then we clean up right now */
848 dbg(2," %s : open count %d", __FUNCTION__, dev->open_count); 841 dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
849 if (!dev->open_count) { 842 if (!dev->open_count) {
@@ -854,8 +847,6 @@ static void adu_disconnect(struct usb_interface *interface)
854 up(&dev->sem); 847 up(&dev->sem);
855 } 848 }
856 849
857 mutex_unlock(&disconnect_mutex);
858
859 dev_info(&interface->dev, "ADU device adutux%d now disconnected", 850 dev_info(&interface->dev, "ADU device adutux%d now disconnected",
860 (minor - ADU_MINOR_BASE)); 851 (minor - ADU_MINOR_BASE));
861 852