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

mfi_check_for_sscd() dereferences NULL cm_data on MFI_CMD DCMD CFG_ADD with zero data_len (and heap-OOB-reads short buffers, bypassing pre/post checks)

Summary

mfi_ioctl calls mfi_check_for_sscd() for every MFI_CMD before validating the command. With a user frame opcode=MFI_DCMD_CFG_ADD, data_len=0 and no DATAIN/DATAOUT flags, cm->cm_data stays NULL; mfi_check_for_sscd then evaluates conf_data->ld[0].params.isSSCD - a read at NULL+~2.5KB -> panic before any firmware round-trip. With a small nonzero data_len the same expression is a heap OOB read whose garbage result can set skip_pre_post=1 and bypass mfi_check_command_pre/post for CFG_ADD. Local DoS + pre/post-check bypass by any operator-group user via one ioctl; no firmware cooperation needed. NULL+length-guard fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3872 Β· 8 files
FileTypeDescriptionSize
README.md β€” 1.2 KB ↓ raw
VERDICT.md β€” 1.2 KB ↓ raw
build.sh β€” 106 B view raw
env.txt β€” 283 B view raw
fix.diff β€” 607 B view raw
mfi_ioctl_nullderef.c β€” 4.2 KB view raw
run.log β€” 264 B view raw
run.sh β€” 102 B view raw

DF-3872 β€” mfi_check_for_sscd() dereferences NULL cm_data (MFI_CMD, DCMD CFG_ADD, data_len=0)

mfi.c:3040 calls skip_pre_post = mfi_check_for_sscd(sc, cm); for every MFI_CMD ioctl before the pre/post command checks. Inside (mfi.c:2677-2685):

struct mfi_config_data *conf_data = cm->cm_data;
...
if ((cm->cm_frame->dcmd.opcode == MFI_DCMD_CFG_ADD) &&
    (conf_data->ld[0].params.isSSCD == 1)) {

With a user frame of cmd=MFI_CMD_DCMD, opcode=MFI_DCMD_CFG_ADD (0x04020000), data_len=0 and no DATAIN/DATAOUT flags, mfi.c:2996-3002 leaves cm->cm_data = 0, so conf_data->ld[0].params.isSSCD reads at NULL + offsetof(mfi_config_data, ld[0].params.isSSCD) (~2.5 KB, past array[MFI_MAX_ARRAYS]) β€” kernel read of unmapped low memory -> trap_fatal -> panic. Single ioctl, no firmware roundtrip needed.

Secondary: with a small-but-nonzero data_len (< offsetof(ld[0])), the same expression is a heap OOB read whose garbage value can force skip_pre_post=1, bypassing mfi_check_command_pre/post for CFG_ADD.

Build/run

cc -O2 -o mfi_ioctl_nullderef mfi_ioctl_nullderef.c   # trigger 2 = this finding
./mfi_ioctl_nullderef

Guest has no MegaRAID -> hardware-gated (run.log).

VERDICT.md
↓ download raw

DF-3872 VERDICT β€” inconclusive (hardware-gated), source-certain

  • Verified: mfi.c:2996-3002 (cm->cm_data = 0 when cm_len==0 or no data flags β€” note the "legacy app shim" at 2990 only sets the FLAGS, not a buffer), mfi.c:3040 (unconditional call), mfi.c:2679 + 2684-2685 (NULL deref guarded only by the opcode compare β€” short-circuit does NOT save the CFG_ADD case), mfireg.h MFI_DCMD_CFG_ADD = 0x04020000, mfi_config_data layout (size/array_count/spare_count/array[16]/ld[64] -> ld[0] ~2.5 KB in).
  • The read faults in kernel mode on an unmapped low address -> panic; nothing firmware-dependent happens before the fault (no roundtrip).
  • Guest: no /dev/mfi* (run.log) -> kernel path not exercisable; code-level certainty high: this is a straight missing-NULL/OOB guard on a user ioctl.
  • fix.diff checks conf_data != NULL and data_len covering ld[0].

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

require conf_data != NULL and cm_len covering ld[0] in mfi_check_for_sscd

fix.diff
↓ fix.diffper-fix-DF-3872

Confirmed kernel references

Detail

Evidence (decisive lines)

DF-3872/run.log (build OK, device absent) + VERDICT.md line-by-line trace

PoC changes

written fresh; shared trigger binary covers DF-3871 (T1), DF-3872 (T2), DF-3873 (T3)

Verified recommended fix

mfi_ioctl_nullderef.c

Verdict

MFI_CMD with cmd=MFI_CMD_DCMD, opcode=MFI_DCMD_CFG_ADD, data_len=0 leaves cm->cm_data NULL (mfi.c:2996-3002); mfi_check_for_sscd (mfi.c:3040 -> 2679,2684-2685) then evaluates conf_data->ld[0].params.isSSCD at NULL+~2.5K -> kernel fault -> panic, before any firmware round-trip. Small nonzero data_len instead gives a heap OOB read that can bypass pre/post checks (skip_pre_post=1). Guest has no MegaRAID: hardware-gated, source-certain.