# 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).
