DF-4014 / verdict.json
{ "finding_id": "DF-4014", "status": "inconclusive", "reproduced": 0, "impact": "none", "confidence": "certain", "verdict": "Source-certain use-after-free, not live-reproducible on this guest (no LSI Fusion-MPT HBA, /dev/mps* absent, so the wait/timeout path cannot be entered). mps_wait_command() (mps.c:2118-2132) returns ETIMEDOUT WITHOUT aborting the command -- the chip still owns the request/reply frames and the SGL over cm->cm_data. mps_user_pass_thru() then immediately kfrees cm->cm_data and mps_free_command()s the slot back to sc->req_list (mps_user.c:977-982 -> :1039-1046; mpsvar.h:514-540), handing both to new users while the outstanding SMID still refers to them: a late completion mutates a possibly-reused command (cross-command corruption) and the chips residual DMA lands in freed memory. The task-management branch has the same defect (:838-844 -> mpssas_free_tm at :863). This is the DF-4010 (mpr) site with the opposite and more dangerous defect: mpr LEAKS the buffer across its reinit; mps FREES while in flight. The driver itself documents the correct policy for config pages (mps_user.c:240-245: Leave the request... well just get into trouble freeing it now).", "exploit_chain": "operator-group user -> MPTIOCTL_PASS_THRU with a request the firmware holds >30 s -> ETIMEDOUT -> immediate kfree(cm->cm_data) + mps_free_command(cm) while the chip still DMAs per the SGL and the SMID stays outstanding -> freed chunk reused by slab grooming -> device DMA / late-completion writes corrupt the replacement object -> on real hardware: kernel heap corruption toward uid=0 (characterization needs the HBA).", "evidence": "VERDICT.md (line-complete trace), fix.diff (abandon-on-timeout routing both branches to Ret, mirroring the drivers own config-page policy)", "kernel_refs": [ "sys/dev/raid/mps/mps_user.c:975", "sys/dev/raid/mps/mps_user.c:977", "sys/dev/raid/mps/mps_user.c:1039", "sys/dev/raid/mps/mps_user.c:838", "sys/dev/raid/mps/mps_user.c:863", "sys/dev/raid/mps/mps.c:2118", "sys/dev/raid/mps/mpsvar.h:514" ], "poc_changes": "no kernel replica built: the essence of the bug is the devices late DMA into freed memory, which a stubbed replica cannot demonstrate beyond the source trace", "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": 120, "guest_dirty": 0, "build_cmd": "patch -p1 --dry-run < fix.diff (applies cleanly to sys/dev/raid/mps/mps_user.c)", "run_cmd": "n/a (HW-gated; see VERDICT.md source trace)", "code_hash": "", "notes": "companion leak finding DF-4015 (mps_user_command goto Ret skips mps_free_command) is the same policy question in the opposite direction; both are resolved upstream by a proper command-abort on timeout.", "recommended_fix": "On wait-timeout, abandon the command instead of freeing it (fix.diff), matching the drivers documented config-page policy; long term add a real mps abort/reinit path.", "fix_status": "not_testable", "fix_baseline_reproduced": 0, "fix_patched_reproduced": 0, "fix_verdict": "fix.diff dry-run applies cleanly; combined nativekernel build (DF-4012+4013+4014 diffs together) rc=0 under -Werror, installed and booted (kern.version DragonFly 6.5-DEVELOPMENT #1: Thu Sep 17 14:24:13 UTC 2026). Live before/after comparison is impossible on this guest (no mps HBA to drive the timeout path), hence not_testable rather than fixed.", "fix_evidence": "fix.diff; fix_kernel_build.log", "fix_kernel_uname": "DragonFly 6.5-DEVELOPMENT #1: Thu Sep 17 14:24:13 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64 (combined-fix kernel)" } |