Skip to content

Commit 906dec1

Browse files
Michal Peciogregkh
authored andcommitted
usb: xhci: Fix isochronous Ring Underrun/Overrun event handling
The TRB pointer of these events points at enqueue at the time of error occurrence on xHCI 1.1+ HCs or it's NULL on older ones. By the time we are handling the event, a new TD may be queued at this ring position. I can trigger this race by rising interrupt moderation to increase IRQ handling delay. Similar delay may occur naturally due to system load. If this ever happens after a Missed Service Error, missed TDs will be skipped and the new TD processed as if it matched the event. It could be given back prematurely, risking data loss or buffer UAF by the xHC. Don't complete TDs on xrun events and don't warn if queued TDs don't match the event's TRB pointer, which can be NULL or a link/no-op TRB. Don't warn if there are no queued TDs at all. Now that it's safe, also handle xrun events if the skip flag is clear. This ensures completion of any TD stuck in 'error mid TD' state right before the xrun event, which could happen if a driver submits a finite number of URBs to a buggy HC and then an error occurs on the last TD. Signed-off-by: Michal Pecio <[email protected]> Signed-off-by: Mathias Nyman <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent bfa8459 commit 906dec1

File tree

1 file changed

+14
-6
lines changed

1 file changed

+14
-6
lines changed

drivers/usb/host/xhci-ring.c

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2627,6 +2627,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
26272627
int status = -EINPROGRESS;
26282628
struct xhci_ep_ctx *ep_ctx;
26292629
u32 trb_comp_code;
2630+
bool ring_xrun_event = false;
26302631

26312632
slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
26322633
ep_index = TRB_TO_EP_ID(le32_to_cpu(event->flags)) - 1;
@@ -2733,14 +2734,12 @@ static int handle_tx_event(struct xhci_hcd *xhci,
27332734
* Underrun Event for OUT Isoch endpoint.
27342735
*/
27352736
xhci_dbg(xhci, "Underrun event on slot %u ep %u\n", slot_id, ep_index);
2736-
if (ep->skip)
2737-
break;
2738-
return 0;
2737+
ring_xrun_event = true;
2738+
break;
27392739
case COMP_RING_OVERRUN:
27402740
xhci_dbg(xhci, "Overrun event on slot %u ep %u\n", slot_id, ep_index);
2741-
if (ep->skip)
2742-
break;
2743-
return 0;
2741+
ring_xrun_event = true;
2742+
break;
27442743
case COMP_MISSED_SERVICE_ERROR:
27452744
/*
27462745
* When encounter missed service error, one or more isoc tds
@@ -2813,6 +2812,7 @@ static int handle_tx_event(struct xhci_hcd *xhci,
28132812
*/
28142813
if (trb_comp_code != COMP_STOPPED &&
28152814
trb_comp_code != COMP_STOPPED_LENGTH_INVALID &&
2815+
!ring_xrun_event &&
28162816
!ep_ring->last_td_was_short) {
28172817
xhci_warn(xhci, "Event TRB for slot %u ep %u with no TDs queued\n",
28182818
slot_id, ep_index);
@@ -2847,6 +2847,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
28472847
goto check_endpoint_halted;
28482848
}
28492849

2850+
/* TD was queued after xrun, maybe xrun was on a link, don't panic yet */
2851+
if (ring_xrun_event)
2852+
return 0;
2853+
28502854
/*
28512855
* Skip the Force Stopped Event. The 'ep_trb' of FSE is not in the current
28522856
* TD pointed by 'ep_ring->dequeue' because that the hardware dequeue
@@ -2893,6 +2897,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
28932897
*/
28942898
} while (ep->skip);
28952899

2900+
/* Get out if a TD was queued at enqueue after the xrun occurred */
2901+
if (ring_xrun_event)
2902+
return 0;
2903+
28962904
if (trb_comp_code == COMP_SHORT_PACKET)
28972905
ep_ring->last_td_was_short = true;
28982906
else

0 commit comments

Comments
 (0)