aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2015-12-16 13:32:38 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-12-18 12:30:34 -0500
commite50293ef9775c5f1cf3fcc093037dd6a8c5684ea (patch)
tree01a8506d548f2bc1615a60e5aae2293abfd6c986
parentabdc9a3b4bac97add99e1d77dc6d28623afe682b (diff)
USB: fix invalid memory access in hub_activate()
Commit 8520f38099cc ("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: Alan Stern <stern@rowland.harvard.edu> Reported-by: Alexandru Cornea <alexandru.cornea@intel.com> Tested-by: Alexandru Cornea <alexandru.cornea@intel.com> Fixes: 8520f38099cc ("USB: change hub initialization sleeps to delayed_work") CC: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/core/hub.c22
1 files changed, 19 insertions, 3 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index a5cc032ef77a..ddbf32d599cb 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1035,10 +1035,20 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
1035 unsigned delay; 1035 unsigned delay;
1036 1036
1037 /* Continue a partial initialization */ 1037 /* Continue a partial initialization */
1038 if (type == HUB_INIT2) 1038 if (type == HUB_INIT2 || type == HUB_INIT3) {
1039 goto init2; 1039 device_lock(hub->intfdev);
1040 if (type == HUB_INIT3) 1040
1041 /* Was the hub disconnected while we were waiting? */
1042 if (hub->disconnected) {
1043 device_unlock(hub->intfdev);
1044 kref_put(&hub->kref, hub_release);
1045 return;
1046 }
1047 if (type == HUB_INIT2)
1048 goto init2;
1041 goto init3; 1049 goto init3;
1050 }
1051 kref_get(&hub->kref);
1042 1052
1043 /* The superspeed hub except for root hub has to use Hub Depth 1053 /* The superspeed hub except for root hub has to use Hub Depth
1044 * value as an offset into the route string to locate the bits 1054 * value as an offset into the route string to locate the bits
@@ -1236,6 +1246,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
1236 queue_delayed_work(system_power_efficient_wq, 1246 queue_delayed_work(system_power_efficient_wq,
1237 &hub->init_work, 1247 &hub->init_work,
1238 msecs_to_jiffies(delay)); 1248 msecs_to_jiffies(delay));
1249 device_unlock(hub->intfdev);
1239 return; /* Continues at init3: below */ 1250 return; /* Continues at init3: below */
1240 } else { 1251 } else {
1241 msleep(delay); 1252 msleep(delay);
@@ -1257,6 +1268,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
1257 /* Allow autosuspend if it was suppressed */ 1268 /* Allow autosuspend if it was suppressed */
1258 if (type <= HUB_INIT3) 1269 if (type <= HUB_INIT3)
1259 usb_autopm_put_interface_async(to_usb_interface(hub->intfdev)); 1270 usb_autopm_put_interface_async(to_usb_interface(hub->intfdev));
1271
1272 if (type == HUB_INIT2 || type == HUB_INIT3)
1273 device_unlock(hub->intfdev);
1274
1275 kref_put(&hub->kref, hub_release);
1260} 1276}
1261 1277
1262/* Implement the continuations for the delays above */ 1278/* Implement the continuations for the delays above */