diff options
author | Hans Yang <hansy@nvidia.com> | 2015-12-01 03:54:59 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-12-01 17:45:52 -0500 |
commit | 464ad8c43a9ead98c2b0eaed86bea727f2ad106e (patch) | |
tree | d06935a40a8ff2f96c2139acc0e845672695880f | |
parent | 33bd2dd03dd0bfa1130d11062a9e5f40d0cf1d3f (diff) |
usb: core : hub: Fix BOS 'NULL pointer' kernel panic
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: Hans Yang <hansy@nvidia.com>
Acked-by: Alan Stern <stern@rowland.harvard.edu>
Cc: stable <stable@vger.kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/core/hub.c | 15 |
1 files changed, 8 insertions, 7 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index bdeadc112d29..585c3cb07da6 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c | |||
@@ -5326,9 +5326,6 @@ static int usb_reset_and_verify_device(struct usb_device *udev) | |||
5326 | if (udev->usb2_hw_lpm_enabled == 1) | 5326 | if (udev->usb2_hw_lpm_enabled == 1) |
5327 | usb_set_usb2_hardware_lpm(udev, 0); | 5327 | usb_set_usb2_hardware_lpm(udev, 0); |
5328 | 5328 | ||
5329 | bos = udev->bos; | ||
5330 | udev->bos = NULL; | ||
5331 | |||
5332 | /* Disable LPM and LTM while we reset the device and reinstall the alt | 5329 | /* Disable LPM and LTM while we reset the device and reinstall the alt |
5333 | * settings. Device-initiated LPM settings, and system exit latency | 5330 | * settings. Device-initiated LPM settings, and system exit latency |
5334 | * settings are cleared when the device is reset, so we have to set | 5331 | * settings are cleared when the device is reset, so we have to set |
@@ -5337,15 +5334,18 @@ static int usb_reset_and_verify_device(struct usb_device *udev) | |||
5337 | ret = usb_unlocked_disable_lpm(udev); | 5334 | ret = usb_unlocked_disable_lpm(udev); |
5338 | if (ret) { | 5335 | if (ret) { |
5339 | dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); | 5336 | dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__); |
5340 | goto re_enumerate; | 5337 | goto re_enumerate_no_bos; |
5341 | } | 5338 | } |
5342 | ret = usb_disable_ltm(udev); | 5339 | ret = usb_disable_ltm(udev); |
5343 | if (ret) { | 5340 | if (ret) { |
5344 | dev_err(&udev->dev, "%s Failed to disable LTM\n.", | 5341 | dev_err(&udev->dev, "%s Failed to disable LTM\n.", |
5345 | __func__); | 5342 | __func__); |
5346 | goto re_enumerate; | 5343 | goto re_enumerate_no_bos; |
5347 | } | 5344 | } |
5348 | 5345 | ||
5346 | bos = udev->bos; | ||
5347 | udev->bos = NULL; | ||
5348 | |||
5349 | for (i = 0; i < SET_CONFIG_TRIES; ++i) { | 5349 | for (i = 0; i < SET_CONFIG_TRIES; ++i) { |
5350 | 5350 | ||
5351 | /* ep0 maxpacket size may change; let the HCD know about it. | 5351 | /* ep0 maxpacket size may change; let the HCD know about it. |
@@ -5442,10 +5442,11 @@ done: | |||
5442 | return 0; | 5442 | return 0; |
5443 | 5443 | ||
5444 | re_enumerate: | 5444 | re_enumerate: |
5445 | /* LPM state doesn't matter when we're about to destroy the device. */ | ||
5446 | hub_port_logical_disconnect(parent_hub, port1); | ||
5447 | usb_release_bos_descriptor(udev); | 5445 | usb_release_bos_descriptor(udev); |
5448 | udev->bos = bos; | 5446 | udev->bos = bos; |
5447 | re_enumerate_no_bos: | ||
5448 | /* LPM state doesn't matter when we're about to destroy the device. */ | ||
5449 | hub_port_logical_disconnect(parent_hub, port1); | ||
5449 | return -ENODEV; | 5450 | return -ENODEV; |
5450 | } | 5451 | } |
5451 | 5452 | ||