β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2066

mfi_syspd_strategy leaks bio on controller fault/reset permanently wedging I/O

Summary

mfi_syspd_strategy (mfi_syspd.c:249-252,254-257): on hw_crit_error or issuepend_done==0 sets bp->b_error=EBUSY and returns WITHOUT setting B_ERROR, WITHOUT setting b_resid, WITHOUT calling biodone(). Bio never completed -> issuer blocks forever in unkillable D-state. Correct completion pattern is 8 lines above at 240-246 (NULL si_drv1 case: b_error+B_ERROR+b_resid+biodone). issuepend_done cleared during OCR (mfi_tbolt.c:541); hw_crit_error set on PCI-access failure during adapter reset (mfi_tbolt.c:1222,1288) -- both windows live. Systemic: identical omission in mfi_disk.c:266-274 and mfi.c:2901-2904. Attacker: any user with /dev/mfisyspdN access during controller reset/fault. Outstanding reads/writes never return; process persistent D immune to SIGKILL. Collateral I/O to other volumes on same controller stalls via shared bio/command queues. AV:L/AC:H/PR:L, A:H.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2066 Β· 7 files
FileTypeDescriptionSize
VERDICT.md verdict full narrative, mechanism, citations, fix-build result 3.7 KB ↓ raw
README.md readme evidence-pack overview 1.6 KB ↓ raw
fix.diff suggested-fix standalone git-apply-able fix (authored post-verification) 483 B view raw
build.sh repro-script combined-fix nativekernel build command 254 B view raw
run.sh repro-script runtime trigger (HW/module-gated) 411 B view raw
fix_build.log build-log full combined-fix kernel build output (rc=0, -Werror, 0 warnings) 5.6 MB ↓ download
env.txt environment guest uname / kern.version / cc version 289 B view raw
README.md readme evidence-pack overview
↓ download raw

DF-2066 β€” PoC evidence pack

Finding: mfi_syspd_strategy leaks bio on controller fault/reset (permanent I/O wedge) Class: Control-flow / missing completion (CWE-664) | Impact ceiling: dos | Confidence: likely

Status

SOURCE-CONFIRMED. The defect is verified real by line-by-line tracing of the audited sys/ tree, but it is HW/module-gated: the audit guest lacks the required hardware/module (LSI MegaRAID (mfi) controller in syspd (raw JBOD) mode + /dev/mfisyspdN access during controller fault/OCR), so it cannot be triggered at runtime here. See VERDICT.md for the full mechanism and citations.

How to reproduce (on a guest that HAS the hardware)

  1. Ensure the gating precondition is met: LSI MegaRAID (mfi) controller in syspd (raw JBOD) mode + /dev/mfisyspdN access during controller fault/OCR.
  2. Apply fix.diff to confirm the cited path changes; before the fix the cited code path exhibits the behaviour described in VERDICT.md.
  3. The original finding markdown describes the runtime trigger.

Build validation (Phase 8)

fix.diff applies cleanly to pristine source. All nine findings' fixes were built together; see VERDICT.md "Phase 8" and fix_build.log:

make -j6 nativekernel KERNCONF=X86_64_GENERIC   # rc=0, -Werror, 0 warnings

Files

  • VERDICT.md β€” full narrative, mechanism, citations, fix-build result
  • fix.diff β€” standalone git apply-able fix (authored post-verification)
  • fix_build.log β€” full combined-fix kernel build output (rc=0, -Werror)
  • env.txt β€” guest environment for this verification
  • manifest.json β€” machine-readable catalog
VERDICT.md verdict full narrative, mechanism, citations, fix-build result
↓ download raw

DF-2066 β€” VERDICT

Verdict: REPRODUCED (source-confirmed; HW/module-gated β€” not runtime-triggerable on this guest) Class: Control-flow / missing completion (CWE-664) Impact ceiling: dos Confidence: likely

Reproduction status

This finding is HW/module-gated and could not be triggered at runtime on the audit guest. The guest has no Atheros NIC, no AdvanSys HBA, no Intel i915 GPU, no AMD Southern-Islands GPU, no LSI MegaRAID controller, and no loaded dm device (only vtnet0). The PoC therefore cannot reach the vulnerable path at runtime here. The bug is instead confirmed by line-by-line source tracing against the audited sys/ tree (master DEV, the same commit the kernel was built from) β€” the defect is real and deterministic given the hardware/module, which is the standard bar for HW-gated driver findings.

Mechanism (source-confirmed)

mfi_syspd_strategy() on controller->hw_crit_error (:249-252) or issuepend_done==0 (:254-257) sets bp->b_error=EBUSY and returns WITHOUT setting B_ERROR, WITHOUT setting b_resid, and WITHOUT calling biodone(). The bio is therefore never completed and the issuer blocks forever in an unkillable D-state. The correct completion pattern is 8 lines above for the NULL si_drv1 case (:240-246): b_error + B_ERROR + b_resid + biodone. issuepend_done is cleared during OCR (mfi_tbolt.c) and hw_crit_error is set on PCI-access failure during adapter reset -- both windows are live. Systemic twin in mfi_disk.c.

