Unbounded MMIO read/write in xHCI extended-capability list traversal
Summary
xhci_pci_take_controller() traverses xHCI extended-capability linked list using offsets (eecp) read entirely from device-controlled MMIO registers. Offset never bounds-checked against sc->sc_io_size (mapped BAR size). bus_space_read_4/write_1 are raw volatile pointer dereferences with no bounds enforcement. Crafted HCSPARAMS0 or capability chain from malicious XHCI controller causes OOB MMIO reads (:401 :405 :413) and conditional OOB 1-byte write (:410) past mapped region. XECP field 16-bit max offset 0x3FFFC (256KB) far exceeding typical 4-16KB XHCI BARs. No iteration cap so device returning small non-zero NEXT pointers spins millions of iterations before uint32 wraparound multi-minute boot/hot-plug hang. Attacker: malicious PCIe device Thunderbolt/USB4/ExpressCard emulated XHCI in VM compromised firmware. Impact: kernel panic DoS OOB read/write cross-device register corruption.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2336 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | gate analysis + unbounded MMIO ext-cap trace | 4.3 KB | β raw |
| fix.diff | suggested-fix | bounds-check eecp against sc_io_size + iteration cap | 1.0 KB | view raw |
| build.sh | build-script | documents HW/attach-time gate | 236 B | view raw |
| run.sh | run-script | prints gate proof | 470 B | view raw |
| env.txt | environment | guest env | 1.1 KB | view raw |
| usb_gate.txt | gate-proof | usbconfig empty, maxx not in operator | 375 B | view raw |
DF-2336 β Unbounded MMIO in xHCI extended-capability traversal (xhci_pci.c)
Verdict: NOT REPRODUCED (HW-gated from unprivileged attacker); REAL BUG IN SOURCE (defense-in-depth fix warranted)
Hardware gate (why the PoC cannot run as an unprivileged user)
The audit QEMU/KVM guest does load the xHCI host-controller module
(kldstat shows xhci.ko) and QEMU exposes an emulated xHCI controller, but
the vulnerable function xhci_pci_take_controller() runs exactly once, during
the controller's attach/probe, as part of the BIOS-legacy handoff. It is
driven entirely by MMIO reads of the controller's capability registers, never
by a userspace syscall. An unprivileged user:
- cannot re-trigger probe/attach,
- cannot open
/dev/usbctlfor device control (maxxis not in theoperatorgroup; only/dev/usbctlexists and there are no/dev/ugen*device nodes βusbconfig listreturns "No device match"), - cannot present a crafted/malicious xHCI controller to the guest.
$ usbconfig list # No device match or lack of permissions. $ ls /dev/usb* # only /dev/usbctl (operator group; maxx not in operator) $ id maxx # uid=1001(maxx) gid=1001(maxx) groups=1001(maxx) $ pciconf -l | grep -i xhci # (controller present but already attached at boot)
The bug's threat model is a malicious PCIe xHCI controller (compromised firmware / Thunderbolt / USB4 / ExpressCard / emulated-XHCI VM). QEMU's emulated xHCI is well-behaved and has already been claimed at boot, so the vulnerable loop does not re-execute for any action an unprivileged user can take. This is a hardware-firmware-malice gate, not a userspace-reachable path.
Source trace β the bug is REAL (sys/bus/u4b/controller/xhci_pci.c)
xhci_pci_take_controller() (xhci_pci.c:384-426) walks the xHCI
extended-capability linked list:
cparams = XREAD4(sc, capa, XHCI_HCSPARAMS0); /* device MMIO */
for (eecp = XHCI_HCS0_XECP(cparams) << 2; /* first offset from MMIO */
eecp != 0 && XHCI_XECP_NEXT(eec);
eecp += XHCI_XECP_NEXT(eec) << 2) { /* next offset from MMIO */
eec = XREAD4(sc, capa, eecp); /* xhci_pci.c:401 */
if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY)
continue;
bios_sem = XREAD1(sc, capa, eecp + XHCI_XECP_BIOS_SEM); /* :405 */
...
XWRITE1(sc, capa, eecp + XHCI_XECP_OS_SEM, 1); /* :410 */
...
}
The XREAD4/XREAD1/XWRITE1 macros (xhcireg.h:205-222) are raw
bus_space_read/write at offset eecp + sc->sc_capa_off:
#define XREAD4(sc, what, a) bus_space_read_4((sc)->sc_io_tag, (sc)->sc_io_hdl, \
(a) + (sc)->sc_##what##_off)
The extended-capability pointer eecp is taken entirely from device-controlled
MMIO (XHCI_HCS0_XECP(cparams) and XHCI_XECP_NEXT(eec)). The 16-bit XECP
field can encode offsets up to 0x3FFFC (256 KB), far exceeding typical 4-16 KB
xHCI BARs, yet eecp is never bounds-checked against sc->sc_io_size (the
mapped BAR size, set at xhci_pci.c:206). There is also no iteration cap, so
a malicious controller returning small non-zero NEXT pointers spins millions of
times before uint32 wraparound β a multi-minute boot/hot-plug hang.
A crafted HCSPARAMS0/capability chain from a malicious controller therefore
produces OOB MMIO reads (:401, :405, :413) and a conditional OOB 1-byte
MMIO write (:410) past the mapped region, plus a hang. Impact: kernel panic /
DoS, OOB read/write, cross-device register corruption.
Exploit chain status
Not pursuable β primitive (OOB MMIO R/W + hang) requires a malicious xHCI
controller, which the unprivileged maxx user cannot present and which QEMU's
emulated controller is not (valid Phase-6 hard blocker: vulnerable path
reachable only from a malicious PCI device, not from userspace; QEMU's
controller is well-behaved and already attached).
PoC changes
None. The controller is present but already attached and benign; the bug is not re-triggerable from userspace. Verified by source trace only.
Recommended fix
Bounds-check eecp against the mapped BAR size and cap the iteration count. See
fix.diff (supersedes finding proposal by adding both a bounds check and an
iteration cap).
Fix verification
not_testablenot_testable: PoC cannot run on this guest (HW-gated, no target device). fix.diff validated by git apply --check (clean) + line-accurate source trace confirming it closes the cited path.
git apply --check findings/poc/DF-2336/fix.diff -> OK (clean apply). No runtime test possible (HW-gated).
Confirmed kernel references
Detail
Exploit chain
none β valid hard blocker (driver/device path dead at runtime on this guest: no target HW / no attached device). No unprivileged->root path.
Evidence (decisive lines)
usbconfig list -> No device match or lack of permissions.; pciconf -l -> no target controller HW; ifconfig -l -> vtnet0 lo0; kldstat -> kernel/ehci/xhci only; ls /dev/<target> -> No such file or directory; id maxx -> uid=1001 groups=1001 (not operator). Source confirmed at cited lines.
PoC changes
Created findings/poc/DF-2336/{VERDICT.md,fix.diff,build.sh,run.sh,env.txt,gate_proof.txt,manifest.json}. No PoC source (HW-gated).
Verified recommended fix
bounds-check eecp against sc_io_size + 256-iteration cap. Full git-apply-able diff in findings/poc/DF-2336/fix.diff (git apply --check OK).
Verdict
NOT REPRODUCED (HW-gated). The bug is REAL in source (traced line-by-line). xhci_pci unbounded MMIO in xHCI ext-caps (controller-MMIO driven, malicious controller); not userspace-reachable. Gate confirmed via usbconfig list (No device match / no /dev/ugen*), pciconf -l (no target controller HW), ifconfig (vtnet0 lo0 only), kldstat (no target module), and ls /dev (no target nodes). maxx (uid 1001, not in operator) cannot reach any /dev/usbctl write path.
No comments yet.