DF-4614 / verdict.json
{ "finding_id": "DF-4614", "status": "not_reproduced", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "The ips command-queue lifecycle double-frees. ips_cmdqueue_free (ips.c:139-141) kfrees sc->commandarray unconditionally (outside the used_commands==0 guard, and without NULLing the pointer), and ips_adapter_init ignores the return of the first ips_cmdqueue_init (ips.c:414). When that init fails (bus_dmamem_alloc/map_create/load failure under DMA exhaustion), the internal ips_cmdqueue_free (ips.c:190) already freed the array; adapter init then fails through the IPS_OFFLINE-blocked ffdc path (ips.c:421-425) and its error unwind (ips.c:462 -> ips_adapter_free -> ips.c:533) calls ips_cmdqueue_free a second time: UAF read of command_phys_addr (ips.c:125), possible bus_dmamem_free/bus_dmamap_destroy on garbage pointers (ips.c:127-134), then double kfree (ips.c:140) - kernel heap corruption. Identical shape when the checked second init fails (ips.c:447-451). Trigger C: the unconditional kfree also fires with used_commands != 0, freeing the array under live interrupt handlers during detach, after which ips_adapter_free returns EBUSY leaving the driver on a dangling array. The exact call sequence was demonstrated on the audit guest with doublefree_harness.c (stubbed bus_dma, aborting allocator): [!] DOUBLE FREE: kfree(#2) on 0x800490ec0 already freed at kfree(#1), exit=42 - see run.log. The in-kernel path itself is not_reproduced because the guest has no ServeRAID PCI device (env.txt) and QEMU has no device model for one; the defect trace is fully static and certain.", "exploit_chain": "", "evidence": "run.log (guest-executed harness aborting on the second free of the same pointer: exit=42); build.log (cc on DragonFly 6.5-DEVELOPMENT); VERDICT.md (line-accurate trace of Triggers A/B/C); env.txt (no ServeRAID hardware on guest, driver never attaches)", "kernel_refs": [ "sys/dev/raid/ips/ips.c:116", "sys/dev/raid/ips/ips.c:125", "sys/dev/raid/ips/ips.c:139", "sys/dev/raid/ips/ips.c:414", "sys/dev/raid/ips/ips.c:447", "sys/dev/raid/ips/ips.c:462", "sys/dev/raid/ips/ips.c:533", "sys/dev/raid/ips/ips.c:190" ], "poc_changes": "Harness written fresh for this finding (no seed existed): mirrors ips_cmdqueue_init/ips_cmdqueue_free/ips_adapter_init decision structure with stubbed bus_dma forced to fail at ips.c:167; compiled and run on the DragonFly guest via vm.sh (source transferred with openssl base64 since the guest lacks base64(1)).", "attempts": 2, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 x86_64", "runtime_sec": 45, "guest_dirty": 0, "build_cmd": "cc -O0 -g -o /tmp/df/harness /tmp/df/harness.c (on guest)", "run_cmd": "/tmp/df/harness; echo exit=$? (on guest)", "code_hash": "be655600179e5f801ea0d62a6552473f01a55a18fa69f62f5e60f23dd7f64fa1", "notes": "Fix-validation A/B on a patched kernel is impossible on this guest (device never attaches; forcing it would require a ServeRAID device model QEMU does not have). The harness is a control-flow proof, not an in-kernel execution - framed accordingly. Trigger C (in-flight free) shares the same fix region.", "recommended_fix": "NULL sc->commandarray after kfree and early-return when NULL; refuse to free (EBUSY) while used_commands != 0; honor the return value of the first ips_cmdqueue_init at ips.c:414.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "No hardware to build an A/B kernel run on; fix.diff removes all three trigger paths by construction (NULL-out + guard + checked init), and the harnesss mirrored control flow confirms the guard semantics (free-called-once) when re-run with the same fix applied to the mirror.", "fix_evidence": "fix.diff (ips_cmdqueue_free NULL/guard clauses + sc->commandarray = NULL + checked ips_cmdqueue_init at ips.c:414)." } |