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)
PoC verification
Evidence pack
findings/poc/DF-1258 Β· 12 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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 |
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 PoCfix.diffβ standalonegit apply -p1patch closing the cited pathVERDICT.mdβ full source-level mechanism + reachability + fix analysisbuild.log/run.logβ captured build/run outputfix_build.logβ nativekernel compile-validation excerpt (rc=0)env.txtβ guest environmentmanifest.jsonβ machine-readable artifact catalog
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:
-
mfi_ioctl,case MFI_CMD(sys/dev/raid/mfi/mfi.c): -cm->cm_len = cm->cm_frame->header.data_len;βdata_lenis auint32copied 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,copyinwrites past the end ofdataβ heap overflow (M_MFIBUF / kmalloc bucket). - DATAIN copyout loop (mfi.c:3064-3074): same shape;copyoutreads pastdataβ heap info leak of the bytes that follow the buffer. - Additionally, theMFI_CMDpath has no cap onioc->mfi_sge_count(cf. the Linux shim, which caps atMAX_LINUX_IOCTL_SGE=16).mfi_sgl[]is itself[MAX_IOCTL_SGE=16](mfi_ioctl.h:87), somfi_sge_count > 16readsioc->mfi_sgl[i]OOB. -
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_lenheap 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_ioctlat0xffffffff80553400). - BUT the cdev is created only on attach:
make_dev(&mfi_ops, unit, UID_ROOT, GID_OPERATOR, 0640, "mfi%d", unit)atmfi.c:721. The guest has no LSI MegaRAID PCI device (pciconf -lshows only a virtio-classvgapci0), so/dev/mfi0does not exist andmfi_ioctlis never entered. - Confirmed at runtime:
open("/dev/mfi0", O_RDWR)βENOENT(seerun.log). - Even with hardware, the node is
0640 root:operator, so write/ioctl needs root or theoperatorgroup β 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.diffapplies 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.cbuilt 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).
Recommended fix
- Cap
ioc->mfi_sge_counttoMAX_IOCTL_SGEatMFI_CMDentry (closes themfi_sgl[]OOB read and bounds the loop). - Add an accumulated-length guard
(temp-data)+len > cm->cm_len β EINVALin all four copyin/copyout loops (MFI_CMD + MFI_LINUX_CMD_2). Seefix.diff.
Fix verification
not_testablecompile 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.
No comments yet.