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

mfi_syspd_dump acknowledges every crash-dump chunk the firmware rejected β€” polled transport returns 0 for any firmware cmd_status, so a dead/removed system PD 'successfully' dumps an empty core (DF-5027 shape)

Summary

The polled mfi_send_frame returns 0 for ANY non-timeout firmware status and the dump path never inspects cmd_status/scsi_status afterwards (contrast mfi_bio_complete which checks both) - a faulted PD completes-but-rejects every WRITE_10 while do_dump records chunk success; savecore believes an empty core. Identical class to DF-5027 (ips); the mfi_disk LD twin shares the omission. cmd_status re-check fix compile-validated.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5050 Β· 6 files
FileTypeDescriptionSize
README.md β€” 1.7 KB ↓ raw
VERDICT.md β€” 3.3 KB ↓ raw
env.txt β€” 394 B view raw
fix.diff β€” 1.3 KB view raw
manifest.json β€” 969 B view raw
verdict.json β€” 2.6 KB view raw

DF-5050 β€” mfi_syspd_dump acknowledges dump chunks the firmware rejected

What this is

mfi_syspd_dump (sys/dev/raid/mfi/mfi_syspd.c:287-309) forwards crash-dump writes to mfi_dump_syspd_blocks (mfi.c:2439-2484) and returns its error. But the polled transport (mfi_send_frame, mfi.c:2273-2323) returns 0 for any firmware completion status β€” it only fails on timeout (cmd_status == MFI_STAT_INVALID_STATUS, mfi.c:2316-2320). Neither mfi_dump_syspd_blocks nor mfi_syspd_dump ever inspects pass->header.cmd_status / scsi_status afterwards.

So when the firmware completes-but-rejects every WRITE_10 (dead, removed or faulted system PD β€” exactly the state you are likely crash-dumping through), every 64K chunk of the "dump" is reported success without a byte written. do_dump/savecore then record and restore an empty/corrupt core dump. Same shape as DF-5027 (ips(4)), here on the mfi syspd path; the LD twin (mfi_dump_blocks, mfi.c:2437ff) shares the omission and belongs to the mfi_disk family follow-up.

Reproduce

No userland trigger exists β€” the sink is the kernel crash-dump path. On hardware: configure dumpdev=/dev/mfisyspdN, fault/remove the PD, panic the system β†’ dump "succeeds", savecore produces a garbage/missing core. On this QEMU guest there is no MegaRAID controller at all (env.txt), so the path is unreachable β€” the source chain is deterministic and cited in VERDICT.md.

Fix

fix.diff β€” after the DMA unload in mfi_dump_syspd_blocks, check pass->header.cmd_status != MFI_STAT_OK || scsi_status != 0 and return EIO so do_dump aborts instead of recording fake success. Compile-validated on stock X86_64_GENERIC (make nativekernel clean).

VERDICT.md
↓ download raw

VERDICT β€” DF-5050

Status: not_reproduced (hardware-gated β€” no MegaRAID controller on the audit guest; the sink is the kernel crash-dump path, which additionally requires a panic). Deterministic source chain. Fix compile-validated. Confidence in the bug: certain (every link is a direct path:line read; no race, no speculation).

