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

MFI_CMD ioctl: devclass_get_softc() result used without NULL check -> one-ioctl kernel NULL-deref panic

Summary

mfi_ioctl MFI_CMD takes mfi_adapter_no (user uint16) and does sc = devclass_get_softc(devclass, adapter) with no NULL check; any adapter number >= the installed count returns NULL and the next statement (lockmgr) writes through NULL+offset -> panic. The Linux shim paths check NULL; only the native path omits it. Local DoS by any user who can open /dev/mfi0 (0640 root:operator) on a host with an mfi adapter - one ioctl. NULL-check fix.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-3871 Β· 8 files
FileTypeDescriptionSize
README.md β€” 1.2 KB ↓ raw
VERDICT.md β€” 1.1 KB ↓ raw
build.sh β€” 106 B view raw
env.txt β€” 283 B view raw
fix.diff β€” 484 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-3871 β€” mfi_ioctl MFI_CMD: devclass_get_softc NULL not checked -> kernel NULL deref panic

mfi_ioctl() MFI_CMD path, sys/dev/raid/mfi/mfi.c:2953-2962:

adapter = ioc->mfi_adapter_no;                       /* user, uint16 */
if (device_get_unit(sc->mfi_dev) == 0 && adapter != 0) {
        devclass = devclass_find("mfi");
        sc = devclass_get_softc(devclass, adapter);   /* NULL for adapter >= units */
}
lockmgr(&sc->mfi_io_lock, LK_EXCLUSIVE);             /* NULL deref */

devclass_get_softc() (subr_bus.c:950) returns NULL for any out-of-range or sparse unit. With exactly one adapter, every mfi_adapter_no in 1..65535 yields NULL, and the very next statement writes through &sc->mfi_io_lock (fault near NULL -> trap_fatal -> panic). The Linux ioctl shim checks NULL (mfi.c:3146-3147 and 3167); only the native MFI_CMD path lacks it.

Reach: any user able to open /dev/mfi0 (0640 root:operator) on a host with an mfi(4) adapter β€” trivial one-ioctl local DoS.

Build/run

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

On hardware: kernel panics at the lockmgr. On the audit guest (no MegaRAID): open() fails ENOENT -> hardware-gated (run.log).

VERDICT.md
↓ download raw

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

  • Code path verified line-by-line: mfi.c:2953-2962 (lookup), subr_bus.c:950-958 (NULL for out-of-range unit), mfi.c:2958 (lockmgr through NULL sc).
  • The sibling Linux shim paths DO NULL-check (mfi.c:3146-3147, 3167-3168), which is direct evidence the check was simply forgotten in the native path.
  • PoC builds and runs on the guest; /dev/mfi0 absent (no MegaRAID controller) so the kernel path cannot fire here β€” run.log records the ENOENT.
  • Trigger is one ioctl with mfi_adapter_no=1 on any real mfi host; write to near-NULL in kernel mode -> trap_fatal -> panic. Impact: local DoS by any user with the device open (operator group).
  • fix.diff adds the same NULL check the Linux shim already has.

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

add the same NULL check the Linux shim has

fix.diff
↓ fix.diffper-fix-DF-3871

Confirmed kernel references

Detail

Evidence (decisive lines)

DF-3871/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 mfi_adapter_no >= adapter count makes devclass_get_softc() return NULL (subr_bus.c:950-958) and mfi.c:2958 lockmgr's through &sc->mfi_io_lock -> kernel NULL-deref panic from a single ioctl; the Linux shim paths null-check (mfi.c:3146,3167), the native path does not. Device 0640 root:operator. Guest has no MegaRAID (run.log): hardware-gated, source-certain.