DF-5776 / verdict.json
{ "finding_id": "DF-5776", "status": "reproduced", "reproduced": true, "impact": "none", "confidence": "certain", "verdict": "The pass-2 lead is confirmed live: PCIOCREAD/PCIOCWRITE (sys/bus/pci/pci_user.c:602-614) validate only pi_reg >= 0 and width alignment \u2014 there is no upper bound, so any aligned offset up to 0x7FFFFFFC is accepted and dispatched to PCIB_READ/WRITE_CONFIG (:633-639, :651-657). The MD layer is the only net: type-1 pci_cfgenable (x86_64/pci_cfgreg.c:187-198) rejects reg > 255 and ECAM rejects reg > 4095, so no OOB access exists \u2014 but pci_user.c:658 then returns error=0, laundering the dropped access into fabricated success (dropped reads return pi_data=0xffffffff, the cfgreg -1 sentinel, indistinguishable from a vacant-function read; dropped writes report rc=0 and never landed \u2014 live-proven on the guest as root with reg=256/4096/0x7FFFFFFC, readback and device config unchanged, guest healthy). PCIOCGETBAR additionally passes pbi_reg to pci_read_config(...,4) with zero validation of any kind (:711), contained in practice only by the resource_list_find rid match. The whole surface is root-gated (pci_user.c:82-88 + FWRITE/securelevel gate :324-330; unpriv live-probes EACCES/EPERM), so impact is privileged-tooling honesty/robustness, not unpriv memory safety: severity Low, CWE-20. fix.diff (ioctl-layer bound pi_reg > PCIE_REGMAX + 1 - pi_width via overflow-safe subtraction, plus pbi_reg sign/bound/alignment) was built into kernel #1 and validated: reg > 4095 now returns EINVAL while in-range registers, ECAM extended range, GETCONF pagination and GETBAR behavior are unchanged.", "exploit_chain": "", "evidence": "run.log + run.baseline-freshboot.out: reg 0x1000 (PCIE_REGMAX+1) PCIOCREAD reg=4096 w=4 rc=0 errno=0 data=0xffffffff and write reg 0x7FFFFFFC PCIOCWRITE ... rc=0 errno=0 on kernel #0; run.patched.out: same lines with rc=-1 errno=22 on the fix.diff-patched kernel #1; fix_build.log full untrimmed kernel build (build_rc=0); VERDICT.md for the full path:line trace and negative results.", "kernel_refs": [ "sys/bus/pci/pci_user.c:602", "sys/bus/pci/pci_user.c:610", "sys/bus/pci/pci_user.c:633", "sys/bus/pci/pci_user.c:651", "sys/bus/pci/pci_user.c:658", "sys/bus/pci/pci_user.c:711", "sys/bus/pci/x86_64/pci_bus.c:71", "sys/bus/pci/x86_64/pci_cfgreg.c:187", "sys/bus/pci/x86_64/pci_cfgreg.c:194", "sys/bus/pci/x86_64/pci_cfgreg.c:212", "sys/bus/pci/x86_64/pci_cfgreg.c:237", "sys/bus/pci/x86_64/pci_cfgreg.c:322", "sys/bus/pci/x86_64/pci_cfgreg.c:348" ], "poc_changes": "No seed code existed (the lead was a task_log note). Probe authored fresh: fixed struct/API usage against sys/sys/pciio.h, chose virtio NIC 0:3:0 as target (not the root disk), kept every destructive probe out-of-range by construction so the type-1 drop makes them harmless, and added GETCONF pagination + GETBAR negative probes and an unpriv open-gate control.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 1560, "guest_dirty": 0, "build_cmd": "cc -O -Wall -o /root/pci5776 /root/pci5776.c", "run_cmd": "/root/pci5776", "code_hash": "61793eeb8eff2e1178ab77dad0dda32f64a936e3559b3e93b1a032549a79806f", "notes": "Guest was reset with-src after the fix-validation cycle (guest_dirty=0). Type-1 config mechanism confirmed (i440FX, no mmapped dmesg lines); securelevel=-1. Residual by design: on type-1-only machines reg in [256,4095] remains a silent no-op-with-success because that range is the real ECAM extended-config space and mechanism capability is MD-private; full closure needs failure propagation from the cfgreg layer, which is DF-5570s file (kept separate). Known findings not re-reported: DF-1067, DF-1068, DF-2326. Probe section-8 comment expect LAST_DEVICE=0 is wrong: status=1 is LIST_CHANGED (stale generation check at pci_user.c:354-359 fires first) \u2014 correct kernel behavior.", "recommended_fix": "At the ioctl layer, reject undecodable register offsets instead of silently succeeding: pi_reg > PCIE_REGMAX + 1 - pi_width -> EINVAL (overflow-safe subtraction form), and validate PCIOCGETBAR pbi_reg (>= 0, <= PCIE_REGMAX, 4-aligned); see findings/poc/DF-5776/fix.diff.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Sun Oct 11 19:51:10 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "fix_verdict": "fixed: vm.sh reset with-src, patch -p0 applied to guest /usr/src (both hunks succeeded), make nativekernel KERNCONF=X86_64_GENERIC + installkernel (fix_build.log build_rc=0), reboot into kernel #1, exact same probe: every previously-silent out-of-range read/write (reg 4096, 65536, 0x7FFFFFFC) now returns rc=-1 errno=22 EINVAL, while reg 0x00 reads 0x10001af4, reg 256..4095 stays accepted for ECAM compatibility, GETCONF pagination and GETBAR outputs are byte-identical, and the guest remains healthy (run.patched.out).", "fix_evidence": "run.patched.out lines PCIOCREAD reg=4096 w=4 rc=-1 errno=22, PCIOCWRITE reg=2147483644 w=4 val=0xdeadbeef rc=-1 errno=22 vs run.baseline-freshboot.out same lines rc=0 errno=0 on kernel #0; fix_build.log tail >>> Kernel build for X86_64_GENERIC completed ... install_rc=0." } |