Commit b2b54c70 authored by Henning Meier-Geinitz's avatar Henning Meier-Geinitz Committed by Greg Kroah-Hartman

[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.
parent bf056a3b
/* -*- linux-c -*- */ /* -*- 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) 1999, 2000, 2001, 2002 David E. Nelson
* Copyright (C) 2002, 2003 Henning Meier-Geinitz * Copyright (C) 2002, 2003 Henning Meier-Geinitz
...@@ -350,6 +350,9 @@ ...@@ -350,6 +350,9 @@
* - Added vendor/product ids for Artec, Avision, Brother, Medion, Primax, * - Added vendor/product ids for Artec, Avision, Brother, Medion, Primax,
* Prolink, Fujitsu, Plustek, and SYSCAN scanners. * Prolink, Fujitsu, Plustek, and SYSCAN scanners.
* - Fixed generation of devfs names if dynamic minors are disabled. * - 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 * TODO
* - Performance * - Performance
...@@ -427,6 +430,7 @@ irq_scanner(struct urb *urb, struct pt_regs *regs) ...@@ -427,6 +430,7 @@ irq_scanner(struct urb *urb, struct pt_regs *regs)
return; return;
default: default:
dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status); dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
return;
} }
dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data); dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data);
...@@ -461,6 +465,7 @@ open_scanner(struct inode * inode, struct file * file) ...@@ -461,6 +465,7 @@ open_scanner(struct inode * inode, struct file * file)
return -ENODEV; return -ENODEV;
} }
scn = usb_get_intfdata(intf); scn = usb_get_intfdata(intf);
kobject_get(&scn->kobj);
dev = scn->scn_dev; dev = scn->scn_dev;
...@@ -521,6 +526,8 @@ close_scanner(struct inode * inode, struct file * file) ...@@ -521,6 +526,8 @@ close_scanner(struct inode * inode, struct file * file)
up(&scn_mutex); up(&scn_mutex);
up(&(scn->sem)); up(&(scn->sem));
kobject_put(&scn->kobj);
return 0; return 0;
} }
...@@ -813,6 +820,37 @@ ioctl_scanner(struct inode *inode, struct file *file, ...@@ -813,6 +820,37 @@ ioctl_scanner(struct inode *inode, struct file *file,
return retval; 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 static struct
file_operations usb_scanner_fops = { file_operations usb_scanner_fops = {
.owner = THIS_MODULE, .owner = THIS_MODULE,
...@@ -982,6 +1020,8 @@ probe_scanner(struct usb_interface *intf, ...@@ -982,6 +1020,8 @@ probe_scanner(struct usb_interface *intf,
return -ENOMEM; return -ENOMEM;
} }
memset (scn, 0, sizeof(struct scn_usb_data)); 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); scn->scn_irq = usb_alloc_urb(0, GFP_KERNEL);
if (!scn->scn_irq) { if (!scn->scn_irq) {
...@@ -1049,6 +1089,7 @@ probe_scanner(struct usb_interface *intf, ...@@ -1049,6 +1089,7 @@ probe_scanner(struct usb_interface *intf,
} }
usb_get_dev(dev);
scn->bulk_in_ep = have_bulk_in; scn->bulk_in_ep = have_bulk_in;
scn->bulk_out_ep = have_bulk_out; scn->bulk_out_ep = have_bulk_out;
scn->intr_ep = have_intr; scn->intr_ep = have_intr;
...@@ -1089,28 +1130,13 @@ disconnect_scanner(struct usb_interface *intf) ...@@ -1089,28 +1130,13 @@ disconnect_scanner(struct usb_interface *intf)
intf->kdev = NODEV; intf->kdev = NODEV;
usb_set_intfdata(intf, NULL); usb_set_intfdata(intf, NULL);
if (scn) { if(scn->intr_ep) {
down (&scn_mutex); dbg("%s(%d): Unlinking IRQ URB", __FUNCTION__, scn->scn_minor);
down (&(scn->sem)); usb_unlink_urb(scn->scn_irq);
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)
kobject_put(&scn->kobj);
} }
/* we want to look at all devices, as the vendor/product id can change /* we want to look at all devices, as the vendor/product id can change
......
/* /*
* 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) 1999, 2000, 2001, 2002 David E. Nelson
* Previously maintained by Brian Beattie * Previously maintained by Brian Beattie
...@@ -335,7 +335,9 @@ struct scn_usb_data { ...@@ -335,7 +335,9 @@ struct scn_usb_data {
wait_queue_head_t rd_wait_q; /* read timeouts */ wait_queue_head_t rd_wait_q; /* read timeouts */
struct semaphore sem; /* lock to prevent concurrent reads or writes */ struct semaphore sem; /* lock to prevent concurrent reads or writes */
unsigned int rd_nak_timeout; /* Seconds to wait before read() timeout. */ 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; extern devfs_handle_t usb_devfs_handle;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment