{
  "finding_id": "DF-2323",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "corruption",
  "confidence": "certain",
  "verdict": "Reproduced on a production-config kernel (INVARIANTS off) via the verbatim pci_read_vpd compiled into the DFPCI-family harness kernels: a well-formed VPD-R item completes and shrinks vpd_ros to exactly the entry count with no NULL sentinel, then a VPD register stall drives state=-2 and the cleanup loop at pci.c:1300 walks past the 32-byte allocation, kfree()ing whatever the adjacent heap holds. With controlled adjacency the walk freed a LIVE 64-byte canary (silent on production builds), the freed-live chunk was immediately re-handed by kmalloc(64) to a second owner, and both owners read/wrote the same memory (overlapping live allocations). With a wild pointer planted instead, kfree faulted in pmap_kvtom and panicked. Correction to the claim: a second VPD-R item with a bad checksum does NOT trigger it (case 0x10 allocates a fresh zeroed array, replacing and leaking the shrunk one; proven by the badck2 run) - the actual co-trigger is the state<-2 I/O-error path. On the stock INVARIANTS kernel the claimed corruption is unreachable because krealloc KKASSERTs M_ZERO (kern_slaballoc.c:1187) at the shrink itself - still a device-triggered panic at the same site, captured. fix.diff (count-bounded cleanups + rocnt/wcnt tracking + M_ZERO removal from the 4 kreallocs) A/B validated on both configs: zero out-of-set frees, canary intact, and the stock-config scenario no longer panics.",
  "exploit_chain": "malicious PCIe device with VPD capability serves one well-formed VPD-R item (shrink krealloc drops the NULL sentinel) then stalls the VPD ADDR completion bit -> pci_read_vpd state=-2 -> cleanup loop reads vpd_ros[off].value past the shrunk allocation -> attacker-influenced adjacent 32B-zone chunk supplies the pointer -> kfree(live object) -> chunk immediately re-allocatable while original owner retains it -> overlapping live kernel objects (demonstrated: canary vs kmalloc(64) same address, contents cross-visible); wild pointer variant -> kernel page fault panic",
  "evidence": "panic_stock.txt (stock: krealloc M_ZERO assert at the shrink, trace krealloc<-dfh_run_vpd), rig/groom.log (UNTRACKED kfree of live canary + kmalloc(64)==canary + TWO LIVE OWNERS SAME MEMORY), panic_wild.txt (page fault in pmap_kvtom<-dfh_run_vpd), rig/natural_5runs.log (5 natural runs, adjacency snapshots), rig/badck2.log (claimed trigger refuted: fresh array + 3-block leak), rig/fixed_runs.log (fixed kernel: 0 untracked frees, canary intact), rig/dfpci_post.c dfh_kfree/krealloc trackers",
  "kernel_refs": [
    "sys/bus/pci/pci.c:1223",
    "sys/bus/pci/pci.c:1225",
    "sys/bus/pci/pci.c:1278",
    "sys/bus/pci/pci.c:1280",
    "sys/bus/pci/pci.c:1297",
    "sys/bus/pci/pci.c:1300",
    "sys/bus/pci/pci.c:1117",
    "sys/bus/pci/pci.c:1315",
    "sys/bus/pci/pci.c:2146",
    "sys/kern/kern_slaballoc.c:1187"
  ],
  "poc_changes": "verdict-mode seed replaced by the established verbatim-interposition kernel-compiled harness: extracted pci_read_vpd/vpd_nextbyte/pci_read_vpd_reg byte-for-byte (sha256-gated), fabricated pcicfg device with a VPD register-protocol state machine (completion-bit stall point set by scenario), tracked kmalloc/krealloc/kfree with real-allocator passthrough, adjacency grooming in the 32-byte slab zone; trigger is sysctl debug.dfpcih.cmd; scenario image off-by-one (item length 8 vs 9) fixed after first run",
  "attempts": 9,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Sun Oct 18 19:32:11 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/DFPCIP x86_64 (production-config run; DFPCI/DFPCIPF/DFPCIF runs logged in env.txt)",
  "runtime_sec": 4200,
  "guest_dirty": 0,
  "build_cmd": "sh rig/build.sh DFPCI (guest: make -j6 nativekernel KERNCONF=DFPCI|DFPCIP|DFPCIPF|DFPCIF; see rig/gen.sh, rig/gen_fixed.sh)",
  "run_cmd": "vm.sh run_root \"sysctl debug.dfpcih.cmd=vpd_stock\" (quote the scenario without apostrophes; scenarios: vpd stock, vpd natural, vpd groom, vpd wild, vpd badck2, vpd errfirst, vpd ok); see rig/run_2323_stock.sh rig/run_2323_prod.sh",
  "code_hash": "ce238fc92dcbe657b33d6084dfbc60caa3ace94d56814d639d95ec5c0582105e",
  "notes": "no DB writes by reviewer; impact=corruption chosen over panic because the deterministic (groomed) outcome is silent free-of-live-object + overlapping allocations on production builds; natural runs depended on adjacent heap being zeroed (5/5 benign) which is heap-state luck, not a bound; stock INVARIANTS kernels convert every trigger of this path into the krealloc M_ZERO assert panic instead",
  "recommended_fix": "bound both VPD error-cleanup loops by tracked entry counts (vpd_rocnt/vpd_wcnt maintained at every entry completion and reset per item) and drop the unsupported M_ZERO flag from the four krealloc calls"
}