⬢ DragonFlyBSD Kernel Audit
DF-4614 / verdict.json
← back to finding ↓ download raw
{
  "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)."
}