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 -*- */
/*
* 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 @@ irq_scanner(struct urb *urb, struct pt_regs *regs)
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 @@ open_scanner(struct inode * inode, struct file * file)
return -ENODEV;
}
scn = usb_get_intfdata(intf);
kobject_get(&scn->kobj);
dev = scn->scn_dev;
......@@ -521,6 +526,8 @@ close_scanner(struct inode * inode, struct file * file)
up(&scn_mutex);
up(&(scn->sem));
kobject_put(&scn->kobj);
return 0;
}
......@@ -813,6 +820,37 @@ ioctl_scanner(struct inode *inode, struct file *file,
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 @@ probe_scanner(struct usb_interface *intf,
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 @@ probe_scanner(struct usb_interface *intf,
}
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 @@ disconnect_scanner(struct usb_interface *intf)
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);
dbg("%s(%d): Unlinking IRQ URB", __FUNCTION__, 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
......
/*
* 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 @@ struct scn_usb_data {
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;
......
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