DF-4971 / verdict.json
{ "finding_id": "DF-4971", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Static trace complete; kernel path not instantiable on the audit guest (no LSI 0x1000 PCI device, QEMU has no MegaRAID model; mfip exists only as a child of mfi, mfi.c:737). mfip_detach (mfi_cam.c:142-159) frees the sim without draining mfisc->mfi_cam_ccbq and without clearing mfisc->mfi_cam_start (set at mfi_cam.c:116). Queued ccbs (parked whenever the controller had no free command, mfi_cam.c:268) are invisible to xpt_bus_deregister (cam_xpt.c:4383 XXX) and unrecoverable through the sim (no XPT_ABORT case, mfi_cam.c:246 default CAM_REQ_INVALID): permanent hang + ccb leak. The still-live parent then dispatches them through the stale callback: mfi_pci_detach deletes children first (mfi_pci.c:328) and mfi_intr->mfi_startio (mfi.c:1245->2132-2133) runs before mfi_free, popping the leaked ccbs via mfip_start whose sim is freed; on kldunload the callback is unmapped module text; the late xpt_done (mfi_cam.c:378) completes the ccb against deregistered CAM state (path->bus->sim==&cam_dead_sim per cam_xpt.c:4456) with periphs destroyed - UAF-shaped. Guest-compiled choreography mirror (CAM refcount semantics transcribed from cam_sim.c/cam_xpt.c) shows all violations in stock mode and zero with the drain+clear fix; fix.diff builds into mfip.ko on the guest with -Werror.", "exploit_chain": "", "evidence": "run.log: post-detach: ... queued-nonempty=YES mfi_cam_start=STALE, V1 VIOLATED: 3 ccb(s) leaked, V2 VIOLATED, [xpt_done] UAF: ccb 1 completed after its periph was destroyed (dispatch sim=... dead_sim), [mfip_start] UAF: call into unmapped module text after kldunload, BUGGY: 4 violation(s) exit=1 vs FIXED: 0 violation(s) exit=0; fix_build.log shows patched mfip.ko linking with -Werror and source restore", "kernel_refs": [ "sys/dev/raid/mfi/mfi_cam.c:142", "sys/dev/raid/mfi/mfi_cam.c:116", "sys/dev/raid/mfi/mfi_cam.c:242", "sys/dev/raid/mfi/mfi_cam.c:246", "sys/dev/raid/mfi/mfi_cam.c:268", "sys/dev/raid/mfi/mfi_cam.c:378", "sys/dev/raid/mfi/mfi.c:1245", "sys/dev/raid/mfi/mfi.c:2132", "sys/dev/raid/mfi/mfi_pci.c:328", "sys/bus/cam/cam_xpt.c:4383", "sys/bus/cam/cam_xpt.c:4456" ], "poc_changes": "mirror authored fresh (no seed); DragonFly userland queue.h needs plain TAILQ field names (kernel camq_entry union uses sim_links.tqe); post-detach free-command replenishment added to model mfi_intr completing a disk bio before mfi_startio", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 x86_64 (X86_64_GENERIC)", "runtime_sec": 55, "guest_dirty": 0, "build_cmd": "cc -O -Wall -o mfip_detach_mirror mfip_detach_mirror.c", "run_cmd": "./mfip_detach_mirror buggy; ./mfip_detach_mirror fixed", "code_hash": "575b68531fa38d1aacabeed0651c1930b945270d360727ee64b03cd392788b89", "notes": "Hardware-gated (LSI MegaRAID). Same family as mrsas_cam DF-4438 and aac_cam DF-4717-4720; verdict follows the DF-4438 precedent (static trace + guest mirror). Severity driven by: permanent unkillable hang + ccb leak at every detach with queued ccbs, plus stale-callback dispatch (freed sim / unmapped text / deregistered periph state). No controlled write demonstrated; no unpriv user->root chain.", "recommended_fix": "In mfip_detach, under mfi_io_lock: drain mfi_cam_ccbq completing each ccb with CAM_REQ_ABORTED via xpt_done, set sc->mfi_sc->mfi_cam_start = NULL, then xpt_bus_deregister + cam_sim_free", "fix_status": "inconclusive", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "Kernel-level fix validation impossible (no MegaRAID HBA on guest). Validated at two levels: (1) the mirrors fixed mfip_detach drains the queue and clears the callback - 0 violations where stock shows 4 (run.log); (2) fix.diff applies cleanly to the guest /usr/src copy and mfip.ko builds with -Werror (fix_build.log). Guest sources restored after the build.", "fix_evidence": "run.log FIXED section (FIXED: 0 violation(s) exit=0); fix_build.log (Hunk #1/#2 succeeded, cc -Werror, ld -r mfip.ko, RESTORED_CLEAN)" } |