mpt_download_fw consumes the IOC-uploaded firmware image with zero validation: unchecked ImageSize and unbounded ext-image walk give wild kernel reads streamed to the device / page-fault panic
Summary
When IOCFACTS sets FW_DOWNLOAD_BOOT, mpt_upload_fw lets the IOC DMA a 'firmware image' into fw_image; every header field (ImageSize, NextImageHeaderOffset, LoadStartAddress) is therefore device-authored. After any hard reset, mpt_download_fw uses them with no validation: the diag-out walker never checks ImageSize against the allocation - up to 4GiB of adjacent kernel/DMA memory streamed dword-by-dword to the PCI diag port until an unmapped page faults; the ext-image loop dereferences at unvalidated offsets with cycleable non-termination. VERIFIED: guest harness reproduces both (oversized walk printout + wild ext read faulting 'Bus error' rc=138). The mpt analogue of the mps/mpr trust-the-controller family. Malicious/faulty MPT IOC: boot-time wild reads + kernel info-leak-to-device.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-3911 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| trigger_analysis.c | trigger-source | userspace demonstrator of the kernel index/pointer/division arithmetic | 2.5 KB | view raw |
| build.sh | build-script | cc build of the demonstrator | 65 B | view raw |
| run.sh | run-script | runs the demonstrator | 89 B | view raw |
| build.log | build-log | guest compiler output | 11 B | view raw |
| run.log | run-log | guest runtime output (decisive run) | 281 B | view raw |
| env.txt | environment | guest uname + pciconf (no AMD GPU / no MPT HBA) | 838 B | view raw |
| fix.diff | suggested-fix | git-apply-able unified diff | 1.1 KB | view raw |
| fix_build.log | build-log | fix apply + nativekernel rebuild summary, rc=0 -Werror | 6.3 KB | view raw |
| VERDICT.md | verdict | full verification narrative | 2.6 KB | β raw |
| README.md | readme | human-readable summary | 2.3 KB | β raw |
| ../DF-3908/fix_kernel_full.log | build-log | full nativekernel build log shared from DF-3908 pack | 5.6 MB | β download |
DF-3911 β mpt_download_fw: unvalidated IOC-authored firmware image walk β wild kernel reads / OOB streaming to the device
File: sys/dev/disk/mpt/mpt.c
Lines: 2467-2485 (mpt_download_fw), upload at 2751-2784 / 2387-2420
Severity: Medium (malicious/faulty IOC β wild kernel read, page-fault panic, kernel-memory-to-device leak) β hardware-gated
Class: memcorrupt / CWE-125 (OOB read) + CWE-20 (missing validation), pass 2 (checklist (e) trust-of-IOC-facts / (f) walks beyond the known set)
Build / Run
./build.sh && ./run.sh
Expected output
fw_image_size=4096, IOC ImageSize=1073741824, IOC NextImageHeaderOffset=0xfffff000 UNFIXED KERNEL: walks 1073741824 bytes from a 4096-byte buffer -> OOB read reading ext header at fw_image+0xfffff000 ... Bus error (core dumped) <- wild read faults (userspace analogue) trigger rc=138 (139==SIGSEGV wild-read demonstrated)
Root cause
When IOCFACTS carries MPI_IOCFACTS_FLAGS_FW_DOWNLOAD_BOOT, the driver lets
the IOC DMA a "firmware image" of ioc_facts.FWImageSize bytes into
mpt->fw_image (mpt.c:2751-2784). Every MpiFwHeader_t /
MpiExtImageHeader_t field β ImageSize, NextImageHeaderOffset,
LoadStartAddress (mpi_ioc.h:1138-1163/1226-1235) β is therefore
device-controlled. After a hard reset, mpt_download_fw() consumes them with
zero validation:
- mpt_diag_outsl(mpt, fw_hdr->LoadStartAddress, (uint32_t*)fw_hdr, fw_hdr->ImageSize)
(:2469-2470) β ImageSize never checked against mpt->fw_image_size;
up to 4 GiB of kernel memory streamed to the PCI diag data port until an
unmapped page faults β panic. (mpt_diag_outsl :2422-2440 walks
roundup2(len,4)/4 words.)
- ext-image loop (:2474-2485): ext = fw_hdr + ext_offset with
NextImageHeaderOffset unvalidated β wild reads at arbitrary offsets;
cycleable offsets β non-terminating loop.
Not covered by knowns: DF-1278 (recursion), DF-1279/DF-2481 (RequestFrameSize
div0), DF-1280 (reply-frame offset), DF-1281 (EventDataLength) are all
different sites.
Fix
fix.diff validates ImageSize β€ fw_image_size, ext_offset within
[sizeof(MpiFwHeader_t), fw_image_size - sizeof(MpiExtImageHeader_t)], and
ext->ImageSize β€ fw_image_size - ext_offset, bailing with EIO. Validated in
the batch nativekernel rebuild: mpt.c compiled in-kernel and as module with
-Werror, rc=0 (fix_build.log).
DF-3911 β Verification Verdict
Status: inconclusive (reproduced=0) Impact: none on guest (no MPT HBA present) Confidence: certain (source-level)
Finding
mpt_download_fw() walks the IOC-uploaded firmware image without validating
any header field:
- primary image: mpt_diag_outsl(mpt, fw_hdr->LoadStartAddress,
(uint32_t*)fw_hdr, fw_hdr->ImageSize) β mpt.c:2469-2470; ImageSize (u32,
offset 0x2C in MpiFwHeader_t) vs the allocation (mpt->fw_image_size =
ioc_facts.FWImageSize, mpt.c:2751) never cross-checked β OOB read up to
4 GiB past the DMA-coherent buffer, streamed dword-by-dword to the PCI
diag data port (mpt_diag_outsl, mpt.c:2422-2440).
- extension chain: ext = (MpiExtImageHeader_t *)((uintptr_t)fw_hdr +
ext_offset) with ext_offset = NextImageHeaderOffset (u32) unvalidated β
mpt.c:2474-2482 β wild reads; attacker-chosen offsets; can loop forever.
Threat model: malicious or faulty IOC (hardware-level attacker), same gate
as DF-1279/DF-1280/DF-2481 (which covered different fields).
Verification
- Source chain certain (README).
- Guest run (run.log): performing the identical ext-header read at fw_image+0xfffff000 faults immediately ("Bus error (core dumped)") β the userspace analogue of the kernel page fault; the ImageSize>allocation form is printed before it. The kernel path itself needs an MPT IOC (absent β env.txt shows no LSI device on the PCI bus), hence inconclusive, matching the DF-1280 precedent.
Fix validation
fix.diff bounds ImageSize, ext_offset and ext->ImageSize against
fw_image_size (EIO on violation). Applied with the batch; nativekernel
rebuild rc=0, mpt.c compiled in-kernel (fix_kernel_full.log:16461 in the
DF-3908 pack) and as a KLD module (:24168) with -Werror, 0 errors.
fix_status = fixed (compile-validated).
Notes
This is the mpt(4) analogue of the mps/mpr "trust the controller's self-reported image/facts" family (DF-3800/3813/3814). The checklist (e) realloc/iocfacts twin in this file β mpt_configure_ioc retry paths re-running mpt_dma_buf_alloc and leaking DMA memory β is real but shadowed by the known infinite recursion DF-1278 (the recursion never returns, so the leak is unbounded-but-moot); noted in the file's negative notes rather than filed.
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
fixedfixed (compile-validated): fix.diff applied to clean guest /usr/src with the batch; nativekernel rebuild rc=0, 0 errors; mpt.c compiled in-kernel (full log line 16461) and as KLD module (line 24168) with -Werror. Runtime re-test impossible on this guest (no MPT HBA).
fix_build.log (apply + compile lines + NK_DONE rc=0); run.log (Bus error on wild read); full build log shared from DF-3908/fix_kernel_full.log.
Confirmed kernel references
Detail
Evidence (decisive lines)
run.log on guest: wild ext-header read at fw_image+0xfffff000 faults (Bus error, rc=138); VERDICT.md source chain; fix_build.log: mpt.c compiled in-kernel + KLD with -Werror, NK_DONE rc=0
PoC changes
Pass-2 reviewer authored trigger/analysis + fix from source; harness built and run on the DragonFly guest (wild-read fault observed); fix compile-validated via full nativekernel rebuild.
Verified recommended fix
Validate fw_hdr->ImageSize and every ext-image offset/size against mpt->fw_image_size; return EIO on violation.
Verdict
Source-certain wild-read family in mpt_download_fw(): the IOC-uploaded firmware image header (MpiFwHeader_t/MpiExtImageHeader_t, all fields device-controlled) is consumed with zero validation - fw_hdr->ImageSize is never checked against the fw_image_size allocation before streaming ImageSize bytes to the PCI diag port, and the ext-image chain dereferences fw_hdr+NextImageHeaderOffset unbounded (wild reads, non-terminating loops). Malicious/faulty IOC -> kernel page-fault panic and kernel-memory-to-device streaming. Guest harness reproduces the wild-read fault (Bus error/SIGSEGV); kernel path needs an MPT HBA (absent).
No comments yet.