# DF-1280 — Unchecked reply-frame offset in mpt_intr (OOB read/write)

## Finding
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(... offset, MPT_REPLY_SIZE, ...);     /* 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:723-738`); `MPT_REPLY_OTOV`
expands to `&m->reply[offset]` (`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 (`evtf->context = context;`)
then writes back into the same out-of-range `reply_frame`, so the bug is an
OOB read *and* an OOB write — into an attacker-influenced kernel heap
offset.

A malicious or buggy IOC that posts a crafted reply descriptor (e.g. via
PCIe DMA of a malicious reply queue entry) triggers this on the next
interrupt. The `MPI_ADDRESS_REPLY_A_BIT` mask is just the LSB of the
descriptor; the upper 31 bits are taken straight from the descriptor.

## Fix
Bounds-check `offset` against the actual 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
- The mpt driver is statically compiled in but no MPT HBA exists in QEMU
  (see DF-1278 README), so `mpt_intr` is never invoked. Bug is **not
  runtime-triggerable on this guest**.
- Source-level confirmation:
  - `mpt_pci.c:724-727` — reply DMA tag maxsize is `2 * PAGE_SIZE`.
  - `mpt_pci.c:733-738` — `bus_dmamem_alloc` allocates 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]; ctxt_idx = ...->MsgContext;`
  - `mpt.c:588-589` — event-deferral path writes `evtf->context` back through
    the same out-of-range pointer.
- Fix verified to compile (combined build with DF-1278/1279/1285/1287).

## Realistic impact ceiling
Hardware-attacker OOB read+write into the kernel heap via a crafted IOC
address-reply descriptor. CVSS reflects `C:H/I:H/A:H` at `AV:P` because the
write targets (function pointers, refcounts) are bucket-dependent but
attacker-influenced through the `MsgContext` indexing path.
