diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2010-01-10 20:15:03 +1100 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-03-02 14:54:13 -0800 |
commit | f7410ced7f931bb1ad79d1336412cf7b7a33cb14 (patch) | |
tree | c25966dee9bb112a8e9495cd161a4db95c1785a7 | |
parent | d23356da714595b888686d22cd19061323c09190 (diff) | |
download | talos-obmc-linux-f7410ced7f931bb1ad79d1336412cf7b7a33cb14.tar.gz talos-obmc-linux-f7410ced7f931bb1ad79d1336412cf7b7a33cb14.zip |
USB: Move hcd free_dev call into usb_disconnect to fix oops
USB: Move hcd free_dev call into usb_disconnect
I found a way to oops the kernel:
1. Open a USB device through devio.
2. Remove the hcd module in the host kernel.
3. Close the devio file descriptor.
The problem is that closing the file descriptor does usb_release_dev
as it is the last reference. usb_release_dev then tries to invoke
the hcd free_dev function (or rather dereferencing the hcd driver
struct). This causes an oops as the hcd driver has already been
unloaded so the struct is gone.
This patch tries to fix this by bringing the free_dev call earlier
and into usb_disconnect. I have verified that repeating the
above steps no longer crashes with this patch applied.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/core/hcd.h | 2 | ||||
-rw-r--r-- | drivers/usb/core/hub.c | 12 | ||||
-rw-r--r-- | drivers/usb/core/usb.c | 3 |
3 files changed, 13 insertions, 4 deletions
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h index 8953ded69541..a3cdb09734ab 100644 --- a/drivers/usb/core/hcd.h +++ b/drivers/usb/core/hcd.h @@ -248,7 +248,7 @@ struct hc_driver { /* xHCI specific functions */ /* Called by usb_alloc_dev to alloc HC device structures */ int (*alloc_dev)(struct usb_hcd *, struct usb_device *); - /* Called by usb_release_dev to free HC device structures */ + /* Called by usb_disconnect to free HC device structures */ void (*free_dev)(struct usb_hcd *, struct usb_device *); /* Bandwidth computation functions */ diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 0e0a190bbd00..e198ff8a11c0 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1524,6 +1524,15 @@ static void update_address(struct usb_device *udev, int devnum) udev->devnum = devnum; } +static void hub_free_dev(struct usb_device *udev) +{ + struct usb_hcd *hcd = bus_to_hcd(udev->bus); + + /* Root hubs aren't real devices, so don't free HCD resources */ + if (hcd->driver->free_dev && udev->parent) + hcd->driver->free_dev(hcd, udev); +} + /** * usb_disconnect - disconnect a device (usbcore-internal) * @pdev: pointer to device being disconnected @@ -1592,6 +1601,8 @@ void usb_disconnect(struct usb_device **pdev) *pdev = NULL; spin_unlock_irq(&device_state_lock); + hub_free_dev(udev); + put_device(&udev->dev); } @@ -3166,6 +3177,7 @@ loop_disable: loop: usb_ep0_reinit(udev); release_address(udev); + hub_free_dev(udev); usb_put_dev(udev); if ((status == -ENOTCONN) || (status == -ENOTSUPP)) break; diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 32966ccdff63..1297e9b16a51 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -225,9 +225,6 @@ static void usb_release_dev(struct device *dev) hcd = bus_to_hcd(udev->bus); usb_destroy_configuration(udev); - /* Root hubs aren't real devices, so don't free HCD resources */ - if (hcd->driver->free_dev && udev->parent) - hcd->driver->free_dev(hcd, udev); usb_put_hcd(hcd); kfree(udev->product); kfree(udev->manufacturer); |