# DF-5297 verdict

**Status: REPRODUCED** — deterministic, 3/3 runs (plus a 4th on the clean
`with-src` snapshot baseline before fix validation).
**Impact: kernel memory corruption, device-controlled content**
(intra-object write into the live kernel CCB).
**Confidence: certain.**

## Root cause (path:line)

* `sys/bus/u4b/storage/umass.c:2602` — the autosense REQUEST SENSE
  allocation length is taken verbatim from `ccb->csio.sense_len`.
* `sys/bus/u4b/storage/umass.c:2609-2611` — the same value is passed as the
  data-in DMA length with `&ccb->csio.sense_data.error_code` as the target
  buffer.
* `sys/bus/cam/scsi/scsi_all.h:911-953` — `struct scsi_sense_data` is
  exactly 32 bytes in DragonFly.
* `sys/bus/cam/cam_ccb.h:601-602` — `sense_len` is a user-settable
  `u_int8_t` inside `ccb_scsiio`, copied from the user CCB by
  `xpt_merge_ccb()` (`sys/bus/cam/scsi/scsi_pass.c:546`) with no validation
  anywhere in CAM.

There is no third place: every kernel-internal caller (da/cd/sa/pt/probe)
hardcodes `SSD_FULL_SIZE` (32) or `SSD_MIN_SIZE` (18); the only way to get
`sense_len > 32` is a user-supplied CCB via pass(4) `CAMIOCOMMAND`.

## Reproduction chain (observed)

1. `pyevilmsc.py` on the QEMU host presents a full-speed BBB SCSI disk over
   usbredir (`usb-redir` device on a `piix3-usb-uhci` bus). Guest:
   `umass0`, `da8`, `/dev/pass8`.
2. The device CHECK CONDITIONs the READ(10) of LBA 0x5297.
3. `umass_cam_cb` (STATUS_CMD_FAILED) fetches sense: REQUEST SENSE with
   alloc length 255 (user's `sense_len`), DMA length 255 into the 32-byte
   `csio.sense_data`.
4. The device returns exactly 255 bytes (legal per SPC): 18 valid NOT-READY
   sense bytes + a rolling `0x40+(i&0x3f)` pattern.
5. The USB host controller DMAs all 255 bytes into the kernel CCB; bytes
   32..254 land past `sense_data`, overwriting `sense_len`, `cdb_len`,
   `sglist_cnt`, `scsi_status`, `sense_resid`, `resid`, `cdb_io` (incl. the
   `cdb_ptr` pointer slot), `msg_ptr`, `msg_len`, `tag_action`, `tag_id`,
   `init_id` and the union tail.
6. pass(4) `bcopy()`s the whole kernel `union ccb` back to userland
   (scsi_pass.c:612), so the corruption is directly observable:
   `cdb_io` region = `6c 6d 6e 6f …` (pattern indices 44..91) and
   `csio.resid == 0x6b6a6968` (pattern indices 40..43).

`run.log` is the decisive run; `run.2.log`/`run.3.log` equivalents were
captured in-session (3/3 identical 48/48 matches; rc=0 each time).

## Exploitability assessment (honest)

* The write length (≤255) and content are fully controlled by the malicious
  device; the destination is a freshly-DMA'd kernel object (`union ccb`,
  kmalloc M_CAMXPT, 720 bytes — the overflow stays intra-object, no
  adjacent-object overwrite: sense_data+255 < sizeof(union ccb)).
* Overwritable fields of note: `cdb_io.cdb_ptr` (read as a pointer by
  `umass_cam_sense_cb`, umass.c:2648-2652, when the CCB had
  CAM_CDB_POINTER set — yields a device-influenced `cmd[0]` read from an
  arbitrary address), `msg_ptr`, `resid`, `sglist_cnt`.
* **Privilege gate:** setting `sense_len > 32` requires issuing a CCB
  through `/dev/pass*`, which is `root:operator 0600`. An *unprivileged*
  user cannot trigger this on a default system; the untrusted party is the
  USB device (a root user plugging in an evil stick gets kernel memory
  corrupted with device bytes). uid=0 escalation for an unprivileged local
  user is therefore **not reachable** through this primitive alone — no
  unprivileged trigger path exists (all kernel CAM callers use 32).
*Severity accordingly: High (kernel memory corruption class) with the
privilege precondition documented.*

## Fix validation

`fix.diff` clamps `ccb->csio.sense_len` to `sizeof(ccb->csio.sense_data)`
before both uses. Validation protocol (see fix_*.log files):

1. `vm.sh reset` to the `with-src` snapshot; evil disk re-attached;
   baseline PoC on the unpatched kernel: 48/48 overflow (again).
2. `patch -p1 < fix.diff` in guest `/usr/src`; `make nativekernel
   KERNCONF=X86_64_GENERIC` + `make installkernel`; reboot.
3. Same PoC against the same evil device on the patched kernel:
   pattern confined to `sense_data[0..31]`, `cdb_io`/`resid` clean,
   `PATTERN_MATCH 0/48` → overflow gone, command still fails correctly
   (CHECK CONDITION with the device's NOT-READY sense) — no regression.

## Files

* `run.log` — decisive vulnerable run (48/48).
* `fix_baseline.log`, `fix_patched.log` — fix-validation pair.
* `evilmsc-device.log`, `qemu-usbredir.log` — device/host traces.
* `env.txt` — guest uname/nodes/INVARIANTS/compiler.

## Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest `make -j6 nativekernel KERNCONF=X86_64_GENERIC` completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
