aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHans Yang <hansy@nvidia.com>2015-12-01 03:54:59 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2015-12-01 17:45:52 -0500
commit464ad8c43a9ead98c2b0eaed86bea727f2ad106e (patch)
treed06935a40a8ff2f96c2139acc0e845672695880f
parent33bd2dd03dd0bfa1130d11062a9e5f40d0cf1d3f (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.c15
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
5444re_enumerate: 5444re_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;
5447re_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