]> git.hungrycats.org Git - linux/commitdiff
usb: xhci: Handle bogus TRB pointers in Missed Service Error events
authorMichal Pecio <michal.pecio@gmail.com>
Thu, 6 Aug 2026 14:21:13 +0000 (17:21 +0300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 2 Sep 2026 12:31:48 +0000 (14:31 +0200)
commit 3d9eeb336131bc5a174367c384fa00c15c8744fd upstream.

xHCI 1.0 allowed these pointers to be zero. Some Intel chipsets from the
era usually set it to zero, but sometimes (apparently) to the next TRB
after the one referenced by the previous transfer event on the endpoint.

Usually that's indeed the missed TD, but it may also be the last TRB of
a two-TRB TD already completed with Short Packet on its first TRB. Then
the driver skips all pending TDs, failing to find a match.

When handling Missed Service Error, scan TD list twice and only really
skip TDs in the second pass if the first pass found a match. This won't
catch bogus pointers to wrong TDs, but such a bug would be practically
impossible to detect automatically and isn't known to exist.

Reported-by: Bart Nagel <bart@tremby.net>
Closes: https://lore.kernel.org/linux-usb/al_hchyOdPoPWKEo@spiral/
Suggested-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Fixes: d0b619599e52 ("usb: xhci: Expedite skipping missed isoch TDs on modern HCs")
Cc: stable@vger.kernel.org
Signed-off-by: Michal Pecio <michal.pecio@gmail.com>
Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://patch.msgid.link/20260806142113.2436238-18-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/usb/host/xhci-ring.c

index 2bb7569c95054959e238c0cd40f70f9bd95ee749..58cd8967b3df5f4c38cff6ac02009142df2b0eea 100644 (file)
@@ -2653,6 +2653,17 @@ static bool xhci_spurious_success_tx_event(struct xhci_hcd *xhci,
        }
 }
 
+static struct xhci_td *find_td_by_dma(struct xhci_ring *ep_ring, dma_addr_t dma)
+{
+       struct xhci_td *td;
+
+       if (dma)
+               list_for_each_entry(td, &ep_ring->td_list, td_list)
+                       if (trb_in_td(td, dma))
+                               return td;
+       return NULL;
+}
+
 /*
  * If this function returns an error condition, it means it got a Transfer
  * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
@@ -2845,8 +2856,11 @@ static int handle_tx_event(struct xhci_hcd *xhci,
                xhci_dequeue_td(xhci, td, ep_ring, td->status);
        }
 
-       /* If the TRB pointer is NULL, missed TDs will be skipped on the next event */
-       if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !ep_trb_dma)
+       /*
+        * We don't know how many TDs were missed when ep_trb_dma is zero (as permitted by
+        * xHCI 1.0) or bogus. Bail out leaving ep->skip set, next event will sort it out.
+        */
+       if (trb_comp_code == COMP_MISSED_SERVICE_ERROR && !find_td_by_dma(ep_ring, ep_trb_dma))
                return 0;
 
        if (list_empty(&ep_ring->td_list)) {