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;
 
