# DF-1228 — VERDICT

**Finding:** `mpr_intr_locked()` uses the untrusted HBA-supplied `SMID`
directly as an array index into `sc->commands[]` at
`sys/dev/raid/mpr/mpr.c:2336` and `:2407-2408`, with no bounds check.

**Status:** NOT REPRODUCED (latent — no LSI MPT-Fusion 3 controller on this guest).
**Confidence (bug is real):** certain (traced line-by-line in `sys/`).
**Impact ceiling:** kernel heap OOB-read / control-flow hijack via
`cm->cm_complete` function pointer, when triggered by a malicious/buggy PCIe
HBA (PCIe passthrough, firmware compromise, DMA attack, or buggy controller).

## Mechanism (confirmed line-by-line in `sys/`)

1. `sc->commands` is allocated as `sizeof(struct mpr_command) * sc->num_reqs`
   at `sys/dev/raid/mpr/mpr.c:1522`, where `num_reqs` is an `int` derived
   from the controller's IOC facts (`mpr.c:381` `sc->num_reqs = prireqcr + reqcr;`).
   It is typically a few thousand.

2. The reply descriptor's `SMID` is a `U16` (0..65535) — see
   `sys/dev/raid/mpr/mpi/mpi2.h:416,442,456,...`. The HBA writes the reply
   descriptor into `sc->post_queue[]` (`mpr.c:2315`); the host CPU reads it
   back. SMID values are therefore **attacker-influenced** in the
   malicious/buggy-HBA threat model.

3. `mpr_intr_locked` at `sys/dev/raid/mpr/mpr.c:2336`:
   ```c
   case MPI2_RPY_DESCRIPT_FLAGS_SCSI_IO_SUCCESS:
   case MPI25_RPY_DESCRIPT_FLAGS_FAST_PATH_SCSI_IO_SUCCESS:
   case MPI26_RPY_DESCRIPT_FLAGS_PCIE_ENCAPSULATED_SUCCESS:
       cm = &sc->commands[le16toh(desc->SCSIIOSuccess.SMID)];
   ```
   and at `sys/dev/raid/mpr/mpr.c:2407-2408`:
   ```c
   } else {
       cm = &sc->commands[le16toh(desc->AddressReply.SMID)];
   ```
   **No bounds check precedes either indexing.** A `SMID > sc->num_reqs`
   silently produces an out-of-bounds pointer into adjacent kernel heap.

4. The only guard is `KASSERT(cm->cm_state == MPR_CM_STATE_INQUEUE, ...)`
   at lines 2337 and 2409. This is a no-op on production kernels
   (`INVARIANTS` is for debug builds) AND only fires AFTER the OOB index
   has occurred. The OOB pointer is then dereferenced to write
   `cm->cm_state` and `cm->cm_reply` (lines 2339-2340 / 2411-2412) — that
   is itself an OOB write — before the KASSERT would trip.

5. The OOB `cm` is then passed to `mpr_complete_command(sc, cm)` at line
   2434, which calls `cm->cm_complete(sc, cm)` (the function pointer
   inside the OOB struct). On INVARIANTS-OFF kernels this is a hijackable
   control-flow transfer from attacker-influenced heap residue.

6. Other sites in the same driver DO bounds-check SMID before indexing —
   e.g. `mpr_user_pass_thru` at `sys/dev/raid/mpr/mpr.c:1898`:
   ```c
   if (smid == 0 || smid > sc->num_reqs)
       ...
   ```
   so the fix is consistent with established practice elsewhere in `mpr`.

## Why it is NOT REPRODUCED on this guest

- `pciconf -lv` shows the standard QEMU i440BX/PIIX3/virtio device set only.
  There is no LSI/Broadcom/Avago MPT-Fusion SAS controller.
- `kldstat -v` confirms `pci/mpr` is **statically linked into X86_64_GENERIC**
  (the driver code IS in the running kernel) — the bug path exists, but the
  interrupt handler never fires without matching hardware.
- `ls /dev/mpr*` returns no device nodes (the cdev is created only in
  `mpr_attach` when matching HW probes successfully).

PoC `mpr_smid_oob.c` confirms the no-`/dev/mprN` state at runtime.

## Threat model & privilege boundary

This is a **driver-vs-peripheral** bug. The SMID is written by the HBA into
host memory via DMA; the host CPU trusts it. Realistic trigger scenarios:

- **Compromised HBA firmware** — many RAID HBAs run their own microcode,
  updatable from host tools or via flash. A backdoored/buggy firmware can
  emit any SMID in its reply descriptors.
- **PCIe passthrough of a hostile device** — VMs that pass through a
  physical HBA to a guest have the guest trust the HBA blindly; this is
  a known PCIe-trust problem.
- **Buggy controller** — even without malice, a firmware bug returning a
  stale or wrapped SMID causes a kernel OOB write/panic (DoS) on a real
  production host.

An unprivileged *user* cannot trigger this from userspace (the cdev is
`0640 root:operator`, and the SMID is on the HBA-reply path, not a direct
ioctl input). So this is **not** an unprivileged-to-root escalation vector
— it is a kernel-integrity / availability issue against a malicious or
buggy HBA.

## Fix (authored in `fix.diff`, applied + compile-validated)

Reject SMID `0` (reserved by MPI2 spec for unsolicited events) and
`SMID >= sc->num_reqs` (out of array) before indexing `sc->commands[]`,
in both the `SCSIIOSuccess` (line 2336) and `AddressReply` (line 2407)
branches of `mpr_intr_locked`. The fix mirrors the bounds check already
used in `mpr_user_pass_thru` at `mpr.c:1898`. The KASSERT is left in
place as a debug aid.

This brings the interrupt path up to the same level of input validation
already applied on the user-ioctl path.

## Validation

- `fix.diff` applies cleanly with `patch -p1 --forward` (verified).
- All 5 audit fixes applied together; `make -j6 nativekernel
  KERNCONF=X86_64_GENERIC` returned **rc=0** with **no errors / warnings**
  under `-Werror`. `mpr.c` was compiled cleanly into both the kernel
  proper and the `mpr.ko` module.
- Fix is **not_testable** at runtime on this guest (no mpr controller),
  consistent with the DF-1227 precedent.
