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

Heap buffer overflow / OOB read in MFI_CMD and MFI_LINUX_CMD_2 ioctl SGL copyin/copyout

Summary

mfi_ioctl MFI_CMD at mfi.c:2991-2998: cm_len=header.data_len (uint32 user), data=kmalloc(cm_len). SGL copyin loop at :3012-3025: for(i=0;i<mfi_sge_count;i++){copyin;temp+=len} with NO accumulated length check vs cm_len. DATAOUT: iov_len>data_len -> heap overflow. DATAIN: copyout OOB -> heap info leak. MFI_LINUX_CMD_2 at :3252-3263/:3294-3305: same, has count cap MAX_LINUX_IOCTL_SGE=8 but no length-sum cap. Native MFI_CMD also lacks count cap -> ioc->mfi_sgl[i>=16] reads past ioctl struct. Operator group (0640). Fix: track resid vs cm_len per SGE, cap count to MAX_IOCTL_SGE.

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-1258 Β· 12 files
FileTypeDescriptionSize
mfi_ioctl_overflow.c trigger-source trigger / documentation PoC 3.0 KB view raw
fix.diff suggested-fix git-apply-able patch closing the cited path 2.1 KB view raw
VERDICT.md verdict full source trace + reachability + fix analysis 4.2 KB ↓ raw
README.md readme build/run/expected 1.4 KB ↓ raw
build.sh build-script cc -O2 -Wall -o mfi_ioctl_overflow mfi_ioctl_overflow.c 126 B view raw
run.sh run-script ./mfi_ioctl_overflow 52 B view raw
run.log run-log decisive run, full output 462 B view raw
fix_build.log build-log nativekernel compile-validation excerpt, rc=0 32.5 KB view raw
env.txt environment uname, cc version, device/module state 583 B view raw
nk_full.log build-log full nativekernel build log (rc=0) 5.6 MB ↓ download
../fix_build_combined.log build-log Combined 41-finding kernel build (rc=0, -Werror clean) 5.6 MB ↓ download
../fix_build_summary.txt build-summary Summary of the combined 41-finding kernel build 826 B view raw
README.md readme build/run/expected
↓ download raw

DF-1258 PoC β€” mfi MFI_CMD/MFI_LINUX_CMD_2 SGL heap overflow

Status

INCONCLUSIVE on the audit guest: bug confirmed real by source trace at sys/dev/raid/mfi/mfi.c:2991-3305, but hardware-gated and not triggerable here. fix.diff authored and validated to apply + compile (nativekernel rc=0, -Werror). See VERDICT.md for the full trace and fix.diff for the patch.

Build

./build.sh

(equivalent: cc -O2 -Wall -o mfi_ioctl_overflow mfi_ioctl_overflow.c)

Run (as unprivileged user)

./run.sh

Expected on the audit guest

The PoC runs and reports that the kernel trigger surface is absent on this QEMU/KVM guest (no LSI MegaRAID controller / no AMD Tonga GPU / no DP MST hardware; mfi is in-kernel but /dev/mfi0 is never created; drm/amdgpu are loadable modules that are not loaded and would not attach). It exits 0 without exercising the sink. The bug is therefore a latent, hardware-dependent defect, not a false positive.

Files

  • mfi_ioctl_overflow.c β€” trigger / documentation PoC
  • fix.diff β€” standalone git apply -p1 patch closing the cited path
  • VERDICT.md β€” full source-level mechanism + reachability + fix analysis
  • build.log / run.log β€” captured build/run output
  • fix_build.log β€” nativekernel compile-validation excerpt (rc=0)
  • env.txt β€” guest environment
  • manifest.json β€” machine-readable artifact catalog
VERDICT.md verdict full source trace + reachability + fix analysis
↓ download raw

DF-1258 β€” mfi MFI_CMD / MFI_LINUX_CMD_2 SGL heap overflow

Verdict

INCONCLUSIVE (hardware-gated latent bug, not triggerable on this guest). The overflow is confirmed real by source tracing; it is not reachable on the audit QEMU guest because the /dev/mfi%d character device only exists once the mfi driver attaches to an LSI MegaRAID SAS controller, and no such hardware is present. The fix.diff is authored and validated to apply + compile against the real tree (nativekernel rc=0 with -Werror).

Mechanism (source trace)

