# DF-2273 — VERDICT

## Status: REPRODUCED (as the bounded scan over-read described); impact none

## Mechanism (line-accurate)

chsetvoltag (scsi_ch.c:1238) receives the csvr and at :1300-1301 executes

  memcpy(ssvtp.vitf, csvr->csvr_voltag.cv_volid,
         min(strlen(csvr->csvr_voltag.cv_volid), sizeof(ssvtp.vitf)));

cv_volid is u_char[CH_VOLTAG_MAXLEN+1] = [33] (sys/sys/chio.h:133-136);
vitf is u_int8_t[32] (scsi_ch.h:299-305). Nothing guarantees a NUL inside
the 33 bytes, so strlen scans past the field on a non-terminated request.

## Mechanical verification

strlen interposed (counting shim -> real strlen) inside the verbatim
driver TU on the harness kernel; userland driver places NUL at chosen
offsets or none:

- nulpos 0/5/32 -> scan 0/5/32 (correct stop at the NUL)
- struct fully nonzero past the field -> scan 36: walked cv_volid[33] +
  cv_serial (2) + padding + 1 byte beyond the copied struct into kernel
  stack (strlen_over33=1, strlen_max=36)
- ssvtp.vitf as seen by the transport: 32 data bytes + space padding
  (svt_vitf_nonspace=32) — the retained copy is bounded by min(,32);
  NO retained over-read (exactly what the finding claims)

## Impact ceiling — lower than the classic shape

DragonFly marshals _IOW ioctl payloads through a one-shot copyin into a
kernel stack buffer (sys/kern/sys_generic.c:685 copyin, :724 fo_ioctl
with the kernel buffer). The scan therefore runs over kernel stack
memory adjacent to the copy, stopping at the first zero byte; it cannot
walk user pages, cannot reach unmapped user VA, and cannot fault. The
boundary experiment proves it end to end: csvr placed so the field ends
exactly at a page boundary with the next page PROT_NONE -> ioctl
SUCCEEDS (the syscall-layer copyin touches only the struct itself);
no kernel-mode walk of user memory exists in this path.

Net effect: a few bytes of adjacent kernel stack are scanned (not
disclosed anywhere — the memcpy copy is bounded and vitf is
space-padded before the copy). Info severity stands; impact none.

## Fix validation

strnlen with the field-size bound (fix.diff). Fixed kernel counters:
strlen_calls=0, strnlen_calls=5, strnlen_max=33 — the scan now stops at
the field end in every case; retained copy unchanged (32 bytes);
boundary case clean. fix_status: fixed.

## Files

voltag_baseline.log, voltag_fixed.log, ch_user.c, fix.diff, env.txt,
verdict.json, manifest.json; shared rig ../DF-2270/rig/.

## 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.
