This commit fixes the following oops: [10238.622067] scsi host3: uas_eh_bus_reset_handler start [10240.766164] usb 3-4: reset SuperSpeed USB device number 3 using xhci_hcd [10245.779365] usb 3-4: device descriptor read/8, error -110 [10245.883331] usb 3-4: reset SuperSpeed USB device number 3 using xhci_hcd [10250.897603] usb 3-4: device descriptor read/8, error -110 [10251.058200] BUG: unable to handle kernel NULL pointer dereference at 0000000000000040 [10251.058244] IP: [<ffffffff815ac6e1>] xhci_check_streams_endpoint+0x91/0x140 <snip> [10251.059473] Call Trace: [10251.059487] [<ffffffff815aca6c>] xhci_calculate_streams_and_bitmask+0xbc/0x130 [10251.059520] [<ffffffff815aeb5f>] xhci_alloc_streams+0x10f/0x5a0 [10251.059548] [<ffffffff810a4685>] ? check_preempt_curr+0x75/0xa0 [10251.059575] [<ffffffff810a46dc>] ? ttwu_do_wakeup+0x2c/0x100 [10251.059601] [<ffffffff810a49e6>] ? ttwu_do_activate.constprop.111+0x66/0x70 [10251.059635] [<ffffffff815779ab>] usb_alloc_streams+0xab/0xf0 [10251.059662] [<ffffffffc0616b48>] uas_configure_endpoints+0x128/0x150 [uas] [10251.059694] [<ffffffffc0616bac>] uas_post_reset+0x3c/0xb0 [uas] [10251.059722] [<ffffffff815727d9>] usb_reset_device+0x1b9/0x2a0 [10251.059749] [<ffffffffc0616f42>] uas_eh_bus_reset_handler+0xb2/0x190 [uas] [10251.059781] [<ffffffff81514293>] scsi_try_bus_reset+0x53/0x110 [10251.059808] [<ffffffff815163b7>] scsi_eh_bus_reset+0xf7/0x270 <snip> The problem is the following call sequence (simplified): 1) usb_reset_device 2) usb_reset_and_verify_device 2) hub_port_init 3) hub_port_finish_reset 3) xhci_discover_or_reset_device This frees xhci->devs[slot_id]->eps[ep_index].ring for all eps but 0 4) usb_get_device_descriptor This fails 5) hub_port_init fails 6) usb_reset_and_verify_device fails, does not restore device config 7) uas_post_reset 8) xhci_alloc_streams NULL deref on the free-ed ring The real problem here is that xhci_discover_or_reset_device frees the rings during a reset, and if usb_reset_and_verify_device then fails to restore the device configuration (and thus re-alloc the rings), that we're then left with a device which from the usb_device_driver's pov is still functional (its disconnect method will get called when khub gets around to it), but in reality is not. This commit adds a check for this condition to xhci_check_args(), which should cover all public entry points into the xhci driver. Cc: stable@xxxxxxxxxxxxxxx Reported-by: Claudio Bizzarri <claudio.bizzarri@xxxxxxxxx> Signed-off-by: Hans de Goede <hdegoede@xxxxxxxxxx> --- drivers/usb/host/xhci.c | 37 ++++++++++++++++++++++++++----------- 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c index 642791c..64160ff 100644 --- a/drivers/usb/host/xhci.c +++ b/drivers/usb/host/xhci.c @@ -1146,10 +1146,12 @@ unsigned int xhci_last_valid_endpoint(u32 added_ctxs) * returns 0 this is a root hub; returns -EINVAL for NULL pointers. */ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, - struct usb_host_endpoint *ep, int check_ep, bool check_virt_dev, - const char *func) { + struct usb_host_endpoint *ep, bool check_ep, + bool check_ep_ring, bool check_virt_dev, const char *func) +{ struct xhci_hcd *xhci; - struct xhci_virt_device *virt_dev; + struct xhci_virt_device *virt_dev = NULL; + unsigned int ep_index; if (!hcd || (check_ep && !ep) || !udev) { pr_debug("xHCI %s called with invalid args\n", func); @@ -1176,6 +1178,18 @@ static int xhci_check_args(struct usb_hcd *hcd, struct usb_device *udev, } } + /* + * If we get called after a reset, then usb_reset_device -> + * xhci_discover_or_reset_device may have free-ed eps[ep_index].ring, + * without it being setup again because of the usb_reset_device + * failing to re-configure the device. + */ + if (check_ep_ring) { + ep_index = xhci_get_endpoint_index(&ep->desc); + if (virt_dev->eps[ep_index].ring == NULL) + return -ENODEV; + } + if (xhci->xhc_state & XHCI_STATE_HALTED) return -ENODEV; @@ -1281,7 +1295,7 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags) int size, i; if (!urb || xhci_check_args(hcd, urb->dev, urb->ep, - true, true, __func__) <= 0) + true, true, true, __func__) <= 0) return -EINVAL; slot_id = urb->dev->slot_id; @@ -1596,7 +1610,7 @@ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, u32 new_add_flags, new_drop_flags; int ret; - ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); + ret = xhci_check_args(hcd, udev, ep, true, false, true, __func__); if (ret <= 0) return ret; xhci = hcd_to_xhci(hcd); @@ -1675,7 +1689,7 @@ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct xhci_virt_device *virt_dev; int ret = 0; - ret = xhci_check_args(hcd, udev, ep, 1, true, __func__); + ret = xhci_check_args(hcd, udev, ep, true, false, true, __func__); if (ret <= 0) { /* So we won't queue a reset ep command for a root hub */ ep->hcpriv = NULL; @@ -2694,7 +2708,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_slot_ctx *slot_ctx; struct xhci_command *command; - ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); + ret = xhci_check_args(hcd, udev, NULL, false, false, true, __func__); if (ret <= 0) return ret; xhci = hcd_to_xhci(hcd); @@ -2793,7 +2807,7 @@ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_virt_device *virt_dev; int i, ret; - ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); + ret = xhci_check_args(hcd, udev, NULL, false, false, true, __func__); if (ret <= 0) return; xhci = hcd_to_xhci(hcd); @@ -2979,7 +2993,8 @@ static int xhci_check_streams_endpoint(struct xhci_hcd *xhci, if (!ep) return -EINVAL; - ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__); + ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, + true, true, true, __func__); if (ret <= 0) return -EINVAL; if (usb_ss_max_streams(&ep->ss_ep_comp) == 0) { @@ -3429,7 +3444,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev) struct xhci_slot_ctx *slot_ctx; int old_active_eps = 0; - ret = xhci_check_args(hcd, udev, NULL, 0, false, __func__); + ret = xhci_check_args(hcd, udev, NULL, false, false, false, __func__); if (ret <= 0) return ret; xhci = hcd_to_xhci(hcd); @@ -3600,7 +3615,7 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev) pm_runtime_put_noidle(hcd->self.controller); #endif - ret = xhci_check_args(hcd, udev, NULL, 0, true, __func__); + ret = xhci_check_args(hcd, udev, NULL, false, false, true, __func__); /* If the host is halted due to driver unload, we still need to free the * device. */ -- 2.1.0 -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html