DF-2304 / verdict.json
{ "finding_id": "DF-2304", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "Forced-branch mechanical reproduction on the verbatim-interposition harness: the real fwohci_pci_attach chain (bus_generic_probe -> firewire identify -> BUS_ADD_CHILD -> the verbatim fwohci_pci_add_child -> real device_add_child) ran with device_probe_and_attach interposed to fail exactly at fwohci_pci.c:442. The stock error path called fwohci_pci_detach(parent), whose device_delete_child at fwohci_pci.c:352 freed the child (kobj_delete/kfree, subr_bus.c:1306), and add_child then called device_delete_child(parent, child) again at fwohci_pci.c:447 on the same freed pointer. The INVARIANTS kernel panicked inside the second delete: panic Bad link elm prev->next != elm, backtrace fwohci_pci_add_child+0xfd <- bus_generic_identify <- bus_generic_probe, 553 MB dump. On a non-INVARIANTS kernel the stale-pointer TAILQ_REMOVEs (parent children list and the global bus_data_devices list) and the kobj_delete/kfree double free execute silently. REACHABILITY IS HONESTLY LATENT: the branch requires device_probe_and_attach of the firewire child to fail, but the child is created by the firewire drivers own identify method, firewire_probe returns 0 unconditionally (firewire.c:313-317), firewire_attach has no failure return (firewire.c:383-416), and the underlying allocations are M_INTWAIT. The branch runs at configuration time only (boot/kldload); no unprivileged or remote input reaches it. Not privesc-class today; it is a real latent heap double-free that fires if firewire attach ever grows a failure path.", "exploit_chain": "", "evidence": "panic.txt: the two logged deletes share child=0xfffff8011769c6a0 (FROM fwohci_pci.c:352 then FROM fwohci_pci.c:447) followed by panic Bad link elm and the fwohci_pci_add_child backtrace. run.log: full trigger output ending at the panic (ssh died). run_fixed.log: fixed kernel, same forced branch, exactly one delete (line 354 post-patch), no panic, resources balanced.", "kernel_refs": [ "sys/bus/firewire/fwohci_pci.c:446-447", "sys/bus/firewire/fwohci_pci.c:439", "sys/bus/firewire/fwohci_pci.c:351-353", "sys/kern/subr_bus.c:1284-1310", "sys/kern/subr_bus.c:1306", "sys/kern/subr_bus.c:2793", "sys/bus/firewire/firewire.c:313-317", "sys/bus/firewire/firewire.c:383-416" ], "poc_changes": "No seeded PoC existed; the rig was built fresh for this batch, extending the DF-2300 verbatim-interposition design to fwohci_pci.c (second TU: fwohci_pci.c TU-included with three call-site interposition macros; fabricated kobj PCI-bus parent + fake fwohci device shell carrying the real fwohci devclass; emulated OHCI regs/PHY reused from fwohci_harness.c). Fixed during bring-up: OHCI_VERSION value (mver bits 16-23 must be 1-9), bootverbose extern (already declared), sys/device.h include, BSD sed -i syntax, and softc resource-pointer preservation between rig runs.", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #2: Sun Oct 18 14:03:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/FWO x86_64", "runtime_sec": 2400, "guest_dirty": 1, "build_cmd": "sh /root/fwo/fwpci_prep.sh && sh /root/fwo/build_kernel.sh (nativekernel KERNCONF=FWO) ; cc -O -Wall -o fwpcictl fwpcictl.c", "run_cmd": "sh /root/fwo/run2304.sh (fwpcictl attach dpafail as root)", "code_hash": "fe15a597dd10cab0a921f14900768d16933a756bac1fad238730ce4d93396ac5", "notes": "Guest had no OHCI hardware: the PCI side is fabricated kobj machinery, the audited code is byte-identical stock. The panic is the INVARIANTS catch of the stale link pointers; the double kfree behind it is subr_bus.c:1306 via the second kobj_delete. Fix validation on the same rig: hunk 3 of fix.diff removes the second delete; A/B captured in run_fixed.log. Reachability quantified in VERDICT.md.", "recommended_fix": "Remove the device_delete_child(parent, child) call at fwohci_pci.c:447; fwohci_pci_detach at :446 already deletes sc->fc.bdev (the same child) and NULLs it." } |