# DF-2270 — Missing bounds check on cesr_element_type in chgetelemstatus

**File:** `sys/bus/cam/scsi/scsi_ch.c:1066-1200`
**Claim:** `chet = cesr->cesr_element_type` (u_int16_t from user, values 4..65535)
indexes `int sc_firsts[4]` / `int sc_counts[4]` with NO bounds check — the only
ioctl handler in the driver without one — so the range check that gates
`cesr_element_count` is evaluated against out-of-bounds heap words.

## Verdict: REPRODUCED — OOB-read-driven validation bypass + guest-killing DoS

Verified on the CAM verbatim-interposition harness kernel: the byte-identical
`scsi_ch.c` compiled into a DragonFly 6.5-DEVELOPMENT INVARIANTS kernel
(`rig/`, sha256-gated `check_verbatim.sh`), a fabricated `ch_softc` placed
inside a 512KB `0x5a`-canary M_DEVBUF region, and a real `/dev/ch0` created
by the drivers own `make_dev` call. All tests drove the REAL `chioct`/
`chgetelemstatus` code through a real userland `ioctl(2)` on `/dev/ch0`.

## Reproduce

    cd rig
    ./build.sh            # push harness, gen TU, nativekernel, install, reboot
    ./run_tests.sh        # baseline suite (run_all.log / run_dmesg.log)
    ./run_hang.sh         # DESTRUCTIVE 4GiB M_INTWAIT test (hang_serial.log)
    ./make_fix.sh         # apply fix227x_all.diff on guest /usr/src
    ./build.sh            # fixed kernel
    ./run_fixed.sh        # A/B suite (run_fixed_all.log)

## Expected (vulnerable kernel)

- `ch_user ges 1 0 16`  -> SUCCESS (in-range control works)
- `ch_user ges 1 0 17`  -> EINVAL (in-range validation works)
- `ch_user ges 8 0 300` -> **SUCCESS** (element type 8 does not exist; the
  gate consulted `sc_counts[8]` = canary `0x5a5a5a5a` = 1515870810)
- `ch_user ges 1000 0 1000` -> **SUCCESS** (OOB read +4000 bytes past array)
- `sysctl debug.dfch.map=1` prints exactly which OOB words authorize (canary)
  and which reject (`sc_movemask` bytes read as `-1`)
- `sysctl debug.dfch.hang=1` (or a userland `ges 8 0 65535` against a target
  reporting desclen 0xffff) computes `size = 0xffff*0xffff+16` ≈ 4GiB and
  calls `kmalloc(size, M_DEVBUF, M_INTWAIT)`:

```
DFH2270HANG: thread calling REAL chgetelemstatus() with type=8 count=65535 ...
DFH2270HANG: *** THREAD DID NOT RETURN within 8s: kmalloc(..., M_INTWAIT) of
~4GiB is stuck (watchdog fired; system must be reset)
pagedaemon: WARNING! Continuous paging for 1 minutes
pagedaemon: WARNING! Continuous paging for 1 minutes
```
after which the guest dies outright (ssh banner timeout, force-kill + reset).

## Expected (fixed kernel)

- `ges 8 0 300` and `ges 1000 0 1000` -> EINVAL (gate added)
- `ges 1 0 16` still SUCCESS, `ges 1 0 17` still EINVAL (no regression)
- the same `debug.dfch.hang=1` direct trigger returns instantly:
  `chgetelemstatus RETURNED (no hang)`, `RES ccbs=0`

## Impact ceiling

Privesc: NONE. `/dev/ch0` is created `UID_ROOT/GID_OPERATOR 0600`
(scsi_ch.c:364-366) and `chopen` requires `SYSCAP_RESTRICTEDROOT`
(scsi_ch.c:396) — verified `crw------- root operator` on the guest. Only
root reaches the bug. Consequence = root-triggerable kernel heap OOB read
(4..65535 ints past two arrays, values used as authorization AND as SCSI
element addresses in the CDB, scsi_ch.c:1100/1137) plus the demonstrated
guest-killing M_INTWAIT allocation hang. Medium severity as filed stands.

## Fix

`fix.diff` (= rig/fix2270.diff, included in rig/fix227x_all.diff): the exact
sibling idiom from chmove (scsi_ch.c:805):

```diff
 	/* perform argument checking */
+	if (cesr->cesr_element_type > CHET_DT)
+		return (EINVAL);
```

## Files

- `rig/` full harness (df_pre.c / verbatim scsi_ch.c / df_post.c, gen.sh,
  check_verbatim.sh, build.sh, make_fix.sh, run_*.sh, ch_user.c)
- `run_all.log`, `run_dmesg.log` — baseline suite
- `hang_serial.log`, `hang_pagedaemon.log` — destructive DoS run (serial)
- `hang_iteration1.txt` — harness-artifact panic of iteration 1 (honesty)
- `run_fixed_all.log`, `run_fixed_dmesg.log` — A/B on the fixed kernel
- `fix.diff` — the one-hunk fix
- `env.txt`, `verdict.json`, `manifest.json`