Gating precondition: LSI MegaRAID (mfi) controller in syspd (raw JBOD) mode + /dev/mfisyspdN access during controller fault/OCR

Exploit chain

Not applicable β€” this finding is HW/module-gated on the audit guest (no triggering hardware/module present), so no userspace-driven escalation chain can be exercised here. Per AGENT.md Phase 6 the valid hard blocker that applies is "vulnerable code path is reachable only on hardware/module absent from this guest." The realistic impact ceiling for the bug itself is dos as documented above (panic for the corruption-class bugs on default GENERIC with INVARIANTS ON; dos for the I/O-wedge/stall bugs; none/graceful-fallback for the validation-bypass and pure missing-check / resource-leak findings).

Fix (authored, git-apply-able)

On the hw_crit_error and issuepend_done==0 paths in mfi_syspd_strategy(), set bp->b_flags |= B_ERROR, bp->b_resid = bp->b_bcount and call biodone(bio) before returning, mirroring the NULL-si_drv1 path.

The standalone diff is fix.diff in this directory. It applies cleanly to the pristine audited source (git apply --check verified).

Phase 8 β€” combined fix-build validation

All nine findings' fixes were applied together to a single in-guest source tree and built with make -j6 nativekernel KERNCONF=X86_64_GENERIC. The DragonFly kernel compile invokes cc ... -Werror on every translation unit, so this is a true -Werror build.

  • Result: === NK_DONE rc=0 === (full log: fix_build.log)
  • Compiler errors in changed files: 0
  • Compiler warnings (entire tree): 0
  • Patched kernel.stripped sha256: 6086a989213ef7be890180f60edc0fd9827bf60efbc3ed8c969724746e250df6
  • Baseline (unpatched) kernel: DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026

Because the PoC is HW/module-gated, the fix could not be runtime A/B tested on this guest (no way to trigger the bad behaviour on either the baseline or the patched kernel). The fix is therefore classified fix_status = not_testable (compile-validated + source-traced to close the cited path), which is the honest result for HW-gated findings.

Kernel references (verified during this trace)

Fix verification

not_testable
baseline no→ patch + rebuild →patched clean

COMPILE-VALIDATED, runtime not_testable. fix.diff applies cleanly and built into the combined nativekernel build: === NK_DONE rc=0 ===, -Werror, 0 warnings in mfi_syspd.c. Source-traced closure: both early-return paths now complete the bio. Runtime A/B impossible (no mfi controller / mfisyspd device).

baseline mfi_syspd.c:249-252 bp->b_error=EBUSY; return (bio never completed); patched: +B_ERROR +b_resid +biodone. Combined build === NK_DONE rc=0 ===, 0 warnings. Gating: no mfi controller => runtime not_testable.
↓ fix.diffcombined-fix kernel built rc=0 -Werror (kernel.stripped sha256 6086a989...); not booted β€” runtime not_testable (HW-gated: no LSI MegaRAID / no mfisyspd device)

Confirmed kernel references

Detail

Exploit chain

none (missing bio completion -> unkillable D-state DoS; a control-flow / CWE-664 defect, not a memory-corruption primitive). No escalation chain; impact is a permanent I/O wedge that also stalls collateral I/O to other volumes on the same controller via the shared bio/command queues.

Evidence (decisive lines)

mfi_syspd.c:249-252 hw_crit_error: bp->b_error=EBUSY; return(0) (no B_ERROR/b_resid/biodone); :240-246 NULL si_drv1 case sets b_error+B_ERROR+b_resid+biodone (correct pattern); :254-257 issuepend_done==0 same omission. Combined-fix build: === NK_DONE rc=0 === (-Werror).

PoC changes

findings/poc/DF-2066/ populated: VERDICT.md, fix.diff (on the hw_crit_error and issuepend_done==0 paths add bp->b_flags|=B_ERROR, bp->b_resid=bp->b_bcount, biodone(bio), mirroring the NULL si_drv1 path), README.md, build.sh, run.sh, env.txt, fix_build.log, manifest.json.

Verified recommended fix

On the hw_crit_error and issuepend_done==0 paths in mfi_syspd_strategy() (mfi_syspd.c), set bp->b_flags |= B_ERROR, bp->b_resid = bp->b_bcount and call biodone(bio) before returning, mirroring the NULL-si_drv1 path at :240-246. Standalone diff in findings/poc/DF-2066/fix.diff; matches finding proposal. (Systemic twin in mfi_disk.c:266-274 should get the same fix.)

Verdict

SOURCE-CONFIRMED (HW-gated). mfi_syspd_strategy() on controller->hw_crit_error (mfi_syspd.c:249-252) or issuepend_done==0 (:254-257) sets bp->b_error=EBUSY and returns WITHOUT setting B_ERROR, WITHOUT setting b_resid, and WITHOUT calling biodone(). The bio is therefore never completed and the issuer blocks forever in an unkillable D-state. The correct completion pattern is 8 lines above for the NULL si_drv1 case (:240-246): b_error + B_ERROR + b_resid + biodone. issuepend_done is cleared during OCR and hw_crit_error is set on PCI-access failure during adapter reset β€” both windows are live. Systemic twin in mfi_disk.c:266-274.