aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHerbert Xu <herbert@gondor.apana.org.au>2010-01-10 04:15:03 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2010-03-02 17:54:13 -0500
commitf7410ced7f931bb1ad79d1336412cf7b7a33cb14 (patch)
treec25966dee9bb112a8e9495cd161a4db95c1785a7
parentd23356da714595b888686d22cd19061323c09190 (diff)
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.h2
-rw-r--r--drivers/usb/core/hub.c12
-rw-r--r--drivers/usb/core/usb.c3
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 {
248 /* xHCI specific functions */ 248 /* xHCI specific functions */
249 /* Called by usb_alloc_dev to alloc HC device structures */ 249 /* Called by usb_alloc_dev to alloc HC device structures */
250 int (*alloc_dev)(struct usb_hcd *, struct usb_device *); 250 int (*alloc_dev)(struct usb_hcd *, struct usb_device *);
251 /* Called by usb_release_dev to free HC device structures */ 251 /* Called by usb_disconnect to free HC device structures */
252 void (*free_dev)(struct usb_hcd *, struct usb_device *); 252 void (*free_dev)(struct usb_hcd *, struct usb_device *);
253 253
254 /* Bandwidth computation functions */ 254 /* 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)
1524 udev->devnum = devnum; 1524 udev->devnum = devnum;
1525} 1525}
1526 1526
1527static void hub_free_dev(struct usb_device *udev)
1528{
1529 struct usb_hcd *hcd = bus_to_hcd(udev->bus);
1530
1531 /* Root hubs aren't real devices, so don't free HCD resources */
1532 if (hcd->driver->free_dev && udev->parent)
1533 hcd->driver->free_dev(hcd, udev);
1534}
1535
1527/** 1536/**
1528 * usb_disconnect - disconnect a device (usbcore-internal) 1537 * usb_disconnect - disconnect a device (usbcore-internal)
1529 * @pdev: pointer to device being disconnected 1538 * @pdev: pointer to device being disconnected
@@ -1592,6 +1601,8 @@ void usb_disconnect(struct usb_device **pdev)
1592 *pdev = NULL; 1601 *pdev = NULL;
1593 spin_unlock_irq(&device_state_lock); 1602 spin_unlock_irq(&device_state_lock);
1594 1603
1604 hub_free_dev(udev);
1605
1595 put_device(&udev->dev); 1606 put_device(&udev->dev);
1596} 1607}
1597 1608
@@ -3166,6 +3177,7 @@ loop_disable:
3166loop: 3177loop:
3167 usb_ep0_reinit(udev); 3178 usb_ep0_reinit(udev);
3168 release_address(udev); 3179 release_address(udev);
3180 hub_free_dev(udev);
3169 usb_put_dev(udev); 3181 usb_put_dev(udev);
3170 if ((status == -ENOTCONN) || (status == -ENOTSUPP)) 3182 if ((status == -ENOTCONN) || (status == -ENOTSUPP))
3171 break; 3183 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)
225 hcd = bus_to_hcd(udev->bus); 225 hcd = bus_to_hcd(udev->bus);
226 226
227 usb_destroy_configuration(udev); 227 usb_destroy_configuration(udev);
228 /* Root hubs aren't real devices, so don't free HCD resources */
229 if (hcd->driver->free_dev && udev->parent)
230 hcd->driver->free_dev(hcd, udev);
231 usb_put_hcd(hcd); 228 usb_put_hcd(hcd);
232 kfree(udev->product); 229 kfree(udev->product);
233 kfree(udev->manufacturer); 230 kfree(udev->manufacturer);