# DF-1125 — isp 24XX FCP response/sense length OOB read

## Verdict
**CONFIRMED (source-trace + harness) — INCONCLUSIVE on-guest (HW-gated).** The bug
is real; it cannot be triggered on the audit QEMU guest because the guest has no
QLogic 24XX Fibre Channel HBA and no FC fabric (virtio net/display only).

## Bug (one line)
`isp_handle_intr_response()` reads `req_response_len` straight from firmware DMA
and does `snsp = req_rsp_sense + rlen` without clamping; `req_rsp_sense[28]` is the
last field of the 64-byte stack queue entry, so any `rlen >= 28` drives `snsp`
past the entry, and `XS_SAVE_SENSE` memcpy()s up to 32 bytes from the wild pointer.

## Mechanism (path:line)
- `sys/dev/disk/isp/isp.c:5042` — `uint8_t qe[QENTRY_LEN];` (QENTRY_LEN = 64), a
  **stack** buffer; `sp = (isp24xx_statusreq_t *)qe`.
- `ispmbox.h:654-668` — `isp24xx_statusreq_t` is exactly 64 bytes; `req_rsp_sense[28]`
  is the final field (offset 36), filling the entry to the boundary.
- `isp.c:5225` — `rlen = sp->req_response_len;` (read from firmware DMA, **unvalidated**).
- `isp.c:5238-5240` — `snsp = sp->req_rsp_sense; snsp += rlen; slen = sp->req_sense_len;`
  with no bound; `rlen >= 28` => `snsp >= qe+64`.
- `isp.c:5307-5308` — `XS_SAVE_SENSE(xs, snsp, slen)` => `isp_freebsd.h:483`
  `memcpy(&xs->sense_data, snsp, imin(XS_SNSLEN, slen))` reads up to 32 bytes
  from the wild pointer.

## Trigger / threat model
A remote Fibre Channel target sending an FCP_RSP IU with `FCP_RSP_LEN` (mapped to
`req_response_len`) >= 28. `amdgpu_ucode_validate`-class guards do not apply; there
is no length validation anywhere on the 24XX path. Small `rlen` => kernel stack
info leak into CAM autosense (readable via REQUEST SENSE); large `rlen` => panic
from unmapped page. CVSS AV:A (adjacent fabric), no auth beyond fabric presence.

## Reproduction on the audit guest
Not possible — no QLogic 24XX FC HBA on the QEMU guest. `harness.c` is a faithful
**userspace replica** of the kernel arithmetic, built and run as the unprivileged
`maxx` user. It proves the OOB read primitive concretely and also runs the fixed
(clamped) logic to show 0 OOB remain.

## Build / run
```
cc -O2 -Wall -Wextra -o harness harness.c   # build.sh
./harness                                      # run.sh
```
Expected: `DF-1125: CONFIRMED OOB read primitive` then
`DF-1125 FIX: VALIDATED - clamps eliminate all OOB reads`.

## Fix
`fix.diff` clamps `rlen` to `sizeof(req_rsp_sense)` (28) and `slen` to
`sizeof(req_rsp_sense) - rlen` so `snsp + slen` cannot exceed the entry. Applies
cleanly (`git apply --check` OK); compiles in the GENERIC kernel build (rc=0).