Two ioctl paths share the same root cause β€” a kmalloc(data_len) buffer that is filled from a user SGL with no accumulated-length bound:

  1. mfi_ioctl, case MFI_CMD (sys/dev/raid/mfi/mfi.c): - cm->cm_len = cm->cm_frame->header.data_len; β€” data_len is a uint32 copied in verbatim from the user frame (mfi.c:2991). - cm->cm_data = data = kmalloc(cm->cm_len, M_MFIBUF, M_WAITOK|M_ZERO); (mfi.c:2998). - DATAOUT copyin loop (mfi.c:3015-3025): for (i=0; i<ioc->mfi_sge_count; i++){ len=ioc->mfi_sgl[i].iov_len; copyin(addr,temp,len); temp=&temp[len]; } β€” no check that (temp-data)+len <= cm->cm_len. If Ξ£ iov_len > cm_len, copyin writes past the end of data β†’ heap overflow (M_MFIBUF / kmalloc bucket). - DATAIN copyout loop (mfi.c:3064-3074): same shape; copyout reads past data β†’ heap info leak of the bytes that follow the buffer. - Additionally, the MFI_CMD path has no cap on ioc->mfi_sge_count (cf. the Linux shim, which caps at MAX_LINUX_IOCTL_SGE=16). mfi_sgl[] is itself [MAX_IOCTL_SGE=16] (mfi_ioctl.h:87), so mfi_sge_count > 16 reads ioc->mfi_sgl[i] OOB.

  2. mfi_linux_ioctl_int, case MFI_LINUX_CMD_2 (mfi.c:3202-3305): the SGE count IS capped (lioc_sge_count > MAX_LINUX_IOCTL_SGE β†’ EINVAL, mfi.c:3207), but the copyin (mfi.c:3253-3263) and copyout (mfi.c:3295-3305) loops still have no accumulated-length bound, so the identical Ξ£ iov_len > cm_len heap overflow / leak applies.

Reachability on the audit guest

  • mfi IS compiled into X86_64_GENERIC (device mfi, grep -c mfi_ on /boot/kernel/kernel β‡’ 172 symbols; mfi_ioctl at 0xffffffff80553400).
  • BUT the cdev is created only on attach: make_dev(&mfi_ops, unit, UID_ROOT, GID_OPERATOR, 0640, "mfi%d", unit) at mfi.c:721. The guest has no LSI MegaRAID PCI device (pciconf -l shows only a virtio-class vgapci0), so /dev/mfi0 does not exist and mfi_ioctl is never entered.
  • Confirmed at runtime: open("/dev/mfi0", O_RDWR) β‡’ ENOENT (see run.log).
  • Even with hardware, the node is 0640 root:operator, so write/ioctl needs root or the operator group β€” a privileged trigger, not fully unprivileged.

Exploit chain

None developed β€” not exercisable on this guest. The primitive (heap OOB write of attacker-controlled bytes via copyin, or OOB read via copyout, into a kmalloc bucket) is real, but the only entry is /dev/mfi*, which requires an LSI MegaRAID controller (absent) and root/operator credentials. There is no unprivileged, hardware-free path on the default guest. This is a latent driver bug reachable on MegaRAID-equipped hosts.

PoC changes

Authored mfi_ioctl_overflow.c β€” opens /dev/mfi0 and reports the device absence (the trigger surface is missing on this guest), so the negative result is unambiguous rather than a silent pass.

Fix validation

  • fix.diff applies cleanly: git apply --check -p1 β‡’ OK (5 hunks).
  • Compiles: applied to /usr/src, make -j6 nativekernel KERNCONF=X86_64_GENERIC β‡’ NK_DONE rc=0, no errors, mfi.c built with -Werror (fix_build.log).
  • Functional runtime test: not_testable (no LSI MegaRAID HW β†’ no /dev/mfi, so the overflow cannot be triggered on either unpatched or patched kernel; both would simply lack the device node).
  • Cap ioc->mfi_sge_count to MAX_IOCTL_SGE at MFI_CMD entry (closes the mfi_sgl[] OOB read and bounds the loop).
  • Add an accumulated-length guard (temp-data)+len > cm->cm_len β†’ EINVAL in all four copyin/copyout loops (MFI_CMD + MFI_LINUX_CMD_2). See fix.diff.

Fix verification

not_testable

compile validated -Werror

module/kernel build rc=0

Confirmed kernel references

β€”

Detail

Exploit chain

none

Evidence (decisive lines)

β€”

Verdict

Source-confirmed. mfi MFI_CMD SGL copyin accumulation no bounds vs cm_len + mfi_sge_count uncapped. mfi in GENERIC, no LSI RAID on guest.