Source chain

  1. Crash dump writes to a syspd dump device route to mfi_syspd_dump (mfi_syspd.c:287-309); for len > 0 it calls mfi_dump_syspd_blocks(parent_sc, sc->pd_id, offset / MFI_SECTOR_LEN, virt, len) (mfi_syspd.c:301-304).
  2. mfi_dump_syspd_blocks (mfi.c:2439-2448, 2451-2484) builds a MFI_CMD_PD_SCSI_IO WRITE_10 pass frame with cm->cm_flags = MFI_CMD_POLLED | MFI_CMD_DATAOUT (2475) and calls mfi_mapcmd (2477), then returns error (2483) β€” the transport error only. It never reads back pass->header.cmd_status or pass->header.scsi_status.
  3. mfi_mapcmd (mfi.c:2152-2175) β†’ mfi_send_frame (mfi.c:2273-2323). For a POLLED command it pre-sets hdr->cmd_status = MFI_STAT_INVALID_STATUS (2284), issues, busy-waits (2309-2314) and: * timeout β†’ returns ETIMEDOUT (2316-2320); * any other firmware status β€” including MFI_STAT_FAILED, MFI_STAT_*, SCSI check conditions (scsi_status != 0) β€” returns 0 (2322). The firmware's actual verdict is only visible in hdr->cmd_status/hdr->scsi_status, which the dump path never consults (contrast the normal I/O completion mfi_bio_complete, mfi.c:2103-2108, which does check both).
  4. Consequently mfi_syspd_dump returns 0 for every chunk (mfi_syspd.c:304, 308) and do_dump's write loop believes the whole core was persisted.

Concrete failing scenario: system disk(s) on a healthy volume, dumpdev on a raw system PD; the PD dies (or was hot-removed β€” MR_EVT_PD_REMOVED, and note DF-5049: the syspd may still be present and openable after removal); panic; firmware completes each WRITE_10 with a failure status; driver reports success; savecore resurrects an empty/partial dump and the operator loses the crash evidence. Availability/integrity only (C:N/I:L/A:N) β€” the DF-5027 class.

Guest evidence

  • No MegaRAID PCI device, no /dev/mfisyspd* (env.txt) β€” path unreachable on this guest; no runtime reproduction possible, by hardware gate.
  • fix.diff applied to the guest /usr/src copy; make -DNO_CLEAN nativekernel KERNCONF=X86_64_GENERIC β†’ "Kernel build for X86_64_GENERIC completed", exit 0; source reverted and verified byte-identical afterwards. Baseline behavior not exercisable β†’ fix validation is compile-level only (fix_status not_testable).

Notes

  • The len == 0 branch of mfi_syspd_dump (mfi_syspd.c:305-307) is the upstream-inherited no-op "sync cache" β€” noted, but the real integrity bug is the per-chunk fake success above.
  • LD twin: mfi_dump_blocks (mfi.c:2437ff) + mfi_disk_dump (mfi_disk.c:322-345) have the identical omission; left to the mfi_disk family fix to keep this diff scoped to the syspd finding.

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 no→ patch + rebuild →patched clean

fix.diff applied cleanly on the guest /usr/src copy and compile-validated (make -DNO_CLEAN nativekernel KERNCONF=X86_64_GENERIC -> completed, exit 0); source reverted and verified byte-identical. Dump-path behavior not exercisable without hardware.

VERDICT.md guest-evidence section
↓ fix.diffper-fix-DF-5050

Confirmed kernel references

Detail

Evidence (decisive lines)

VERDICT.md source chain (path:line for all four links incl. the contrast with mfi_bio_complete which DOES check cmd_status); env.txt hardware absence; fix.diff compile validation

PoC changes

No seed PoC; none possible (kernel crash-dump path). Pack documents the source chain instead.

Verified recommended fix

After mfi_mapcmd in mfi_dump_syspd_blocks, fail with EIO when pass->header.cmd_status != MFI_STAT_OK || scsi_status != 0 (see fix.diff)

Verdict

Hardware-gated: the sink is the kernel crash-dump path and the guest has no MegaRAID controller (env.txt), so no runtime reproduction was possible. The source chain is deterministic: mfi_syspd_dump (mfi_syspd.c:301-304) forwards dump writes to mfi_dump_syspd_blocks (mfi.c:2477), whose polled transport mfi_send_frame returns 0 for ANY firmware completion status except timeout (mfi.c:2316-2322) β€” the dump path never re-checks pass->header.cmd_status/scsi_status, so firmware-rejected WRITE_10s (dead/removed/faulted PD) are acknowledged chunk-by-chunk and savecore believes an empty/corrupt dump. DF-5027 shape on the mfi syspd path.