[Bug 297053] xhci(4): TRB-array memory corruption and kernel panic for bulk frames larger than 64 KiB

[email protected] Sat, 25 Jul 2026 20:31:17 +0000
Newsgroups gmane.os.freebsd.bugs
Message-ID <[email protected]/bugzilla/>
https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=297053

            Bug ID: 297053
           Summary: xhci(4): TRB-array memory corruption and kernel panic
                    for bulk frames larger than 64 KiB
           Product: Base System
           Version: 16.0-CURRENT
          Hardware: amd64
                OS: Any
            Status: New
          Severity: Affects Some People
          Priority: ---
         Component: kern
          Assignee: [email protected]
          Reporter: [email protected]

A regression introduced by commit
  e0b235ecd4fa9a67578be68057dbcad797f96397 ("xhci: Refactor
  xhci_generic_setup code") can make xhci_setup_normal_trbs() write beyond
  struct xhci_td::td_trb[] when a bulk or interrupt transfer contains a
  frame larger than XHCI_TD_PAYLOAD_MAX (65536 bytes).

  The problem is still present in main as of 2026-07-25.

  This is a bug in the vanilla xHCI implementation rather than a
  device-driver-specific buffer-size violation.

  The stock USB framework permits transfers larger than 64 KiB:

  * struct usb_config describes bufsize as the total pipe-buffer size.
  * The stock ugen implementation permits receive and transmit buffer
    sizes up to 256 KiB.
  * ugen_ctrl_read_callback() can submit the configured maximum length as
    one USB frame.
  * xhci_xfer_setup() sets hc_max_frame_size to 65536 and allocates
    additional struct xhci_td objects according to max_data_length divided
    by max_hc_frame_size.

  This shows that the host-controller driver is expected to divide a large
  frame among the additional TDs it allocated.

  After the refactoring, xhci_setup_bulk() assigns only one xhci_td to each
  USB frame. It passes the complete frame length to
  xhci_setup_normal_trbs(). That function repeatedly increments i and
  writes td->td_trb[i] until the entire frame has been described, without
  checking the array bound or advancing to td->obj_next.

  On amd64 with 4 KiB pages, a 128 KiB frame requires approximately 32
  data TRBs. struct xhci_td only has enough TRB storage for a payload of at
  most 64 KiB, plus boundary and link bookkeeping. The writes consequently
  continue into fields following td_trb[], including page_cache.

  Observed panic:

  panic: page fault

  usb_pc_cpu_flush(pc=0x104e8a000)
  xhci_setup_normal_trbs(cache=..., offset=0, len=131072,
      mps=1024, td=..., td_next=NULL, ...)
  xhci_setup_bulk()
  xhci_setup_generic_chain()
  xhci_device_generic_enter()
  usbd_pipe_enter()
  usb_command_wrapper()
  usbd_transfer_submit()

  KGDB showed i == 32 in xhci_setup_normal_trbs(). The invalid page_cache
  value passed to usb_pc_cpu_flush() is consistent with the preceding TRB
  writes corrupting the remainder of struct xhci_td.

  Code-level reproduction condition:

  1. Use a USB device with a bulk endpoint on an xHCI controller.
  2. Configure a one-frame bulk transfer with a 128 KiB buffer.
  3. Set frame 0's length to 131072 bytes.
  4. Submit the transfer.

  A minimal ugen/USB_FS client can construct this using only the interfaces
  provided by the vanilla FreeBSD USB stack. Drivers that happen to use
  buffers no larger than 64 KiB merely leave the defect dormant.

  Expected result:

  xhci(4) should divide a large Normal-TRB frame among the additional
  struct xhci_td objects allocated by xhci_xfer_setup(), or reject the
  transfer cleanly during setup.

  Actual result:

  xhci_setup_normal_trbs() writes beyond td_trb[], corrupts kernel memory,
  and can panic in usb_pc_cpu_flush().

  The implementation before e0b235ecd4fa divided large frames according
  to max_hc_frame_size and consumed multiple TDs. The proper repair would
  restore that behavior for Normal TRBs. A bounds assertion before every
  td_trb[] write would also provide useful defensive protection.

  Regression commit:
 
https://github.com/freebsd/freebsd-src/commit/e0b235ecd4fa9a67578be68057dbcad797f96397

  Associated review:
  https://reviews.freebsd.org/D57130

-- 
You are receiving this mail because:
You are the assignee for the bug.