Unbounded XHCI extended-capability pointer walk allows OOB MMIO read/write and kernel DoS from malicious PCI controller
Summary
xhci_pci_take_controller at xhci_pci.c:398-413 walks XHCI extended-capability list using offsets directly from controller MMIO: initial eecp = XHCI_HCS0_XECP(cparams)<<2 where XHCI_HCS0_XECP is 16-bit field (xhcireg.h:70) so first eecp can be up to 0xFFFF*4=256KB, larger than typical 8-64KB BARs. XHCI_XECP_NEXT is 8-bit. XREAD4/XREAD1/XWRITE1 expand to plain bus_space_read/write with NO bounds check (xhcireg.h:205-222). sc->sc_io_size recorded at xhci_pci.c:206 but NEVER consulted in this file. OOB MMIO reads at :401/:405/:413 and OOB MMIO write at :410 (XWRITE1 OS_SEM). No iteration cap: controller returning NEXT=0xFF from OOB read (0xFFFFFFFF) loops unboundedly with growing eecp until fault. On platforms with precise pmap_mapdev bounds: kernel page fault panic; on platforms returning 0xFFFFFFFF for OOB: infinite loop. Malicious PCI XHCI controller via Thunderbolt/ExpressCard/PCIe hot-plug. Same class as DF-1092 (EHCI). Fix: bound eecp+4<=sc->sc_io_size in loop condition.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-1106 Β· 8 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df1106.c | trigger-source | documentation-only source trace stub | 1.9 KB | view raw |
| build.sh | build-script | no compilation needed (latent) | 345 B | view raw |
| run.sh | run-script | no runtime trigger | 218 B | view raw |
| fix.diff | suggested-fix | bound eecp+4 against sc->sc_io_size in the XECP loop | 1022 B | view raw |
| VERDICT.md | verdict | full source-level trace + Phase 6 hard-blocker analysis | 3.8 KB | β raw |
| README.md | readme | claim summary + reproducibility notes | 2.8 KB | β raw |
| ../fix_build_combined.log | build-log | Combined 41-finding kernel build (rc=0, -Werror clean) | 5.6 MB | β download |
| ../fix_build_summary.txt | build-summary | Summary of the combined 41-finding kernel build | 826 B | view raw |
DF-1106 β Unbounded XHCI extended-capability pointer walk
Claim
xhci_pci_take_controller() at sys/bus/u4b/controller/xhci_pci.c:398-413:
for (eecp = XHCI_HCS0_XECP(cparams) << 2; /* line 398 */
eecp != 0 && XHCI_XECP_NEXT(eec); /* line 399 */
eecp += XHCI_XECP_NEXT(eec) << 2) { /* line 400 */
eec = XREAD4(sc, capa, eecp); /* line 401 β OOB read */
if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY)
continue;
bios_sem = XREAD1(sc, capa, eecp + XHCI_XECP_BIOS_SEM); /* line 405 β OOB */
...
XWRITE1(sc, capa, eecp + XHCI_XECP_OS_SEM, 1); /* line 410 β OOB write */
...
}
XHCI_HCS0_XECPis a 16-bit field (xhcireg.h:70) β initialeecpcan be up to0xFFFF << 2 = 262140bytes, larger than typical XHCI BARs (8-64 KB).XHCI_XECP_NEXTis an 8-bit field (xhcireg.h:192) β growseecpby up to0xFF << 2 = 1020bytes per iteration, with no termination other than reading a 0 from the controller.XREAD4/XREAD1/XWRITE1(xhcireg.h:205-222) expand to plainbus_space_read/write_*with no bounds check againstsc->sc_io_size(which IS recorded at xhci_pci.c:206 but never consulted in this file).
A malicious PCI XHCI controller (Thunderbolt / ExpressCard / PCIe hot-plug)
can:
- Supply an eecp pointing past its BAR β on platforms with precise
pmap_mapdev bounds this is a kernel page fault panic.
- Return NEXT=0xFF from an OOB read (returns 0xFFFFFFFF) β loops
unboundedly with growing eecp until fault (DoS).
- Make the kernel perform an OOB MMIO write at line 410
(XWRITE1(sc, capa, eecp + XHCI_XECP_OS_SEM, 1)).
Same class as DF-1092 (EHCI).
Reproducibility on this guest
The default audit guest exposes no XHCI controller to the kernel.
QEMU is launched with -device virtio-net-pci and no USB controller; the
kernel boots without attaching xhci0. pciconf -lv | grep -i xhci is
empty and dmesg | grep -iE 'xhci|usb' shows no XHCI attach.
The XHCI driver is built into the GENERIC kernel, but xhci_pci_attach
(and thus xhci_pci_take_controller) is never called without an XHCI PCI
device. The bug is latent β confirmed by source trace, not
runtime-triggerable on this guest. QEMU can emulate an XHCI controller
(-device qemu-xhci), but adding one would require modifying the vm.sh
QEMU command (guest-facing PCI topology change), and the bug is
fundamentally a malicious-controller scenario where the controller lies
about its capability registers β QEMU's emulated XHCI reports honest
values, so even with -device qemu-xhci the bounds check would never
trigger.
Fix
fix.diff: bound eecp + 4 against sc->sc_io_size in the loop
condition so any pointer past the mapped BAR terminates the walk.
DF-1106 β VERDICT
Verdict: NOT REPRODUCED at runtime (latent β no XHCI controller on the audit guest, and the trigger is fundamentally a malicious-controller scenario). Bug confirmed by source trace.
Mechanism (source-confirmed)
xhci_pci_take_controller() at sys/bus/u4b/controller/xhci_pci.c:398-413:
for (eecp = XHCI_HCS0_XECP(cparams) << 2; /* :398 */
eecp != 0 && XHCI_XECP_NEXT(eec); /* :399 */
eecp += XHCI_XECP_NEXT(eec) << 2) { /* :400 */
eec = XREAD4(sc, capa, eecp); /* :401 β OOB read */
if (XHCI_XECP_ID(eec) != XHCI_ID_USB_LEGACY)
continue;
bios_sem = XREAD1(sc, capa, eecp + XHCI_XECP_BIOS_SEM); /* :405 β OOB */
if (bios_sem == 0)
continue;
...
XWRITE1(sc, capa, eecp + XHCI_XECP_OS_SEM, 1); /* :410 β OOB write */
to = 500;
while (1) {
bios_sem = XREAD1(sc, capa, eecp + XHCI_XECP_BIOS_SEM); /* :413 β OOB */
...
}
}
XHCI_HCS0_XECPis a 16-bit field (sys/bus/u4b/controller/xhcireg.h:70) β initialeecpcan be up to0xFFFF << 2 = 262140, larger than typical XHCI BARs (8-64 KB).XHCI_XECP_NEXTis an 8-bit field (xhcireg.h:192) β growseecpby up to0xFF << 2 = 1020per iteration.XREAD4/XREAD1/XWRITE1(xhcireg.h:205-222) expand to plainbus_space_read/write_*with no bounds check.sc->sc_io_sizeIS recorded at xhci_pci.c:206 but is never consulted in this loop.
A malicious XHCI controller can:
- Supply an eecp past its BAR β kernel page-fault panic on platforms
with precise pmap_mapdev bounds.
- Return NEXT=0xFF from an OOB read (i.e. 0xFFFFFFFF) β
XHCI_XECP_NEXT(0xFFFFFFFF) = 0xFF (never 0) β loops unboundedly with
growing eecp until fault (DoS).
- Force the kernel to perform an OOB MMIO write at line 410.
Same class as DF-1092 (EHCI).
Why it can't be triggered on this guest
$ dmesg | grep -iE 'xhci|usb' (empty) $ pciconf -lv | grep -iE 'xhci|class: 0x0c03' (empty)
The QEMU guest has no XHCI controller. Even if one were added via
-device qemu-xhci, the QEMU emulated XHCI reports honest
extended-capability registers, so eecp would always be a valid in-BAR
offset and the OOB walk wouldn't trigger. The bug is fundamentally a
malicious-controller scenario (Thunderbolt / ExpressCard / PCIe hot-plug
of a device that lies about its capability registers) β not reproducible
on the audit guest.
Exploit chain
Primitive class: OOB MMIO read/write driven by attacker-controlled PCI capability registers. In principle an OOB MMIO write could be weaponised into code execution if the write targeted sensitive platform MMIO (e.g. SPI flash mapping, MSIX tables). BUT:
- The primitive requires a malicious PCI device on the bus β not reproducible from any userspace action on the audit guest.
- QEMU's emulated XHCI is well-behaved and won't trigger the bug.
Valid hard blocker ("vulnerable code path is dead code at runtime on
this guest AND no harness can exercise it without hardware the guest
lacks"). Documented as latent. No uid=0 achievable on this guest.
Fix
fix.diff adds eecp + 4 <= sc->sc_io_size to the loop condition so any
pointer past the mapped BAR terminates the walk. Validated as applies +
compiles in a clean GENERIC kernel build (rebuilt fresh, no
warnings/errors).
Fix validation
not_testable β path not runtime-reachable on this guest. Validated
fix.diff applies cleanly and compiles with -Werror in a full
make nativekernel KERNCONF=X86_64_GENERIC (xhci_pci.o rebuilt fresh,
no warnings/errors β see ../DF-1096/all_fixes_build.log).
PoC changes
- Wrote
df1106.c(documentation-only stub explaining the source trace). - Wrote
fix.diff,build.sh,run.sh.
Fix verification
not_testablecompile validated
see evidence pack
Confirmed kernel references
β
Detail
Exploit chain
none
Evidence (decisive lines)
β
Verdict
Source-confirmed. XHCI extended-cap list walk no bounds -> OOB MMIO. No XHCI HW. Compile validated.
No comments yet.