aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/misc/usblcd.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/usblcd.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/usblcd.c')
-rw-r--r--drivers/usb/misc/usblcd.c21
1 files changed, 4 insertions, 17 deletions
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index 12bad8a205a7..6e093c2aac2c 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -51,7 +51,6 @@ struct usb_lcd {
51#define USB_LCD_CONCURRENT_WRITES 5 51#define USB_LCD_CONCURRENT_WRITES 5
52 52
53static struct usb_driver lcd_driver; 53static struct usb_driver lcd_driver;
54static DEFINE_MUTEX(usb_lcd_open_mutex);
55 54
56 55
57static void lcd_delete(struct kref *kref) 56static void lcd_delete(struct kref *kref)
@@ -69,24 +68,19 @@ static int lcd_open(struct inode *inode, struct file *file)
69 struct usb_lcd *dev; 68 struct usb_lcd *dev;
70 struct usb_interface *interface; 69 struct usb_interface *interface;
71 int subminor; 70 int subminor;
72 int retval = 0;
73 71
74 subminor = iminor(inode); 72 subminor = iminor(inode);
75 73
76 mutex_lock(&usb_lcd_open_mutex);
77 interface = usb_find_interface(&lcd_driver, subminor); 74 interface = usb_find_interface(&lcd_driver, subminor);
78 if (!interface) { 75 if (!interface) {
79 err ("USBLCD: %s - error, can't find device for minor %d", 76 err ("USBLCD: %s - error, can't find device for minor %d",
80 __FUNCTION__, subminor); 77 __FUNCTION__, subminor);
81 retval = -ENODEV; 78 return -ENODEV;
82 goto exit;
83 } 79 }
84 80
85 dev = usb_get_intfdata(interface); 81 dev = usb_get_intfdata(interface);
86 if (!dev) { 82 if (!dev)
87 retval = -ENODEV; 83 return -ENODEV;
88 goto exit;
89 }
90 84
91 /* increment our usage count for the device */ 85 /* increment our usage count for the device */
92 kref_get(&dev->kref); 86 kref_get(&dev->kref);
@@ -94,9 +88,7 @@ static int lcd_open(struct inode *inode, struct file *file)
94 /* save our object in the file's private structure */ 88 /* save our object in the file's private structure */
95 file->private_data = dev; 89 file->private_data = dev;
96 90
97exit: 91 return 0;
98 mutex_unlock(&usb_lcd_open_mutex);
99 return retval;
100} 92}
101 93
102static int lcd_release(struct inode *inode, struct file *file) 94static int lcd_release(struct inode *inode, struct file *file)
@@ -363,17 +355,12 @@ static void lcd_disconnect(struct usb_interface *interface)
363 struct usb_lcd *dev; 355 struct usb_lcd *dev;
364 int minor = interface->minor; 356 int minor = interface->minor;
365 357
366 /* prevent skel_open() from racing skel_disconnect() */
367 mutex_lock(&usb_lcd_open_mutex);
368
369 dev = usb_get_intfdata(interface); 358 dev = usb_get_intfdata(interface);
370 usb_set_intfdata(interface, NULL); 359 usb_set_intfdata(interface, NULL);
371 360
372 /* give back our minor */ 361 /* give back our minor */
373 usb_deregister_dev(interface, &lcd_class); 362 usb_deregister_dev(interface, &lcd_class);
374 363
375 mutex_unlock(&usb_lcd_open_mutex);
376
377 /* decrement our usage count */ 364 /* decrement our usage count */
378 kref_put(&dev->kref, lcd_delete); 365 kref_put(&dev->kref, lcd_delete);
379 366