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

mfi_disk_strategy leaks bios on hw_crit_error / issuepend_done early returns β€” permanent unkillable D-state (DF-2066 systemic twin, this file)

Summary

Two early returns set b_error but no B_ERROR/b_resid/biodone - the issuer sleeps in biowait forever (the entire OCR window or fatal controller fault; root-on-mfid wedges the machine). Sink-level REPRODUCED on stock guest: verbatim pattern in a kld raw-disk wedged a reader in D3/physstr surviving kill -9. Fail-label fix mirrors the 9-lines-up correct pattern.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5012 Β· 12 files
FileTypeDescriptionSize
df5012.c β€” 3.5 KB view raw
df5012_trigger.c β€” 828 B view raw
Makefile β€” 52 B ↓ download
build.sh β€” 150 B view raw
run.sh β€” 154 B view raw
build.log β€” 2.2 KB view raw
run.log β€” 1.7 KB view raw
dmesg.txt β€” 231 B view raw
env.txt β€” 348 B view raw
VERDICT.md β€” 3.5 KB ↓ raw
fix.diff β€” 675 B view raw
verdict.json β€” 3.3 KB view raw
VERDICT.md
↓ download raw

DF-5012 β€” VERDICT

Status: REPRODUCED at sink level on the stock audit guest (the exact completion pattern of mfi_disk.c:266-274 demonstrated live); the mfi-specific entry conditions (hw_crit_error, issuepend_done == 0) are hardware-gated (no MegaRAID controller on this guest) and are unconditional control flow in the source β€” line-traced below.

Impact ceiling: dos β€” permanent unkillable D-state for any issuer with a bio in flight when the controller faults or resets (OCR). Whole-system hang when mfid carries the root filesystem.

Why the bio leaks (source trace)

  1. mfi_disk_strategy() mfi_disk.c:249: bios arrive from the disk layer (dev_dstrategy on the mfid cdev or its slices).
  2. mfi_disk.c:266-269: if (controller->hw_crit_error) { bp->b_error = EBUSY; return (0); } β€” no bp->b_flags |= B_ERROR, no bp->b_resid, no biodone(bio). The caller of a d_strategy owns completion; nobody else holds a reference to this bio. It is leaked into nothing.
  3. mfi_disk.c:271-274: identical for controller->issuepend_done == 0. issuepend_done is a per-controller OCR gate: cleared at mfi_tbolt.c:541 when the reset starts, restored at mfi_tbolt.c:1323 when it finishes β€” the window is the entire OCR (seconds). hw_crit_error is set at mfi_tbolt.c:1222/1288 on fatal PCI/firmware access failure during adapter reset and is never cleared until reattach (mfi.c:388).
  4. The issuer (e.g. physio β†’ biowait) sleeps uninterruptibly forever. Contrast the correct pattern 9 lines up (mfi_disk.c:257-263) and the userspace-facing ioctl gate that does return EBUSY (mfi.c:2901-2904).

Guest run (stock kernel, no config changes)

+ ps -o pid,stat,wchan,command -p 1109
   PID STAT   WCHAN    COMMAND
  1109 D3     physstr  /tmp/poc/df5012_trigger wedge /dev/df50120
+ kill -9 1109
+ ps ...          # after SIGKILL, 2 seconds later
  1109 D3     physstr  ...          # STILL ALIVE
+ /tmp/poc/df5012_trigger poke /dev/df50120   # external completion
poke: pread@1M r=512 errno=0
+ ps ...  ->  GONE

dmesg: df5012: LEAKING bio 0xfffff8004f3dfcc0 (off 2097152) -- b_error=EBUSY, return 0, NO biodone then df5012: poke -- completing leaked bio ....

The wedge process survived SIGKILL and terminated only when the leaked bio was completed externally β€” i.e. the driver-side omission alone is sufficient for the permanent hang. That is precisely the mfi_disk.c:266-274 behavior.

Why not a full hardware trigger

No MegaRAID controller exists on the guest (pciconf -l has no LSI 1000:1960 etc.), and mfi cannot be attached without one. QEMU does ship a megasas device model, but the guest VM is booted from a fixed snapshot with a fixed device set; per the DF-2066 precedent (identical bug, sibling file), the source trace above is the standard of proof for HW-gated driver findings.

Exploit chain

Not applicable (dos class). The primitive is a permanent, unkillable kernel sleep, not memory corruption.

Fix

fix.diff: complete the bio on both paths (B_ERROR + b_resid + biodone) via a fail: label β€” the same pattern the NULL-si_drv1 path already uses. Validated git apply --check against the audit tree; not built as a single-fix kernel (dos-class, HW-gated entry).

Fix status (batch validation)

Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.

Fix verification

not_testable
baseline reproduced→ patch + rebuild →patched clean

fix.diff (git apply --check clean) completes the bio on both paths. Single-fix kernel build not performed: the mfi entry path is hardware-gated on this guest, so a patched kernel could not exercise the trigger; the sink demo establishes the failure mode the fix removes.

fix.diff in this directory; VERDICT.md 'Fix' section
↓ fix.diffper-fix-DF-5012

Confirmed kernel references

Detail

Evidence (decisive lines)

run.log lines '== ps before kill ==' through 'GONE' (PID 1109 D3/physstr survives kill -9, dies only after the poke completes the leaked bio); dmesg.txt 'df5012: LEAKING bio ... NO biodone' + 'poke -- completing leaked bio'; df5012.c:81-92 is the verbatim pattern; VERDICT.md has the full source trace

PoC changes

authored fresh from source analysis (no seed); module self-cleans via a recovery-poke offset so the guest is left clean

Verified recommended fix

On both early-return paths in mfi_disk_strategy(), complete the bio: set B_ERROR, b_resid = b_bcount, and call biodone() (goto fail label), mirroring the NULL-si_drv1 path 9 lines above.

Verdict

Sink-level reproduction on the stock guest kernel (DF-4986 house precedent): a kld module whose d_strategy contains the verbatim completion pattern of mfi_disk_strategy()'s hw_crit_error/issuepend_done early returns (b_error=EBUSY; return 0; no B_ERROR, no b_resid, no biodone) wedged the issuing process in an unkillable D-state (D3/physstr) that survived SIGKILL, and it terminated only when an external completion (biodone) was injected -- exactly the driver-side omission of sys/dev/raid/mfi/mfi_disk.c:266-274. The mfi-specific entry (controller->hw_crit_error set at mfi_tbolt.c:1222/1288; issuepend_done==0 for the whole OCR window, mfi_tbolt.c:541..1323) is hardware-gated on this guest (no MegaRAID controller) and is unconditional control flow in the source. Impact ceiling: dos - permanent unkillable hang for any issuer with a bio in flight when the controller faults or resets; whole-system wedge when mfid carries root.