ChangeSet 1.1094.6.6, 2003/03/14 10:37:16-08:00, henning@meier-geinitz.de [PATCH] USB: Fix crash in read/write/ioctl in scanner driver Used kobject reference counting to free the scn struct when the device is closed and disconnected. Avoids crashes when writing to a disconnected device. (Thanks to Greg KH). I've also changed irq_scanner to avoid submitting new URBs when the old one returned with an error. Without this change irq_scanner gets called ever and ever again after a disconnect while open. drivers/usb/image/scanner.c | 70 ++++++++++++++++++++++++++++++-------------- drivers/usb/image/scanner.h | 4 +- 2 files changed, 51 insertions(+), 23 deletions(-) diff -Nru a/drivers/usb/image/scanner.c b/drivers/usb/image/scanner.c --- a/drivers/usb/image/scanner.c Mon Mar 17 11:46:59 2003 +++ b/drivers/usb/image/scanner.c Mon Mar 17 11:46:59 2003 @@ -1,7 +1,7 @@ /* -*- linux-c -*- */ /* - * Driver for USB Scanners (linux-2.5.64) + * Driver for USB Scanners (linux-2.5) * * Copyright (C) 1999, 2000, 2001, 2002 David E. Nelson * Copyright (C) 2002, 2003 Henning Meier-Geinitz @@ -350,6 +350,9 @@ * - Added vendor/product ids for Artec, Avision, Brother, Medion, Primax, * Prolink, Fujitsu, Plustek, and SYSCAN scanners. * - Fixed generation of devfs names if dynamic minors are disabled. + * - Used kobject reference counting to free the scn struct when the device + * is closed and disconnected. Avoids crashes when writing to a + * disconnected device. (Thanks to Greg KH). * * TODO * - Performance @@ -427,6 +430,7 @@ return; default: dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); + return; } dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data); @@ -461,6 +465,7 @@ return -ENODEV; } scn = usb_get_intfdata(intf); + kobject_get(&scn->kobj); dev = scn->scn_dev; @@ -521,6 +526,8 @@ up(&scn_mutex); up(&(scn->sem)); + kobject_put(&scn->kobj); + return 0; } @@ -813,6 +820,37 @@ return retval; } +static void destroy_scanner (struct kobject *kobj) +{ + struct scn_usb_data *scn; + + dbg ("%s", __FUNCTION__); + + scn = to_scanner(kobj); + + down (&scn_mutex); + down (&(scn->sem)); + + usb_driver_release_interface(&scanner_driver, + &scn->scn_dev->actconfig->interface[scn->ifnum]); + + kfree(scn->ibuf); + kfree(scn->obuf); + + dbg("%s: De-allocating minor:%d", __FUNCTION__, scn->scn_minor); + devfs_unregister(scn->devfs); + usb_deregister_dev(1, scn->scn_minor); + usb_free_urb(scn->scn_irq); + usb_put_dev(scn->scn_dev); + up (&(scn->sem)); + kfree (scn); + up (&scn_mutex); +} + +static struct kobj_type scanner_kobj_type = { + .release = destroy_scanner, +}; + static struct file_operations usb_scanner_fops = { .owner = THIS_MODULE, @@ -982,6 +1020,8 @@ return -ENOMEM; } memset (scn, 0, sizeof(struct scn_usb_data)); + kobject_init(&scn->kobj); + scn->kobj.ktype = &scanner_kobj_type; scn->scn_irq = usb_alloc_urb(0, GFP_KERNEL); if (!scn->scn_irq) { @@ -1049,6 +1089,7 @@ } + usb_get_dev(dev); scn->bulk_in_ep = have_bulk_in; scn->bulk_out_ep = have_bulk_out; scn->intr_ep = have_intr; @@ -1089,28 +1130,13 @@ intf->kdev = NODEV; usb_set_intfdata(intf, NULL); - if (scn) { - down (&scn_mutex); - down (&(scn->sem)); - - if(scn->intr_ep) { - dbg("disconnect_scanner(%d): Unlinking IRQ URB", scn->scn_minor); - usb_unlink_urb(scn->scn_irq); - } - usb_driver_release_interface(&scanner_driver, - &scn->scn_dev->actconfig->interface[scn->ifnum]); - - kfree(scn->ibuf); - kfree(scn->obuf); - - dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor); - devfs_unregister(scn->devfs); - usb_deregister_dev(1, scn->scn_minor); - usb_free_urb(scn->scn_irq); - up (&(scn->sem)); - kfree (scn); - up (&scn_mutex); + if(scn->intr_ep) { + dbg("%s(%d): Unlinking IRQ URB", __FUNCTION__, scn->scn_minor); + usb_unlink_urb(scn->scn_irq); } + + if (scn) + kobject_put(&scn->kobj); } /* we want to look at all devices, as the vendor/product id can change diff -Nru a/drivers/usb/image/scanner.h b/drivers/usb/image/scanner.h --- a/drivers/usb/image/scanner.h Mon Mar 17 11:46:59 2003 +++ b/drivers/usb/image/scanner.h Mon Mar 17 11:46:59 2003 @@ -1,5 +1,5 @@ /* - * Driver for USB Scanners (linux-2.5.64) + * Driver for USB Scanners (linux-2.5) * * Copyright (C) 1999, 2000, 2001, 2002 David E. Nelson * Previously maintained by Brian Beattie @@ -335,7 +335,9 @@ wait_queue_head_t rd_wait_q; /* read timeouts */ struct semaphore sem; /* lock to prevent concurrent reads or writes */ unsigned int rd_nak_timeout; /* Seconds to wait before read() timeout. */ + struct kobject kobj; /* Handles our reference counting */ }; +#define to_scanner(d) container_of(d, struct scn_usb_data, kobj) extern devfs_handle_t usb_devfs_handle;