aboutsummaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorMathias Nyman2021-01-29 15:00:26 +0200
committerGreg Kroah-Hartman2021-01-29 14:16:50 +0100
commit04d21f7219acec66751f5512aa8a69f528c5b36a (patch)
treebd85ba9d7bc475755c1f1247279f4fc56d84d760 /drivers/usb
parentc089cadaa0dfb3a02c848197ef9106a04d445604 (diff)
xhci: prevent a theoretical endless loop while preparing rings.
xhci driver links together segments in a ring buffer by turning the last TRB of a segment into a link TRB, pointing to the beginning of the next segment. If the first TRB of every segment for some unknown reason is a link TRB pointing to the next segment, then prepare_ring() loops indefinitely. This isn't something the xhci driver would do. xHC hardware has access to these rings, it sholdn't be writing link TRBs either, but with broken xHC hardware this could in theory be possible. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Link: https://lore.kernel.org/r/20210129130044.206855-10-mathias.nyman@linux.intel.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/host/xhci-ring.c7
1 files changed, 7 insertions, 0 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index 54fcb907b2ba..2ef55484317e 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -2952,6 +2952,7 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
u32 ep_state, unsigned int num_trbs, gfp_t mem_flags)
{
unsigned int num_trbs_needed;
+ unsigned int link_trb_count = 0;
/* Make sure the endpoint has been added to xHC schedule */
switch (ep_state) {
@@ -3023,6 +3024,12 @@ static int prepare_ring(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
ep_ring->enq_seg = ep_ring->enq_seg->next;
ep_ring->enqueue = ep_ring->enq_seg->trbs;
+
+ /* prevent infinite loop if all first trbs are link trbs */
+ if (link_trb_count++ > ep_ring->num_segs) {
+ xhci_warn(xhci, "Ring is an endless link TRB loop\n");
+ return -EINVAL;
+ }
}
return 0;
}