# DF-1237 — VERDICT

**Finding:** `trm_ExecuteSRB` at `sys/dev/disk/trm/trm.c:434-440` copies
`nseg` scatter/gather entries into `pSRB->pSRBSGL` (a DMA-coherent
allocation of `TRM_MAX_SG_LISTENTRY=32` entries) with no bounds check.
Two paths feed `nseg`:

- `trm_action` `CAM_SCATTER_VALID` branch at `trm.c:666-669` passes
  `pcsio->sglist_cnt` (a `u_int16_t`, 0..65535) verbatim — **fully
  user-controlled**.
- The `bus_dmamap_load` callback path at `trm.c:625` is bounded by
  `buffer_dmat`'s `nsegments=TRM_NSEG=btoc(MAXPHYS)+1` — but on x86_64
  `MAXPHYS = 128 KB`, so `TRM_NSEG = 33` while `pSRBSGL` has only 32
  slots → 1-entry (8-byte) overflow even via the legitimate path.

**Status:** NOT REPRODUCED on this guest — `trm` is **not in
X86_64_GENERIC** and no Tekram DC-395 controller is present.
**Confidence (bug is real):** certain (traced line-by-line in `sys/`).
**Impact ceiling:** unbounded DMA-coherent heap overflow (8 bytes ×
user-supplied `nseg`) — exploitable by any local user with write access
to `/dev/passN` (operator group) once a Tekram adapter and `trm` module
are present.

## Mechanism (confirmed line-by-line in `sys/`)

1. `pSRBSGL` is allocated at `sys/dev/disk/trm/trm.c:3027` from
   `pACB->sg_dmat`, which is created at `trm.c:3498` with
   ```c
   /*maxsize*/    TRM_MAX_SG_LISTENTRY * sizeof(SGentry),
   ```
   `TRM_MAX_SG_LISTENTRY = 32` (`trm.h:92`), `sizeof(SGentry) = 8`
   (`trm.h:80-83`). So `pSRBSGL` holds exactly **32** entries.

2. The copy loop in `trm_ExecuteSRB` (`trm.c:431-440`):
   ```c
   if (nseg != 0) {
       PSEG psg;
       bus_dma_segment_t *end_seg;
       bus_dmasync_op_t op;

       end_seg = dm_segs + nseg;
       psg = pSRB->pSRBSGL;
       while (dm_segs < end_seg) {
           psg->address = dm_segs->ds_addr;
           psg->length  = (u_long)dm_segs->ds_len;
           totalxferlen += dm_segs->ds_len;
           psg++; dm_segs++;
       }
   ```
   **No bounds check on `nseg`.** Each iteration writes 8 bytes past the
   previous `psg`, so `nseg > 32` overflows the DMA-coherent slab.

3. `nseg` sources:
   - **`bus_dmamap_load` callback** (`trm_action` `CAM_DIR_IN/OUT`
     non-scatter path, `trm.c:620-627`). `buffer_dmat` is created with
     ```c
     /*nsegments*/ TRM_NSEG,                      /* trm.c:3429 */
     ```
     where `TRM_NSEG = btoc(MAXPHYS) + 1` (`trm.h:98`). On x86_64
     `MAXPHYS = 128*1024`, `btoc(128K) = 32`, so `TRM_NSEG = 33`.
     bus_dma can legitimately produce up to 33 segments for a 128 KB
     buffer that straddles 33 pages — 1 entry (8 bytes) more than
     `pSRBSGL` can hold. **Off-by-one overflow** even without
     `CAM_SCATTER_VALID`.
   - **`CAM_SCATTER_VALID`** (`trm.c:666-669`):
     ```c
     segs = (struct bus_dma_segment *) pcsio->data_ptr;
     trm_ExecuteSRB(pSRB, segs, pcsio->sglist_cnt, 1);
     ```
     `pcsio->sglist_cnt` is a `u_int16_t` from userspace. `pass(4)`
     honors `CAM_SCATTER_VALID` and lets the caller supply a complete
     SG list. **No bounds check.** A caller passing `sglist_cnt=65535`
     triggers an 8 × 65535 = 512 KB DMA-coherent overflow.

4. The overflow corrupts whatever follows `pSRBSGL` in the
   DMA-coherent segment pool — adjacent SRBs' `pSRBSGL`/`pNextSRB`/
   `pccb` pointers, depending on slab layout. On any host with a Tekram
   adapter, this is a real **operator-group → kernel heap corruption**
   vector.

## Why it is NOT REPRODUCED on this guest

- `sys/config/X86_64_GENERIC` does **not** contain `device trm`. The
  driver is built only as the loadable module `trm.ko`.
- The audit guest has no Tekram DC-395U/UW controller in `pciconf -lv`.
- PoC `trm_sg_overflow.c` confirms `kldstat -v | grep trm` returns empty.

## Threat model & privilege boundary

Same as DF-1235: `/dev/passN` is operator-group on stock devfs; any user
in `operator` can issue `CAMIOCOMMAND` with `XPT_SCSI_IO`,
`CAM_SCATTER_VALID`, and a forged `ccb_scsiio.sglist_cnt`. Loading the
`trm` module is a one-time admin action; the exploit itself is fully
unprivileged once the module is loaded.

## Fix (authored in `fix.diff`, applied + compile-validated)

Two-part fix:

1. **Reject `nseg > TRM_MAX_SG_LISTENTRY`** at the top of `trm_ExecuteSRB`'s
   SG-copy block (`trm.c:431-440`), returning `CAM_REQ_TOO_BIG` and the
   SRB to the free list (mirrors existing failure paths in the file).
   This closes the `CAM_SCATTER_VALID` user-controlled overflow.

2. **Change `buffer_dmat`'s `nsegments` from `TRM_NSEG` (33) to
   `TRM_MAX_SG_LISTENTRY` (32)** at `trm.c:3429`, so `bus_dmamap_load`
   cannot produce a 33rd segment that overflows by one entry. With both
   fixes, the SRB's 32-entry SG buffer cannot be overrun on any path.

## Validation

- `fix.diff` applies cleanly with `patch -p1 --forward` (verified).
- All 5 audit fixes applied together; `make -j6 nativekernel
  KERNCONF=X86_64_GENERIC` returned **rc=0** with **no errors / warnings**
  under `-Werror`. `trm.c` was compiled cleanly as the loadable module
  `trm.ko`.
- Fix is **not_testable** at runtime on this guest (trm not in GENERIC,
  no Tekram HW).
