Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S935283AbaBDWWH (ORCPT ); Tue, 4 Feb 2014 17:22:07 -0500 Received: from mail.linuxfoundation.org ([140.211.169.12]:56627 "EHLO mail.linuxfoundation.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S933908AbaBDVIY (ORCPT ); Tue, 4 Feb 2014 16:08:24 -0500 From: Greg Kroah-Hartman To: linux-kernel@vger.kernel.org Cc: Greg Kroah-Hartman , stable@vger.kernel.org, Alan Stern , "Du, Changbin" Subject: [PATCH 3.12 025/133] USB: fix race between hub_disconnect and recursively_mark_NOTATTACHED Date: Tue, 4 Feb 2014 13:07:06 -0800 Message-Id: <20140204210737.743548458@linuxfoundation.org> X-Mailer: git-send-email 1.8.5.1.163.gd7aced9 In-Reply-To: <20140204210737.008598235@linuxfoundation.org> References: <20140204210737.008598235@linuxfoundation.org> User-Agent: quilt/0.61-1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 3.12-stable review patch. If anyone has any objections, please let me know. ------------------ From: Alan Stern commit 543d7784b07ffd16cc82a9cb4e1e0323fd0040f1 upstream. There is a race in the hub driver between hub_disconnect() and recursively_mark_NOTATTACHED(). This race can be triggered if the driver is unbound from a device at the same time as the bus's root hub is removed. When the race occurs, it can cause an oops: BUG: unable to handle kernel NULL pointer dereference at 0000015c IP: [] recursively_mark_NOTATTACHED+0x20/0x60 Call Trace: [] recursively_mark_NOTATTACHED+0x34/0x60 [] recursively_mark_NOTATTACHED+0x34/0x60 [] recursively_mark_NOTATTACHED+0x34/0x60 [] recursively_mark_NOTATTACHED+0x34/0x60 [] usb_set_device_state+0x92/0x120 [] usb_disconnect+0x2b/0x1a0 [] usb_remove_hcd+0xb0/0x160 [] ? _raw_spin_unlock_irqrestore+0x26/0x50 [] ehci_mid_remove+0x1c/0x30 [] ehci_mid_stop_host+0x16/0x30 [] penwell_otg_work+0xd28/0x3520 [] ? __schedule+0x39b/0x7f0 [] ? sub_preempt_count+0x3d/0x50 [] process_one_work+0x11d/0x3d0 [] ? mutex_unlock+0xd/0x10 [] ? manage_workers.isra.24+0x1b5/0x270 [] worker_thread+0xf9/0x320 [] ? _raw_spin_unlock_irqrestore+0x26/0x50 [] ? rescuer_thread+0x2b0/0x2b0 [] kthread+0x94/0xa0 [] ret_from_kernel_thread+0x1b/0x28 [] ? kthread_create_on_node+0xc0/0xc0 One problem is that recursively_mark_NOTATTACHED() uses the intfdata value and hub->hdev->maxchild while hub_disconnect() is clearing them. Another problem is that it uses hub->ports[i] while the port device is being released. To fix this race, we need to hold the device_state_lock while hub_disconnect() changes the values. (Note that usb_disconnect() and hub_port_connect_change() already acquire this lock at similar critical times during a USB device's life cycle.) We also need to remove the port devices after maxchild has been set to 0, instead of before. Signed-off-by: Alan Stern Reported-by: "Du, Changbin" Tested-by: "Du, Changbin" Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -1610,7 +1610,7 @@ static void hub_disconnect(struct usb_in { struct usb_hub *hub = usb_get_intfdata(intf); struct usb_device *hdev = interface_to_usbdev(intf); - int i; + int port1; /* Take the hub off the event list and don't let it be added again */ spin_lock_irq(&hub_event_lock); @@ -1625,11 +1625,15 @@ static void hub_disconnect(struct usb_in hub->error = 0; hub_quiesce(hub, HUB_DISCONNECT); - usb_set_intfdata (intf, NULL); + /* Avoid races with recursively_mark_NOTATTACHED() */ + spin_lock_irq(&device_state_lock); + port1 = hdev->maxchild; + hdev->maxchild = 0; + usb_set_intfdata(intf, NULL); + spin_unlock_irq(&device_state_lock); - for (i = 0; i < hdev->maxchild; i++) - usb_hub_remove_port_device(hub, i + 1); - hub->hdev->maxchild = 0; + for (; port1 > 0; --port1) + usb_hub_remove_port_device(hub, port1); if (hub->hdev->speed == USB_SPEED_HIGH) highspeed_hubs--; -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/