Commit 533030ca authored by Alan Stern's avatar Alan Stern Committed by Willy Tarreau

USB: fix invalid memory access in hub_activate()

commit e50293ef upstream.

Commit 8520f380 ("USB: change hub initialization sleeps to
delayed_work") changed the hub_activate() routine to make part of it
run in a workqueue.  However, the commit failed to take a reference to
the usb_hub structure or to lock the hub interface while doing so.  As
a result, if a hub is plugged in and quickly unplugged before the work
routine can run, the routine will try to access memory that has been
deallocated.  Or, if the hub is unplugged while the routine is
running, the memory may be deallocated while it is in active use.

This patch fixes the problem by taking a reference to the usb_hub at
the start of hub_activate() and releasing it at the end (when the work
is finished), and by locking the hub interface while the work routine
is running.  It also adds a check at the start of the routine to see
if the hub has already been disconnected, in which nothing should be
done.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Reported-by: default avatarAlexandru Cornea <alexandru.cornea@intel.com>
Tested-by: default avatarAlexandru Cornea <alexandru.cornea@intel.com>
Fixes: 8520f380 ("USB: change hub initialization sleeps to delayed_work")
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
[bwh: Backported to 3.2: add prototype for hub_release() before first use]
Signed-off-by: default avatarBen Hutchings <ben@decadent.org.uk>
(cherry picked from commit 10037421)
[wt: made a few changes :
  - adjusted context due to some autopm code being added only in 2.6.33
  - no device_{lock,unlock}() in 2.6.32, use up/down(&->sem) instead]
Signed-off-by: default avatarWilly Tarreau <w@1wt.eu>
parent 00972bcd
......@@ -149,7 +149,7 @@ EXPORT_SYMBOL_GPL(ehci_cf_port_reset_rwsem);
#define HUB_DEBOUNCE_STEP 25
#define HUB_DEBOUNCE_STABLE 100
static void hub_release(struct kref *kref);
static int usb_reset_and_verify_device(struct usb_device *udev);
static inline char *portspeed(int portstatus)
......@@ -678,10 +678,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
unsigned delay;
/* Continue a partial initialization */
if (type == HUB_INIT2)
goto init2;
if (type == HUB_INIT3)
if (type == HUB_INIT2 || type == HUB_INIT3) {
down(&hub->intfdev->sem);
/* Was the hub disconnected while we were waiting? */
if (hub->disconnected) {
up(&hub->intfdev->sem);
kref_put(&hub->kref, hub_release);
return;
}
if (type == HUB_INIT2)
goto init2;
goto init3;
}
kref_get(&hub->kref);
/* After a resume, port power should still be on.
* For any other type of activation, turn it on.
......@@ -820,6 +830,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
PREPARE_DELAYED_WORK(&hub->init_work, hub_init_func3);
schedule_delayed_work(&hub->init_work,
msecs_to_jiffies(delay));
up(&hub->intfdev->sem);
return; /* Continues at init3: below */
} else {
msleep(delay);
......@@ -836,6 +847,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
/* Scan all ports that need attention */
kick_khubd(hub);
if (type == HUB_INIT2 || type == HUB_INIT3)
up(&hub->intfdev->sem);
kref_put(&hub->kref, hub_release);
}
/* Implement the continuations for the delays above */
......
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