Commit 3eee4565 authored by Hans Yang's avatar Hans Yang Committed by Luis Henriques

usb: core : hub: Fix BOS 'NULL pointer' kernel panic

commit 464ad8c4 upstream.

When a USB 3.0 mass storage device is disconnected in transporting
state, storage device driver may handle it as a transport error and
reset the device by invoking usb_reset_and_verify_device()
and following could happen:

in usb_reset_and_verify_device():
   udev->bos = NULL;

For U1/U2 enabled devices, driver will disable LPM, and in some
conditions:
   from usb_unlocked_disable_lpm()
    --> usb_disable_lpm()
    --> usb_enable_lpm()
        udev->bos->ss_cap->bU1devExitLat;

And it causes 'NULL pointer' and 'kernel panic':

[  157.976257] Unable to handle kernel NULL pointer dereference
at virtual address 00000010
...
[  158.026400] PC is at usb_enable_link_state+0x34/0x2e0
[  158.031442] LR is at usb_enable_lpm+0x98/0xac
...
[  158.137368] [<ffffffc0006a1cac>] usb_enable_link_state+0x34/0x2e0
[  158.143451] [<ffffffc0006a1fec>] usb_enable_lpm+0x94/0xac
[  158.148840] [<ffffffc0006a20e8>] usb_disable_lpm+0xa8/0xb4
...
[  158.214954] Kernel panic - not syncing: Fatal exception

This commit moves 'udev->bos = NULL' behind usb_unlocked_disable_lpm()
to prevent from NULL pointer access.

Issue can be reproduced by following setup:
1) A SS pen drive behind a SS hub connected to the host.
2) Transporting data between the pen drive and the host.
3) Abruptly disconnect hub and pen drive from host.
4) With a chance it crashes.
Signed-off-by: default avatarHans Yang <hansy@nvidia.com>
Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 7ced8d0b
...@@ -5347,9 +5347,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -5347,9 +5347,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
if (udev->usb2_hw_lpm_enabled == 1) if (udev->usb2_hw_lpm_enabled == 1)
usb_set_usb2_hardware_lpm(udev, 0); usb_set_usb2_hardware_lpm(udev, 0);
bos = udev->bos;
udev->bos = NULL;
/* Disable LPM and LTM while we reset the device and reinstall the alt /* Disable LPM and LTM while we reset the device and reinstall the alt
* settings. Device-initiated LPM settings, and system exit latency * settings. Device-initiated LPM settings, and system exit latency
* settings are cleared when the device is reset, so we have to set * settings are cleared when the device is reset, so we have to set
...@@ -5358,15 +5355,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -5358,15 +5355,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
ret = usb_unlocked_disable_lpm(udev); ret = usb_unlocked_disable_lpm(udev);
if (ret) { if (ret) {
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
goto re_enumerate; goto re_enumerate_no_bos;
} }
ret = usb_disable_ltm(udev); ret = usb_disable_ltm(udev);
if (ret) { if (ret) {
dev_err(&udev->dev, "%s Failed to disable LTM\n.", dev_err(&udev->dev, "%s Failed to disable LTM\n.",
__func__); __func__);
goto re_enumerate; goto re_enumerate_no_bos;
} }
bos = udev->bos;
udev->bos = NULL;
for (i = 0; i < SET_CONFIG_TRIES; ++i) { for (i = 0; i < SET_CONFIG_TRIES; ++i) {
/* ep0 maxpacket size may change; let the HCD know about it. /* ep0 maxpacket size may change; let the HCD know about it.
...@@ -5463,10 +5463,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev) ...@@ -5463,10 +5463,11 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
return 0; return 0;
re_enumerate: re_enumerate:
/* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1);
usb_release_bos_descriptor(udev); usb_release_bos_descriptor(udev);
udev->bos = bos; udev->bos = bos;
re_enumerate_no_bos:
/* LPM state doesn't matter when we're about to destroy the device. */
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV; return -ENODEV;
} }
......
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