# DF-2271 — scsi_ch.c chgetelemstatus: device-controlled avail walks off buffer

## Verdict: NOT REPRODUCED (hardware-gated) — source bug CONFIRMED real; fix.diff compiles.

**Classification:** `not_reproduced` / HW-gated / impact=none. The OOB-read info-leak
is **real in source**; the defense-in-depth `fix.diff` **compiles cleanly**
(`nativekernel rc=0`, `-Werror`, `-DINVARIANTS`).

### Why not reproduced (the gate)

The bug lives in `chgetelemstatus()` of the **ch(4) medium-changer peripheral**. The
guest has **no medium-changer device**: `camcontrol devlist` shows only the QEMU
DVD-ROM, there is **no `/dev/ch*`** node, and the ch(4) peripheral is not loaded.
The trigger also requires a **malicious SCSI changer** that reports
`READ_ELEMENT_STATUS avail > cesr_element_count`. The unprivileged actor `maxx`
(uid 1001, no privileged groups) cannot reach any of this.

Gate proof (this guest):
```
$ camcontrol devlist   ->  <QEMU QEMU DVD-ROM 2.5+>  (sg0,pass0,cd0)   [no changer]
$ ls /dev/ch*          ->  No such file or directory
$ id maxx              ->  uid=1001(maxx) gid=1001(maxx) groups=1001(maxx)
```

### The source bug (real, cited `path:line`)

`sys/bus/cam/scsi/scsi_ch.c`, `chgetelemstatus()`:
- The data buffer for the second READ ELEMENT STATUS is sized for exactly
  `cesr->cesr_element_count` descriptors: `size = sizeof(headers) + desclen *
  cesr->cesr_element_count` (`scsi_ch.c:1120-1122`), `kmalloc(size,...)` (`:1129`).
- But the parse loop iterates `avail = scsi_2btoul(st_hdr->count)` times
  (`scsi_ch.c:1159`, `:1176`) — a **16-bit value fully controlled by the device**.
  `avail != cesr->cesr_element_count` only produces an `xpt_print` warning
  (`:1161-1163`); it is **not clamped**.
- The loop strides `desc` by `desclen` each iteration (`:1181-1182`) and
  `copy_element_status(... desc ...)` reads through it; with `avail > count`, `desc`
  walks past the `data` allocation, leaking adjacent kernel heap. Leaked bytes land
  in the `avail`-sized `user_data` (`:1167`) and are `copyout`-ed to the user
  (`:1186-1188`). `desclen` is also device-controlled (`:1118`), so the surplus
  stride is attacker-tunable.

Attacker model: malicious SCSI medium-changer target. Victim: a process holding the
changer device open.

### Exploit chain

Not developed — no changer target on this guest and `/dev/ch*` absent. This is the
valid hard blocker "device-controlled primitive, no malicious device present"; there
is no userspace-only path to shape `avail`/`desclen` for the trusted (absent)
changer.

### Fix (defense-in-depth, validated to compile)

`fix.diff` clamps `avail` to `cesr->cesr_element_count` when the device over-reports,
so the parse loop can never stride past the allocation. Validated in the combined
`nativekernel` build (`fix_build.log`).

### Reproduce

No runnable PoC on this guest (`/dev/ch*` absent, no malicious changer target).
`build.sh`/`run.sh` document the gate; authoritative artifacts are `env.txt`,
`fix.diff`, `fix_build.log`.
