DF-4369 / verdict.json
{ "finding_id": "DF-4369", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "likely", "verdict": "Real race, hardware-gated: hpt_timeout() (hpt27xx_osm_bsd.c:411-419, softclock, no lock) dereferences pCmd->priv after the completion path os_cmddone() (:421-466) callout_stop()s the timer and frees both the OS_CMDEXT (:463) and the PCOMMAND (:464). DragonFly callout_stop() explicitly does not interlock against a callout that is in-progress (sys/kern/kern_timeout.c:1087-1088), so a timeout firing on another CPU at the completion instant reads a freed/recycled command: if the block was recycled as a flush command priv points at a kernel stack variable (&done, :303) and ldm_reset_vbus() receives a wild pointer -> fatal trap (panic/DoS, no write primitive). Requires a RocketRAID 27xx controller; the audit guest has none (pciconf shows no vendor 0x1103 device; dmesg: hpt27xx: no controller detected.), so the race could not be executed there \u2014 classification is not_reproduced with the static proof chain in the evidence pack carrying the finding.", "exploit_chain": "", "evidence": "race_proof.txt (arm/completion/timeout interleaving table with path:line); VERDICT.md; run.log line hpt27xx: no controller detected. proving the guest cannot reach the code; fix.diff (lock+revalidate fix)", "kernel_refs": [ "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:411", "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:414", "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:418", "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:421", "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:428", "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:463", "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:464", "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:530", "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:730", "sys/dev/raid/hpt27xx/hpt27xx_osm_bsd.c:303", "sys/dev/raid/hpt27xx/os_bsd.h:190", "sys/dev/raid/hpt27xx/him.h:417", "sys/kern/kern_timeout.c:1087" ], "poc_changes": "No seeded PoC existed. Wrote an environment-probe script (verify_env.sh/run.sh) and a disassembly-verified negative-analysis appendix (race_proof.txt) covering the blobs GET_LOGICAL_DEVICES clamping (min(total,maxcount) at .text 0x77330/0x7f1c0), which killed a candidate stack-overflow theory.", "attempts": 1, "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": 95, "guest_dirty": 0, "build_cmd": "nothing to build (driver compiled into X86_64_GENERIC; no hardware to exercise)", "run_cmd": "dfbsd-qemu/vm.sh run_root pciconf -l; kldstat -v | grep hpt27xx; ls /dev/hpt27xx; dmesg | grep hpt (== ./run.sh)", "code_hash": "4e46e66352d1856483b46c3fefc5f28b4b288e0c722f9f2f1ba77b64c410f094", "notes": "Hardware-gated finding (RocketRAID 27xx, vendor 0x1103): guest PCI bus has only i440FX/PIIX4/bochs-VGA/virtio, driver compiled in but never attaches (hpt_final_init:1021 no controller detected.), no /dev/hpt27xx, no CAM sim \u2014 the racing paths are unreachable on this guest. Static impact on hardware-equipped SMP systems: kernel panic (DoS) when a command completes in the same instant its 20s HPT_OSM_TIMEOUT expires and the freed PCOMMAND is recycled; no write primitive, no escalation. Field symptom: sporadic fatal trap in hpt_timeout on storage boxes with a stalling disk. Same-file negatives proven during this pass are documented in race_proof.txt (count-shuffle clamped by the binary lib; cmdext NULL impossible via devq accounting; flush/ioctl sleep loops lost-wakeup-safe; KdPrint/HPT_ASSERT compile to no-ops in release; ioctl kmalloc sizes root-only-0600 gated).", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff authored (hpt_timeout takes the recursive vbus lock and re-validates pCmd->priv==ext and pCmd->done==os_cmddone before resetting). Cannot be build-validated on the audit guest: no HighPoint controller, and reproducing the baseline race itself requires one. callout_stop_sync() was deliberately avoided \u2014 os_cmddone runs in interrupt context while hpt_timeout needs the same vbus lock, so a synchronizing stop would deadlock.", "fix_evidence": "fix.diff; VERDICT.md Fix section", "recommended_fix": "In hpt_timeout(), take the (recursive) vbus lock and re-validate that pCmd still belongs to this completion path (pCmd->done == os_cmddone && pCmd->priv == ext) before calling ldm_reset_vbus(), so a timeout racing os_cmddones callout_stop/free cannot dereference a recycled PCOMMAND." } |