# VERDICT — DF-1280

## Status
**INCONCLUSIVE (source-confirmed; not runtime-triggerable on this guest).**

## Mechanism (source-confirmed)
The address-reply path in `mpt_intr` (`sys/dev/disk/mpt/mpt.c:709`) at
lines 738-744:

```c
reply_baddr  = MPT_REPLY_BADDR(reply_desc);              /* 738 */
offset       = reply_baddr - (mpt->reply_phys & 0xFFFFFFFF); /* 739 */
bus_dmamap_sync_range(mpt->reply_dmat, mpt->reply_dmap,
    offset, MPT_REPLY_SIZE, BUS_DMASYNC_POSTREAD);       /* 740-742 */
reply_frame  = MPT_REPLY_OTOV(mpt, offset);              /* 743 */
ctxt_idx     = le32toh(reply_frame->MsgContext);         /* 744 */
```

There is **no bounds check** on `offset`. The reply DMA pool is exactly
`2 * PAGE_SIZE` bytes (allocated in `mpt_pci.c:724-738`).
`MPT_REPLY_OTOV(m, i)` expands to `((void *)(&m->reply[i]))` (`mpt.h:842-843`),
so a `reply_desc` whose address bits encode an out-of-window `reply_baddr`
drives both `bus_dmamap_sync_range` and the dereference at line 744 out of
bounds.

The event-deferral fast path at lines 588-589 writes back into the same
out-of-range pointer:

```c
evtf = (struct mpt_evtf_record *)reply_frame;
evtf->context = context;
```

So the bug is both an OOB **read** (line 744) and an OOB **write** (line
589), into an attacker-influenced kernel heap offset.

A malicious or buggy IOC posts a crafted reply descriptor via DMA into the
host-visible reply queue; on the next interrupt this path dereferences it.
The `MPI_ADDRESS_REPLY_A_BIT` is just the LSB of the descriptor; the upper
31 bits come straight from the descriptor.

## Fix
Bounds-check `offset` against the reply pool size (`2 * PAGE_SIZE`) before
`bus_dmamap_sync_range` and `MPT_REPLY_OTOV`. If out of range, drop the
reply (`MPT_REPLY_EMPTY`) and continue the loop, so the bad descriptor
neither crashes the kernel nor feeds bogus `MsgContext` into the
request-pool indexing downstream.

## Verification on this guest
- `mpt` is statically compiled into `X86_64_GENERIC`
  (`sys/config/X86_64_GENERIC:93` — `device mpt`).
- No MPT HBA exists in the QEMU guest, so `mpt_intr` is never invoked. **Not
  runtime-triggerable.**
- Source-level proof of the OOB:
  - `mpt_pci.c:724-727` — reply DMA tag maxsize is `2 * PAGE_SIZE`.
  - `mpt_pci.c:733-738` — `bus_dmamem_alloc` of that exact size.
  - `mpt.h:840-843` — `MPT_REPLY_BADDR(x) = (x << 1)`;
    `MPT_REPLY_OTOV(m, i) = ((void *)(&m->reply[i]))`.
  - `mpt.c:739` — `offset = reply_baddr - reply_phys;` (no validation).
  - `mpt.c:743-744` — `reply_frame = &m->reply[offset]; …->MsgContext` (OOB read).
  - `mpt.c:588-589` — event-deferral path `evtf->context = context;` (OOB write).
- Fix validation: combined `X86_64_GENERIC` rebuild with this fix applied
  exited `rc=0`. Full log: `combined_build.log`.

## Exploit chain
None on this guest (no HW). On a system with a malicious MPT HBA, the
primitive is OOB read+write into kernel heap from IRQ context at the offset
the IOC chose. The realistic ceiling is determined by what slab object
sits at the chosen offset in the 2*PAGE_SIZE pool's neighborhood — but no
escalation is attempted here because the path is unreachable on this guest.

## Realistic impact ceiling
Hardware-attacker kernel heap OOB read+write. CVSS:
`AV:P/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H` — the write target is
attacker-influenced through `MsgContext`-indexed request-pool lookups, so
function-pointer / refcount corruption is conceivable in the right bucket.

## PoC changes
Folder was empty; added `trigger_analysis.c`, `build.sh`, `run.sh`,
`README.md`, this `VERDICT.md`, `fix.diff`, `manifest.json`,
`combined_build.log`.

## Recommended fix
Matches the finding proposal: bounds-check `offset` before
`MPT_REPLY_OTOV`. See `fix.diff`.

## Fix status
**not_testable** — bug requires absent hardware. `fix.diff` applies cleanly
and compiles into `X86_64_GENERIC` (combined build `rc=0`).
