aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/misc/sisusbvga
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/sisusbvga
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/sisusbvga')
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c38
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_con.c25
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb_init.h2
3 files changed, 7 insertions, 58 deletions
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index 6f8b134a79cb..9f37ba44c132 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -72,8 +72,6 @@ MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES
72 72
73static struct usb_driver sisusb_driver; 73static struct usb_driver sisusb_driver;
74 74
75DEFINE_MUTEX(disconnect_mutex);
76
77static void 75static void
78sisusb_free_buffers(struct sisusb_usb_data *sisusb) 76sisusb_free_buffers(struct sisusb_usb_data *sisusb)
79{ 77{
@@ -2511,31 +2509,24 @@ sisusb_open(struct inode *inode, struct file *file)
2511 struct usb_interface *interface; 2509 struct usb_interface *interface;
2512 int subminor = iminor(inode); 2510 int subminor = iminor(inode);
2513 2511
2514 mutex_lock(&disconnect_mutex);
2515
2516 if (!(interface = usb_find_interface(&sisusb_driver, subminor))) { 2512 if (!(interface = usb_find_interface(&sisusb_driver, subminor))) {
2517 printk(KERN_ERR "sisusb[%d]: Failed to find interface\n", 2513 printk(KERN_ERR "sisusb[%d]: Failed to find interface\n",
2518 subminor); 2514 subminor);
2519 mutex_unlock(&disconnect_mutex);
2520 return -ENODEV; 2515 return -ENODEV;
2521 } 2516 }
2522 2517
2523 if (!(sisusb = usb_get_intfdata(interface))) { 2518 if (!(sisusb = usb_get_intfdata(interface)))
2524 mutex_unlock(&disconnect_mutex);
2525 return -ENODEV; 2519 return -ENODEV;
2526 }
2527 2520
2528 mutex_lock(&sisusb->lock); 2521 mutex_lock(&sisusb->lock);
2529 2522
2530 if (!sisusb->present || !sisusb->ready) { 2523 if (!sisusb->present || !sisusb->ready) {
2531 mutex_unlock(&sisusb->lock); 2524 mutex_unlock(&sisusb->lock);
2532 mutex_unlock(&disconnect_mutex);
2533 return -ENODEV; 2525 return -ENODEV;
2534 } 2526 }
2535 2527
2536 if (sisusb->isopen) { 2528 if (sisusb->isopen) {
2537 mutex_unlock(&sisusb->lock); 2529 mutex_unlock(&sisusb->lock);
2538 mutex_unlock(&disconnect_mutex);
2539 return -EBUSY; 2530 return -EBUSY;
2540 } 2531 }
2541 2532
@@ -2543,7 +2534,6 @@ sisusb_open(struct inode *inode, struct file *file)
2543 if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) { 2534 if (sisusb->sisusb_dev->speed == USB_SPEED_HIGH) {
2544 if (sisusb_init_gfxdevice(sisusb, 0)) { 2535 if (sisusb_init_gfxdevice(sisusb, 0)) {
2545 mutex_unlock(&sisusb->lock); 2536 mutex_unlock(&sisusb->lock);
2546 mutex_unlock(&disconnect_mutex);
2547 printk(KERN_ERR 2537 printk(KERN_ERR
2548 "sisusbvga[%d]: Failed to initialize " 2538 "sisusbvga[%d]: Failed to initialize "
2549 "device\n", 2539 "device\n",
@@ -2552,7 +2542,6 @@ sisusb_open(struct inode *inode, struct file *file)
2552 } 2542 }
2553 } else { 2543 } else {
2554 mutex_unlock(&sisusb->lock); 2544 mutex_unlock(&sisusb->lock);
2555 mutex_unlock(&disconnect_mutex);
2556 printk(KERN_ERR 2545 printk(KERN_ERR
2557 "sisusbvga[%d]: Device not attached to " 2546 "sisusbvga[%d]: Device not attached to "
2558 "USB 2.0 hub\n", 2547 "USB 2.0 hub\n",
@@ -2570,8 +2559,6 @@ sisusb_open(struct inode *inode, struct file *file)
2570 2559
2571 mutex_unlock(&sisusb->lock); 2560 mutex_unlock(&sisusb->lock);
2572 2561
2573 mutex_unlock(&disconnect_mutex);
2574
2575 return 0; 2562 return 0;
2576} 2563}
2577 2564
@@ -2601,12 +2588,8 @@ sisusb_release(struct inode *inode, struct file *file)
2601 struct sisusb_usb_data *sisusb; 2588 struct sisusb_usb_data *sisusb;
2602 int myminor; 2589 int myminor;
2603 2590
2604 mutex_lock(&disconnect_mutex); 2591 if (!(sisusb = (struct sisusb_usb_data *)file->private_data))
2605
2606 if (!(sisusb = (struct sisusb_usb_data *)file->private_data)) {
2607 mutex_unlock(&disconnect_mutex);
2608 return -ENODEV; 2592 return -ENODEV;
2609 }
2610 2593
2611 mutex_lock(&sisusb->lock); 2594 mutex_lock(&sisusb->lock);
2612 2595
@@ -2626,8 +2609,6 @@ sisusb_release(struct inode *inode, struct file *file)
2626 /* decrement the usage count on our device */ 2609 /* decrement the usage count on our device */
2627 kref_put(&sisusb->kref, sisusb_delete); 2610 kref_put(&sisusb->kref, sisusb_delete);
2628 2611
2629 mutex_unlock(&disconnect_mutex);
2630
2631 return 0; 2612 return 0;
2632} 2613}
2633 2614
@@ -3383,12 +3364,9 @@ static void sisusb_disconnect(struct usb_interface *intf)
3383 sisusb_console_exit(sisusb); 3364 sisusb_console_exit(sisusb);
3384#endif 3365#endif
3385 3366
3386 /* The above code doesn't need the disconnect 3367 minor = sisusb->minor;
3387 * semaphore to be down; its meaning is to 3368
3388 * protect all other routines from the disconnect 3369 usb_deregister_dev(intf, &usb_sisusb_class);
3389 * case, not the other way round.
3390 */
3391 mutex_lock(&disconnect_mutex);
3392 3370
3393 mutex_lock(&sisusb->lock); 3371 mutex_lock(&sisusb->lock);
3394 3372
@@ -3396,12 +3374,8 @@ static void sisusb_disconnect(struct usb_interface *intf)
3396 if (!sisusb_wait_all_out_complete(sisusb)) 3374 if (!sisusb_wait_all_out_complete(sisusb))
3397 sisusb_kill_all_busy(sisusb); 3375 sisusb_kill_all_busy(sisusb);
3398 3376
3399 minor = sisusb->minor;
3400
3401 usb_set_intfdata(intf, NULL); 3377 usb_set_intfdata(intf, NULL);
3402 3378
3403 usb_deregister_dev(intf, &usb_sisusb_class);
3404
3405#ifdef SISUSB_OLD_CONFIG_COMPAT 3379#ifdef SISUSB_OLD_CONFIG_COMPAT
3406 if (sisusb->ioctl32registered) { 3380 if (sisusb->ioctl32registered) {
3407 int ret; 3381 int ret;
@@ -3426,8 +3400,6 @@ static void sisusb_disconnect(struct usb_interface *intf)
3426 /* decrement our usage count */ 3400 /* decrement our usage count */
3427 kref_put(&sisusb->kref, sisusb_delete); 3401 kref_put(&sisusb->kref, sisusb_delete);
3428 3402
3429 mutex_unlock(&disconnect_mutex);
3430
3431 printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor); 3403 printk(KERN_INFO "sisusbvga[%d]: Disconnected\n", minor);
3432} 3404}
3433 3405
diff --git a/drivers/usb/misc/sisusbvga/sisusb_con.c b/drivers/usb/misc/sisusbvga/sisusb_con.c
index 5947afb0017e..8d0edc867f33 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_con.c
+++ b/drivers/usb/misc/sisusbvga/sisusb_con.c
@@ -214,18 +214,13 @@ sisusbcon_init(struct vc_data *c, int init)
214 * are set up/restored. 214 * are set up/restored.
215 */ 215 */
216 216
217 mutex_lock(&disconnect_mutex); 217 if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
218
219 if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
220 mutex_unlock(&disconnect_mutex);
221 return; 218 return;
222 }
223 219
224 mutex_lock(&sisusb->lock); 220 mutex_lock(&sisusb->lock);
225 221
226 if (!sisusb_sisusb_valid(sisusb)) { 222 if (!sisusb_sisusb_valid(sisusb)) {
227 mutex_unlock(&sisusb->lock); 223 mutex_unlock(&sisusb->lock);
228 mutex_unlock(&disconnect_mutex);
229 return; 224 return;
230 } 225 }
231 226
@@ -264,8 +259,6 @@ sisusbcon_init(struct vc_data *c, int init)
264 259
265 mutex_unlock(&sisusb->lock); 260 mutex_unlock(&sisusb->lock);
266 261
267 mutex_unlock(&disconnect_mutex);
268
269 if (init) { 262 if (init) {
270 c->vc_cols = cols; 263 c->vc_cols = cols;
271 c->vc_rows = rows; 264 c->vc_rows = rows;
@@ -284,12 +277,8 @@ sisusbcon_deinit(struct vc_data *c)
284 * and others, ie not under our control. 277 * and others, ie not under our control.
285 */ 278 */
286 279
287 mutex_lock(&disconnect_mutex); 280 if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
288
289 if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
290 mutex_unlock(&disconnect_mutex);
291 return; 281 return;
292 }
293 282
294 mutex_lock(&sisusb->lock); 283 mutex_lock(&sisusb->lock);
295 284
@@ -314,8 +303,6 @@ sisusbcon_deinit(struct vc_data *c)
314 303
315 /* decrement the usage count on our sisusb */ 304 /* decrement the usage count on our sisusb */
316 kref_put(&sisusb->kref, sisusb_delete); 305 kref_put(&sisusb->kref, sisusb_delete);
317
318 mutex_unlock(&disconnect_mutex);
319} 306}
320 307
321/* interface routine */ 308/* interface routine */
@@ -1490,14 +1477,11 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
1490{ 1477{
1491 int i, ret, minor = sisusb->minor; 1478 int i, ret, minor = sisusb->minor;
1492 1479
1493 mutex_lock(&disconnect_mutex);
1494
1495 mutex_lock(&sisusb->lock); 1480 mutex_lock(&sisusb->lock);
1496 1481
1497 /* Erm.. that should not happen */ 1482 /* Erm.. that should not happen */
1498 if (sisusb->haveconsole || !sisusb->SiS_Pr) { 1483 if (sisusb->haveconsole || !sisusb->SiS_Pr) {
1499 mutex_unlock(&sisusb->lock); 1484 mutex_unlock(&sisusb->lock);
1500 mutex_unlock(&disconnect_mutex);
1501 return 1; 1485 return 1;
1502 } 1486 }
1503 1487
@@ -1508,14 +1492,12 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
1508 first > MAX_NR_CONSOLES || 1492 first > MAX_NR_CONSOLES ||
1509 last > MAX_NR_CONSOLES) { 1493 last > MAX_NR_CONSOLES) {
1510 mutex_unlock(&sisusb->lock); 1494 mutex_unlock(&sisusb->lock);
1511 mutex_unlock(&disconnect_mutex);
1512 return 1; 1495 return 1;
1513 } 1496 }
1514 1497
1515 /* If gfxcore not initialized or no consoles given, quit graciously */ 1498 /* If gfxcore not initialized or no consoles given, quit graciously */
1516 if (!sisusb->gfxinit || first < 1 || last < 1) { 1499 if (!sisusb->gfxinit || first < 1 || last < 1) {
1517 mutex_unlock(&sisusb->lock); 1500 mutex_unlock(&sisusb->lock);
1518 mutex_unlock(&disconnect_mutex);
1519 return 0; 1501 return 0;
1520 } 1502 }
1521 1503
@@ -1526,7 +1508,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
1526 /* Set up text mode (and upload default font) */ 1508 /* Set up text mode (and upload default font) */
1527 if (sisusb_reset_text_mode(sisusb, 1)) { 1509 if (sisusb_reset_text_mode(sisusb, 1)) {
1528 mutex_unlock(&sisusb->lock); 1510 mutex_unlock(&sisusb->lock);
1529 mutex_unlock(&disconnect_mutex);
1530 printk(KERN_ERR 1511 printk(KERN_ERR
1531 "sisusbvga[%d]: Failed to set up text mode\n", 1512 "sisusbvga[%d]: Failed to set up text mode\n",
1532 minor); 1513 minor);
@@ -1550,7 +1531,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
1550 /* Allocate screen buffer */ 1531 /* Allocate screen buffer */
1551 if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) { 1532 if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) {
1552 mutex_unlock(&sisusb->lock); 1533 mutex_unlock(&sisusb->lock);
1553 mutex_unlock(&disconnect_mutex);
1554 printk(KERN_ERR 1534 printk(KERN_ERR
1555 "sisusbvga[%d]: Failed to allocate screen buffer\n", 1535 "sisusbvga[%d]: Failed to allocate screen buffer\n",
1556 minor); 1536 minor);
@@ -1558,7 +1538,6 @@ sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
1558 } 1538 }
1559 1539
1560 mutex_unlock(&sisusb->lock); 1540 mutex_unlock(&sisusb->lock);
1561 mutex_unlock(&disconnect_mutex);
1562 1541
1563 /* Now grab the desired console(s) */ 1542 /* Now grab the desired console(s) */
1564 ret = take_over_console(&sisusb_con, first - 1, last - 1, 0); 1543 ret = take_over_console(&sisusb_con, first - 1, last - 1, 0);
diff --git a/drivers/usb/misc/sisusbvga/sisusb_init.h b/drivers/usb/misc/sisusbvga/sisusb_init.h
index f05f83268af4..864bc0e96591 100644
--- a/drivers/usb/misc/sisusbvga/sisusb_init.h
+++ b/drivers/usb/misc/sisusbvga/sisusb_init.h
@@ -808,8 +808,6 @@ static const struct SiS_VCLKData SiSUSB_VCLKData[] =
808 { 0x2b,0xc2, 35} /* 0x71 768@576@60 */ 808 { 0x2b,0xc2, 35} /* 0x71 768@576@60 */
809}; 809};
810 810
811extern struct mutex disconnect_mutex;
812
813int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo); 811int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
814int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo); 812int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
815 813