Commit 0d00dc26 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

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: default avatarAlan Stern <stern@rowland.harvard.edu>
Reported-by: default avatarDon Zickus <dzickus@redhat.com>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 01bb6501
...@@ -624,7 +624,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, ...@@ -624,7 +624,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf,
/* print devices for all busses */ /* print devices for all busses */
list_for_each_entry(bus, &usb_bus_list, bus_list) { list_for_each_entry(bus, &usb_bus_list, bus_list) {
/* recurse through all children of the root hub */ /* recurse through all children of the root hub */
if (!bus->root_hub) if (!bus_to_hcd(bus)->rh_registered)
continue; continue;
usb_lock_device(bus->root_hub); usb_lock_device(bus->root_hub);
ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos, ret = usb_device_dump(&buf, &nbytes, &skip_bytes, ppos,
......
...@@ -1011,10 +1011,7 @@ static int register_root_hub(struct usb_hcd *hcd) ...@@ -1011,10 +1011,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (retval) { if (retval) {
dev_err (parent_dev, "can't register root hub for %s, %d\n", dev_err (parent_dev, "can't register root hub for %s, %d\n",
dev_name(&usb_dev->dev), retval); dev_name(&usb_dev->dev), retval);
} } else {
mutex_unlock(&usb_bus_list_lock);
if (retval == 0) {
spin_lock_irq (&hcd_root_hub_lock); spin_lock_irq (&hcd_root_hub_lock);
hcd->rh_registered = 1; hcd->rh_registered = 1;
spin_unlock_irq (&hcd_root_hub_lock); spin_unlock_irq (&hcd_root_hub_lock);
...@@ -1023,6 +1020,7 @@ static int register_root_hub(struct usb_hcd *hcd) ...@@ -1023,6 +1020,7 @@ static int register_root_hub(struct usb_hcd *hcd)
if (HCD_DEAD(hcd)) if (HCD_DEAD(hcd))
usb_hc_died (hcd); /* This time clean up */ usb_hc_died (hcd); /* This time clean up */
} }
mutex_unlock(&usb_bus_list_lock);
return retval; return retval;
} }
......
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