Config bridges forward unvalidated reg/width into the PCIe MMIO backend whose reg-only bound allows OOB read/write past the MMCFG kernel_map mapping
Summary
legacy_pcib_read/write_config pass caller reg/bytes with zero validation; the MMIO backend bound-checks only reg>4095 - never reg+bytes<=4096 nor alignment (which the port-I/O backend enforces) - so reg=0xfff/2 or 0xffd/4 crosses the pmap_mapdev MMCFG window: kernel page fault or a 1-3 byte OOB write into the adjacent kernel_map mapping. The one caller with chosen reg (/dev/pci PCIOCREAD/WRITE) sets no upper bound on pi_reg. Root-gated (SYSCAP_RESTRICTEDROOT); dormant on QEMU i440FX. KLD-proven with the exact legal pcie_cfgregopen call ACPI makes: control read at 0xffc/4 succeeds, 0xfff/2 -> Fatal trap 12 at exactly pcie_base+0x10000000; width/alignment/end-bound fix validated on rebuilt kernel (OOO rejected, no panic).
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5454 Β· 13 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 3.3 KB | β raw | |
| VERDICT.md | β | 4.2 KB | β raw | |
| df5454.c | β | 3.2 KB | view raw | |
| build.sh | β | 169 B | view raw | |
| run.sh | β | 120 B | view raw | |
| build.log | β | 5.7 KB | view raw | |
| run.log | β | 1.6 KB | view raw | |
| run_patched.log | β | 791 B | view raw | |
| panic.txt | β | 1008 B | view raw | |
| env.txt | β | 611 B | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| fix_build.log | β | 255 B | view raw | |
| verdict.json | β | 4.8 KB | view raw |
DF-5454 β PCIe MMIO config backend: missing reg+width bound β OOB access past pcie_base devmap
File under audit (pass 2): sys/bus/pci/x86_64/pci_bus.c
Sink: sys/bus/pci/x86_64/pci_cfgreg.c (pciereg_cfgread/pciereg_cfgwrite)
What
The legacy host-PCI bridge config bridges legacy_pcib_read_config /
legacy_pcib_write_config (pci_bus.c:61-75) forward caller-supplied
(bus, slot, func, reg, bytes) to pci_cfgregread/pci_cfgregwrite with no
validation. When MMIO config space is active (CFGMECH_PCIE β Intel
7520/7320/915/925/915GM chipsets, pci_cfgreg.c:104-124, or any ACPI MCFG
table, sys/dev/acpica/acpi.c:1669), the request lands in
pciereg_cfgread/pciereg_cfgwrite (pci_cfgreg.c:315-365), which bound-check
only reg > PCIE_REGMAX (4095). They never check reg + bytes <= 4096 and
never check alignment β both of which the port-I/O backend pci_cfgenable
(pci_cfgreg.c:187-193) enforces.
The MMCFG window is pmap_mapdev_uncacheable(base, (maxbus+1) << 20)
(pci_cfgreg.c:278-279) β with maxbus=255 a 256MB kernel_map allocation. The
highest mapped byte is (bus=255, slot=31, func=7, reg=0xfff). A 2-byte access
at reg=0xfff or 4-byte access at reg=0xffd/0xffe reads/writes 1-3 bytes past
the end of that kernel virtual allocation β kernel page fault (panic) or
silent corruption of the adjacent kernel mapping.
Reachability / threat model
/dev/pciPCIOCREAD/PCIOCWRITE (pci_user.c:602-673) checkspi_width in {1,2,4},pi_reg >= 0, alignment β but sets no upper bound onpi_regβ then callsPCIB_READ_CONFIG/PCIB_WRITE_CONFIGβ these bridges. Opening/dev/pcirequiresSYSCAP_RESTRICTEDROOT(pci_user.c:82) β root-only.- Kernel-internal callers keep reg <= 0xff (capability walk clamps/aligns via
pci_fixup_nextptr, pci.c:602-630). No in-tree caller passes reg>0xff except the ioctl. - Requires CFGMECH_PCIE β dormant on QEMU i440FX (no MCFG; host bridge 8086:1237 not in the chipset list), active on most modern MCFG-bearing x86_64 hardware and on the listed Intel chipsets.
Net: privileged-user-triggerable kernel OOB read/write (1-3 bytes) past a kernel_map devmap allocation β reliable panic (DoS), potential corruption of the adjacent kernel mapping. Severity: Low (already-privileged attacker).
Reproduce (this pack)
build.shβ compile the KLD inside the guest (needs /usr/src, cc, make).run.shβkldload /root/df5454/df5454.koas root.
The KLD performs the exact legal pcie_cfgregopen() call the platform/ACPI
makes at boot (activating CFGMECH_PCIE β dormant on the i440FX guest), then:
- CONTROL: in-bounds read (255,31,7,0xffc,4) β succeeds, no fault.
- step2: OOB read (255,31,7,0xfff,2) β Fatal trap 12, supervisor read,
page not present, fault VA exactly
pcie_base + 0x10000000. - step3/4: OOB write / 3-bytes-past read (only reached if step2 survives).
Expected (vulnerable kernel): panic at step2 β see panic.txt.
Expected (patched kernel, fix.diff): no panic; step2 returns 0xffffffff
(rejected), module load completes, guest stays up β see run_patched.log.
Fix
fix.diff β carry the mech-1 backend's width/alignment/end bound into
pciereg_cfgread/pciereg_cfgwrite. Validated: baseline panics, patched
kernel rejects the access and boots/runs normally.
DF-5454 β VERDICT
Finding: legacy_pcib_read_config/legacy_pcib_write_config
(sys/bus/pci/x86_64/pci_bus.c:61-75) forward caller-supplied (reg, bytes)
into pci_cfgregread/pci_cfgregwrite with no validation; when MMIO config
space is active (CFGMECH_PCIE), the backend pciereg_cfgread/
pciereg_cfgwrite (sys/bus/pci/x86_64/pci_cfgreg.c:315-365) bound-checks
only reg > PCIE_REGMAX (4095) β it never checks reg + bytes <= 4096 nor
alignment, both of which the port-I/O backend pci_cfgenable
(pci_cfgreg.c:187-193) enforces. The MMCFG window is a
pmap_mapdev_uncacheable(base, (maxbus+1)<<20) kernel_map allocation
(pci_cfgreg.c:278-279; pmap.c:6174-6217 kmem_alloc_nofault); its highest
mapped byte is (bus=255, slot=31, func=7, reg=0xfff). A width-2 access at
reg=0xfff (or width-4 at reg=0xffd/0xffe) reads/writes 1-3 bytes past the
end of that allocation.
Reproduced β how
-
Baseline (vulnerable kernel, snapshot with-src, kernel #0 Jul 2 2026): - KLD
df5454.ko(this pack) performs the exact legal call a platform makes at boot to activate MMIO config βpcie_cfgregopen(0x40000000, 0, 255), the same call ACPI MCFG handling makes at sys/dev/acpica/acpi.c:1669 (the QEMU i440FX guest has no MCFG and an unlisted host bridge 8086:1237, so the backend is dormant on stock boot; the KLD removes that environmental precondition only). - CONTROL: in-bounds read(255,31,7,0xffc,4)β succeeds (0x00000000), proving the mapping is healthy up to its final byte. - OOB: read(255,31,7,0xfff,2)β Fatal trap 12: page fault while in kernel mode, supervisor read data, page not present, fault VA0xfffff80128660000=pcie_base + 0x10000000exactly (mapping base0xfffff80118660000), stopped atpci_docfgregread+0xb4: movzwl (%rax),%eaxβ the inlined*(volatile uint16_t *)load ofpciereg_cfgread(seepanic.txt,run.log). - Guest wedged in DDB; ssh never returned (rc 124). That isreproduced, impactpanic, from a root-triggered KLD. -
Patched (fix.diff applied, nativekernel build, kernel #1 Oct 5 18:56): - Same KLD, same commands: CONTROL read still OK (
0x0000000b), OOB read at 0xfff/2 rejected β returns0xffffffff, OOB write rejected (no-op), 0xffd/4 rejected,KLDLOAD_RC=0, guest stayed up (seerun_patched.log). Bad behavior GONE β fix validated.
Why root-gated (severity Low, not higher)
- The only caller that supplies attacker-chosen
regis/dev/pciPCIOCREAD/PCIOCWRITE (sys/bus/pci/pci_user.c:602-673): it validates widthβ{1,2,4},pi_reg >= 0and alignment, but sets no upper bound onpi_regβ however opening/dev/pcirequirescaps_priv_check_self(SYSCAP_RESTRICTEDROOT)(pci_user.c:82), i.e. root. - All kernel-internal callers keep reg <= 0xff aligned (capability walk:
pci_fixup_nextptr, sys/bus/pci/pci.c:602-630). - Activation requires CFGMECH_PCIE (Intel 7520/7320/915/925/915GM chipsets at pci_cfgreg.c:108-123, or ACPI MCFG at sys/dev/acpica/acpi.c:1669) β i.e. most modern MCFG-bearing hardware, dormant on QEMU i440FX.
An already-privileged user gets: reliable kernel panic (DoS) and a 1-3 byte OOB write primitive into whatever kernel mapping follows the MMCFG window in kernel_map β corruption, not controllable enough for privilege escalation beyond what the trigger already requires. No unprivileged chain exists (open of /dev/pci is capability-gated). uid=0 escalation: not applicable (trigger requires root).
Conclusion
Real, proven missing-bounds defect in the x86_64 PCI config path β filed
against sys/bus/pci/x86_64/pci_bus.c (the unvalidated pass-through bridges
are this file's code; the sink and the fix live in the sibling backend
pci_cfgreg.c, which pass 1 marked clean). Severity Low (privileged
trigger), confidence certain, reproduced as panic, fix validated end-to-end
(build + boot + re-run).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedfix.diff applied to guest /usr/src (patch -p1, hunks at 320/348), kernel rebuilt with make nativekernel KERNCONF=X86_64_GENERIC and installed (BUILD_RC=0, INSTALL_RC=0), rebooted, exact same PoC re-run: control read unaffected, all OOB accesses rejected (0xffffffff / no-op), KLDLOAD_RC=0, no panic, guest stayed up. Baseline (unpatched) panicked at the same step. Bad behavior gone.
fix.diff; fix_build.log (BUILD_RC=0 / INSTALL_RC=0, kernel #1: Mon Oct 5 18:56:47 2026); run_patched.log (rejected OOB accesses, guest alive); panic.txt + run.log (baseline panic)
Confirmed kernel references
- sys/bus/pci/x86_64/pci_bus.c:61-66
- sys/bus/pci/x86_64/pci_bus.c:70-75
- sys/bus/pci/x86_64/pci_cfgreg.c:315-340
- sys/bus/pci/x86_64/pci_cfgreg.c:342-365
- sys/bus/pci/x86_64/pci_cfgreg.c:278-279
- sys/bus/pci/x86_64/pci_cfgreg.c:187-193
- sys/bus/pci/pci_user.c:602-673
- sys/bus/pci/pci_user.c:75-91
- sys/dev/acpica/acpi.c:1669
- sys/platform/pc64/x86_64/pmap.c:6174-6217
Detail
Evidence (decisive lines)
panic.txt (Fatal trap 12, fault VA 0xfffff80128660000 == pcie_base+256MB, stopped at pci_docfgregread+0xb4 movzwl); run.log (baseline: control read 0xffc/4 ok then step2 panic); run_patched.log (same PoC on fix.diff kernel: step2 returns 0xffffffff rejected, KLDLOAD_RC=0, guest up); df5454.c (PoC source); fix.diff (validated fix); env.txt (guest 6.5-DEVELOPMENT X86_64_GENERIC, i440FX, no MCFG line in dmesg)
PoC changes
authored fresh (no seed pack existed): KLD performs the legal pcie_cfgregopen() call ACPI MCFG makes at boot to activate the dormant CFGMECH_PCIE backend on the i440FX guest, then issues control (0xffc/4) and OOB (0xfff/2, 0xfff/2 write, 0xffd/4) config accesses via the exported pci_cfgregread/write
Verified recommended fix
In pciereg_cfgread/pciereg_cfgwrite (pci_cfgreg.c) require bytes in {1,2,4}, reg aligned to bytes, and reg+bytes <= PCIE_REGMAX+1 β same policy the mech-1 backend pci_cfgenable already enforces.
Verdict
The legacy host-PCI config bridges in sys/bus/pci/x86_64/pci_bus.c:61-75 forward caller-supplied (reg,bytes) with no validation into the PCIe MMIO backend pciereg_cfgread/cfgwrite (pci_cfgreg.c:315-365), which checks only reg>4095 and never reg+bytes<=4096 nor alignment (the port-I/O backend pci_cfgenable at pci_cfgreg.c:187-193 checks both). A 2-byte access at reg=0xfff on (bus=pcie_maxbus,slot=31,func=7) reads/writes 1-3 bytes past the 256MB pmap_mapdev_uncacheable MMCFG kernel_map allocation. Proven live on the QEMU guest with a KLD that makes the same legal pcie_cfgregopen() call ACPI MCFG handling makes at boot (acpi.c:1669; the i440FX guest boots with the MMIO backend dormant): control read at reg=0xffc/4 succeeds, read at reg=0xfff/2 panics instantly (Fatal trap 12, supervisor read, fault VA exactly pcie_base+0x10000000, stopped at the inlined volatile uint16_t load in pci_docfgregread+0xb4). Root-gated: the only attacker-regulated path, /dev/pci PCIOCREAD/PCIOCWRITE, validates width/alignment but no upper bound on pi_reg, yet /dev/pci open requires SYSCAP_RESTRICTEDROOT (pci_user.c:82). Impact ceiling for the (already-privileged) trigger: reliable kernel panic plus a 1-3 byte OOB write into the kernel_map neighbor of the MMCFG window. No unprivileged chain exists. fix.diff (carry width/alignment/end-bound into the MMIO backend) validated end-to-end: patched kernel rejects all OOB accesses (0xffffffff / silent no-op), control read unaffected, guest stays up.
No comments yet.