HWA RC: fix a kernel panic when unplugging the HWA dongle
authorThomas Pugliese <thomas.pugliese@gmail.com>
Fri, 9 Aug 2013 14:25:37 +0000 (09:25 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 12 Aug 2013 22:41:08 +0000 (15:41 -0700)
This patch fixes a kernel panic that can occur when unplugging the HWA
dongle while a downstream device is in the process of disconnecting.
This involved 2 changes.  First, call usb_lock_device_for_reset before
usb_reset_device to synchronize the HWA's post_rest and disconnect
routines.  Second, set the hwarc->neep_urb and hwarc->rd_buffer to NULL
when they are freed in the error path in the post_reset routine.  This
prevents a double free when the disconnect routine is called and attempts
to free those resources again.

Signed-off-by: Thomas Pugliese <thomas.pugliese@gmail.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/uwb/hwa-rc.c

index 0621abef9b4a0e2ecb1291eda1650f7529a64d8c..0257f35cfb9d01983fa738358b54f4b600af568c 100644 (file)
@@ -611,7 +611,16 @@ static
 int hwarc_reset(struct uwb_rc *uwb_rc)
 {
        struct hwarc *hwarc = uwb_rc->priv;
-       return usb_reset_device(hwarc->usb_dev);
+       int result;
+
+       /* device lock must be held when calling usb_reset_device. */
+       result = usb_lock_device_for_reset(hwarc->usb_dev, NULL);
+       if (result >= 0) {
+               result = usb_reset_device(hwarc->usb_dev);
+               usb_unlock_device(hwarc->usb_dev);
+       }
+
+       return result;
 }
 
 /**
@@ -709,8 +718,10 @@ static int hwarc_neep_init(struct uwb_rc *rc)
 
 error_neep_submit:
        usb_free_urb(hwarc->neep_urb);
+       hwarc->neep_urb = NULL;
 error_urb_alloc:
        free_page((unsigned long)hwarc->rd_buffer);
+       hwarc->rd_buffer = NULL;
 error_rd_buffer:
        return -ENOMEM;
 }
@@ -723,7 +734,10 @@ static void hwarc_neep_release(struct uwb_rc *rc)
 
        usb_kill_urb(hwarc->neep_urb);
        usb_free_urb(hwarc->neep_urb);
+       hwarc->neep_urb = NULL;
+
        free_page((unsigned long)hwarc->rd_buffer);
+       hwarc->rd_buffer = NULL;
 }
 
 /**