From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754635AbcAWO5Z (ORCPT ); Sat, 23 Jan 2016 09:57:25 -0500 Received: from wtarreau.pck.nerim.net ([62.212.114.60]:27537 "EHLO 1wt.eu" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753516AbcAWOqz (ORCPT ); Sat, 23 Jan 2016 09:46:55 -0500 Message-Id: <20160123141223.549473094@1wt.eu> User-Agent: quilt/0.63-1 Date: Sat, 23 Jan 2016 15:12:57 +0100 From: Willy Tarreau To: linux-kernel@vger.kernel.org, stable@vger.kernel.org Cc: Alan Stern , Alexandru Cornea , Greg Kroah-Hartman , Ben Hutchings , Willy Tarreau Subject: [PATCH 2.6.32 36/42] USB: fix invalid memory access in hub_activate() MIME-Version: 1.0 Content-Type: text/plain; charset=ISO-8859-15 In-Reply-To: Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 2.6.32-longterm review patch. If anyone has any objections, please let me know. ------------------ From: Alan Stern commit e50293ef9775c5f1cf3fcc093037dd6a8c5684ea upstream. 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 Reported-by: Alexandru Cornea Tested-by: Alexandru Cornea Fixes: 8520f38099cc ("USB: change hub initialization sleeps to delayed_work") Signed-off-by: Greg Kroah-Hartman [bwh: Backported to 3.2: add prototype for hub_release() before first use] Signed-off-by: Ben Hutchings (cherry picked from commit 10037421b529bc1fc18994e94e37d745184c4ea9) [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: Willy Tarreau --- drivers/usb/core/hub.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 02aad50..5ebd1f1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -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 */ -- 1.7.12.2.21.g234cd45.dirty