# DF-4868 — VERDICT

**Status: untested (hardware-gated). Reproduced: 0. Impact if exercised: panic-class
NULL deref / OOB console leak (Low, firmware-gated). Confidence: certain (code-level),
likely (real-world triggerability).**

## Why untested

The audit QEMU/KVM guest is virtio-only: `pciconf -l` (env.txt) shows no LSI vendor
0x1000 device, there are no `hw.mps.*` sysctls, and "mps" never appears in
dmesg.boot — the mps(4) driver never attaches, so no MPI2 IOC status can ever be
produced. The sink is only reachable in the completion path of an attached
Fusion-MPT2 controller. Classification follows the DF-4789 protocol (the identical
finding on the mpr twin, filed and persisted the same way).

## Line-accurate trace (guest-independent proof of the code path)

- `sys/dev/raid/mps/mps_sas.c:2062-2065` — `case MPI2_IOCSTATUS_INVALID_SGL:`
  calls `mps_print_scsiio_cmd(sc, cm)` with **no gate** (not behind
  `sc->mps_debug`, unlike every other printer in this driver).
- `sys/dev/raid/mps/mps_table.c:494-495` — `mps_print_scsiio_cmd` casts
  `cm->cm_req` to `MPI2_SCSI_IO_REQUEST *` and calls
  `mps_print_sgl(sc, cm, req->SGLOffset0)`; `offset * 4` (`:459`) indexes the
  request frame with an unvalidated u8.
- `mps_table.c:462` — `hexdump(frame, 128, NULL, 0)`; frame pool per-frame size is
  `sc->facts->IOCRequestFrameSize * 4` (`sys/dev/raid/mps/mps.c:750`, `:812-823`)
  — firmware-reported, never checked ≥128.
- `mps_table.c:463-472` — the only loop bound is
  `flags & (MPI2_SGE_FLAGS_END_OF_LIST | MPI2_SGE_FLAGS_END_OF_BUFFER)`
  (mpi2.h:936/941). `sge++` each iteration; 16-byte SGEs; nothing caps iterations
  to the frame.
- `mps_table.c:473-482` — on `MPI2_SGE_FLAGS_LAST_ELEMENT` (0x80, mpi2.h:935):
  `chain = TAILQ_FIRST(&cm->cm_chain_list)` (first hit) or `TAILQ_NEXT(...)`; then
  `frame = (char *)chain->chain` at `:482` — **no NULL check**.
  `cm->cm_chain_list` is `TAILQ_INIT`'d empty at `sys/dev/raid/mps/mps.c:859` and
  only populated by `mps.c:1808` when the SGL spills out of the request frame;
  small I/Os therefore guarantee `TAILQ_FIRST(...) == NULL`.
- `mps_table.c:484` — chain-frame `hexdump(frame, 128, ...)`; chain frames are
  `IOCRequestFrameSize * 4` bytes (`mps.c:772`, `:821-823`).

Reachability delta vs the mpr twin (DF-4789): mpr's simple-SGE walk has *no* chain
handling (chain deref only in the IEEE walk); mps has exactly one walk and it
contains the chain deref — so the NULL-deref shape is on the default path here.

## Exploit chain

None: read-only walk + NULL deref. Ceiling = local DoS (panic) plus console
disclosure of request-pool heap contents (kernel pointers, DMA addresses) in the
`seg%d` lines and hexdumps. The walked data is the driver-built request, so the
trigger requires firmware misbehavior or DMA corruption; severity stays Low.

## Fix validation

`fix_status: not_testable` — the guards in fix.diff are mechanical (NULL break,
per-frame iteration cap, hexdump bounded to `IOCRequestFrameSize*4`) but cannot be
boot-validated without MPT2 hardware in the guest.

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