• Alan Stern's avatar
    USB: fix race between hub_disconnect and recursively_mark_NOTATTACHED · 543d7784
    Alan Stern authored
    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: [<c16d5fb0>] recursively_mark_NOTATTACHED+0x20/0x60
    Call Trace:
     [<c16d5fc4>] recursively_mark_NOTATTACHED+0x34/0x60
     [<c16d5fc4>] recursively_mark_NOTATTACHED+0x34/0x60
     [<c16d5fc4>] recursively_mark_NOTATTACHED+0x34/0x60
     [<c16d5fc4>] recursively_mark_NOTATTACHED+0x34/0x60
     [<c16d6082>] usb_set_device_state+0x92/0x120
     [<c16d862b>] usb_disconnect+0x2b/0x1a0
     [<c16dd4c0>] usb_remove_hcd+0xb0/0x160
     [<c19ca846>] ? _raw_spin_unlock_irqrestore+0x26/0x50
     [<c1704efc>] ehci_mid_remove+0x1c/0x30
     [<c1704f26>] ehci_mid_stop_host+0x16/0x30
     [<c16f7698>] penwell_otg_work+0xd28/0x3520
     [<c19c945b>] ? __schedule+0x39b/0x7f0
     [<c19cdb9d>] ? sub_preempt_count+0x3d/0x50
     [<c125e97d>] process_one_work+0x11d/0x3d0
     [<c19c7f4d>] ? mutex_unlock+0xd/0x10
     [<c125e0e5>] ? manage_workers.isra.24+0x1b5/0x270
     [<c125f009>] worker_thread+0xf9/0x320
     [<c19ca846>] ? _raw_spin_unlock_irqrestore+0x26/0x50
     [<c125ef10>] ? rescuer_thread+0x2b0/0x2b0
     [<c1264ac4>] kthread+0x94/0xa0
     [<c19d0f77>] ret_from_kernel_thread+0x1b/0x28
     [<c1264a30>] ? 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: default avatarAlan Stern <stern@rowland.harvard.edu>
    Reported-by: default avatar"Du, Changbin" <changbinx.du@intel.com>
    Tested-by: default avatar"Du, Changbin" <changbinx.du@intel.com>
    CC: <stable@vger.kernel.org>
    Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
    543d7784
hub.c 156 KB