# DF-1284 — Unbounded scatter-gather loop overflows `pmap->psg[17]` in hptmv `OsSendCommand`

**File:** `sys/dev/raid/hptmv/entry.c:2859` (sink); `:2800` (`pSgTable = pmap->psg`)
**Class:** CWE-787 Out-of-bounds Write (heap)
**Severity:** High

## The bug (source-confirmed)

In `OsSendCommand` (the SIM `XPT_SCSI_IO` action handler), the `CAM_SCATTER_VALID`
branch (`entry.c:2852`) copies a CAM CCB's scatter-gather list into the per-command
SG table with an unbounded loop (`entry.c:2859`):

```c
pCmd->pSgTable = pmap->psg;                          /* :2800 */
...
for (idx = 0; idx < ccb->csio.sglist_cnt; idx++) {   /* :2859 */
    pCmd->pSgTable[idx].dSgAddress = ...;            /* :2860 */
    pCmd->pSgTable[idx].wSgSize    = ...;            /* :2861 */
    pCmd->pSgTable[idx].wSgFlag   = ...;             /* :2862 */
}
```

`pSgTable` points at `pmap->psg`, which is `SCAT_GATH psg[MAX_SG_DESCRIPTORS]`
(`osbsd.h:143`, `MAX_SG_DESCRIPTORS = 17`, `global.h:47`). `sglist_cnt` is
`u_int16_t` (`cam_ccb.h:604`) — range 0..65535 — and is **not** validated against
the SIM's segment limit anywhere in this path. (The *other* path,
`hptmv_dmamap_callback`, does have `HPT_ASSERT(nsegs <= MAX_SG_DESCRIPTORS)` at
`:2652`; the `CAM_SCATTER_VALID` path has none.)

A CCB with `sglist_cnt > 17` therefore writes past `psg[17]` into the rest of the
`BUS_DMAMAP` struct (`next`/`pAdapter`/`dma_map` linkage follow it) and beyond into
the adjacent slab object — an attacker-controlled heap overflow.

## Reachability / threat model

The hptmv driver attaches to HighPoint RocketRAID (RR18xx) controllers. **None are
present** in the audit QEMU guest (`pciconf -l` shows no such device), so the SIM
action path is not runtime-reachable here. On real hardware, an operator/root user
with access to a `/dev/passN` (CAM pass-through) node for an hptmv target can issue
a `CAMIOCOMMAND` with `CAM_SCATTER_VALID` and a hand-crafted `sglist_cnt`, reaching
the loop. See `VERDICT.md`.

## Reproduce (harness)

```sh
./build.sh && ./run.sh
```

Decisive output:
```
[DF-1284] attacker sglist_cnt=64 (max allowed for psg[]=17)
[DF-1284] overflow past psg[17]: YES (47 guard entries clobbered; 752 bytes of adjacent heap overwritten)
```

## Fix

`fix.diff` rejects the CCB (`CAM_REQ_TOO_BIG`) when `sglist_cnt > MAX_SG_DESCRIPTORS`,
mirroring the existing cleanup path (`FreeCommand` / `dmamap_put` / `xpt_done`).
Validated: applies cleanly; `entry.c` compiles under `-Werror` via the kmod
framework. (A twin unbounded loop exists at `entry.c:2524` in `fOsBuildSgl`; the
cited attacker-reachable CAM path is fixed here.)
