aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2012-09-26 13:09:53 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2012-09-26 13:21:08 -0400
commit0a2314035cab62cafc38ea11ec5b6f95cf347b38 (patch)
tree6b06f4fe9e60ee8590fdd4b0a516fe0c56e37c0f /drivers/usb
parent655db7980596f0ad4f15f8f4c51beb3e705762de (diff)
USB: Fix race condition when removing host controllers
This patch (as1607) fixes a race that can occur if a USB host controller is removed while a process is reading the /sys/kernel/debug/usb/devices file. The usb_device_read() routine uses the bus->root_hub pointer to determine whether or not the root hub is registered. The is not a valid test, because the pointer is set before the root hub gets registered and remains set even after the root hub is unregistered and deallocated. As a result, usb_device_read() or usb_device_dump() can access freed memory, causing an oops. The patch changes the test to use the hcd->rh_registered flag, which does get set and cleared at the appropriate times. It also makes sure to hold the usb_bus_list_lock mutex while setting the flag, so that usb_device_read() will become aware of new root hubs as soon as they are registered. Signed-off-by: Alan Stern <stern@rowland.harvard.edu> Reported-by: Don Zickus <dzickus@redhat.com> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/devices.c2
-rw-r--r--drivers/usb/core/hcd.c6
2 files changed, 3 insertions, 5 deletions
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index f4ead1296820..f460de31acee 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -623,7 +623,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
623 /* print devices for all busses */ 623 /* print devices for all busses */
624 list_for_each_entry(bus, &usb_bus_list, bus_list) { 624 list_for_each_entry(bus, &usb_bus_list, bus_list) {
625 /* recurse through all children of the root hub */ 625 /* recurse through all children of the root hub */
626 if (!bus->root_hub) 626 if (!bus_to_hcd(bus)->rh_registered)
627 continue; 627 continue;
628 usb_lock_device(bus->root_hub); 628 usb_lock_device(bus->root_hub);
629 ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, 629 ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index 35b52f6e1c5e..1e741bca0265 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -1011,10 +1011,7 @@ static int register_root_hub(struct usb_hcd *hcd)
1011 if (retval) { 1011 if (retval) {
1012 dev_err (parent_dev, "can't register root hub for %s, %d\n", 1012 dev_err (parent_dev, "can't register root hub for %s, %d\n",
1013 dev_name(&usb_dev->dev), retval); 1013 dev_name(&usb_dev->dev), retval);
1014 } 1014 } else {
1015 mutex_unlock(&usb_bus_list_lock);
1016
1017 if (retval == 0) {
1018 spin_lock_irq (&hcd_root_hub_lock); 1015 spin_lock_irq (&hcd_root_hub_lock);
1019 hcd->rh_registered = 1; 1016 hcd->rh_registered = 1;
1020 spin_unlock_irq (&hcd_root_hub_lock); 1017 spin_unlock_irq (&hcd_root_hub_lock);
@@ -1023,6 +1020,7 @@ static int register_root_hub(struct usb_hcd *hcd)
1023 if (HCD_DEAD(hcd)) 1020 if (HCD_DEAD(hcd))
1024 usb_hc_died (hcd); /* This time clean up */ 1021 usb_hc_died (hcd); /* This time clean up */
1025 } 1022 }
1023 mutex_unlock(&usb_bus_list_lock);
1026 1024
1027 return retval; 1025 return retval;
1028} 1026}