# DF-1259 — mfi_stp_cmd unbounded SGE loop corrupts mfi_softc

## Verdict
**INCONCLUSIVE (hardware-gated latent bug, not triggerable on this guest).**
The OOB write is **confirmed real by source tracing**; not reachable on the
audit QEMU guest because `/dev/mfi%d` only exists once mfi attaches to an LSI
MegaRAID SAS controller (absent). fix.diff authored and **validated to apply +
compile** (nativekernel `rc=0`, `-Werror`).

## Mechanism (source trace)
`mfi_stp_cmd` (`sys/dev/raid/mfi/mfi.c:2725`):
```c
uint8_t i;                                   /* loop var — also wraps at 256 */
...
cm->cm_frame->header.sg_count = ioc->mfi_sge_count;
cm->cm_total_frame_size += (sge_size * ioc->mfi_sge_count);
for (i = 0; i < ioc->mfi_sge_count; i++) {
    bus_dma_tag_create(..., &sc->mfi_kbuff_arr_dmat[i]);      /* [2] */
    bus_dmamem_alloc(sc->mfi_kbuff_arr_dmat[i], ...&sc->kbuff_arr[i]);
    bus_dmamap_load(..., &sc->mfi_kbuff_arr_busaddr[i]);      /* [2] */
    kern_sge[i].phys_addr = ...;                              /* sized [2]  */
    cm->cm_frame->stp.sgl.sg64[i].addr = ...;                 /* sized [2]  */
    copyin(ioc->mfi_sgl[i].iov_base, sc->kbuff_arr[i], ioc->mfi_sgl[i].iov_len);
}
```
Array sizes:
- `mfi_kbuff_arr_dmat[2]`, `mfi_kbuff_arr_dmamap[2]`, `mfi_kbuff_arr_busaddr[2]`
  (`mfivar.h:196-198`).
- `struct mfi_stp_frame { ...; struct mfi_sg32 sg32[2]; struct mfi_sg64 sg64[2]; }`
  (`mfireg.h:591-598`).

The native `MFI_CMD` path calls `mfi_stp_cmd` (`mfi.c:3007-3008`) with **no cap**
on `ioc->mfi_sge_count` (unlike the Linux shim's `MAX_LINUX_IOCTL_SGE`). With
`mfi_sge_count >= 3`, index `i=2` writes past every `[2]` array into the
adjacent `mfi_softc` fields (`mfi_comms`, frame structs) and past the STP SGL
into the next frame fields. (The `uint8_t i` additionally wraps at 256, which is
secondary but real.) This is a kernel-heap / softc OOB write driven by a
user-controlled count.

## Reachability on the audit guest
- mfi is in `X86_64_GENERIC`, but the STP path requires (a) `/dev/mfi0` to exist
  (mfi attach ⇒ needs LSI MegaRAID HW) and (b) a user frame with
  `header.cmd == MFI_CMD_STP`. The guest has no LSI RAID PCI device, so
  `/dev/mfi0` does not exist (`open()` ⇒ `ENOENT`, see `run.log`). The sink is
  unreachable here; latent on MegaRAID-equipped hosts (root/operator only).

## Exploit chain
None developed — not exercisable on this guest (no `/dev/mfi`, no LSI HW). The
primitive is a controlled OOB write into `mfi_softc`/frame fields; reachable only
with hardware + root/operator creds. No unprivileged, hardware-free path exists.

## PoC changes
Authored `mfi_stp_oob.c` — reports `/dev/mfi0` absence for an unambiguous
negative result.

## Fix validation
- `fix.diff` applies cleanly: `git apply --check -p1` ⇒ OK (1 hunk).
- Compiles: `make nativekernel` ⇒ `NK_DONE rc=0`, `mfi.c` built with `-Werror`.
- Functional test: **not_testable** (no HW → no device node on either kernel).

## Recommended fix
Cap `ioc->mfi_sge_count` to the bounce-buffer array bound
(`sizeof(sc->mfi_kbuff_arr_dmat)/sizeof(...[0])` = 2) and return `EINVAL` if
exceeded; widen the loop index from `uint8_t` to `int` (the wrap is moot once
capped, but removes the footgun). See `fix.diff`.
