# DF-5333 VERDICT

## Status: REPRODUCED (3/3 attach cycles, varying garbage each time) — fix validated

## Root cause (source-accurate)

`chdone()` CH_CCB_PROBE (sys/bus/cam/scsi/scsi_ch.c):

- **:541** `char announce_buf[80];` — stack buffer, uninitialized.
- **:549-573** success branch (`CAM_REQ_CMP`) fills it with ksnprintf.
- **:574-637** error branch: if `cherror(...)` returns non-zero, the path
  ends with `announce_buf[0] = '\0'` (:636).
- **BUT** `cherror()` → `cam_periph_error()` returns **0** for some
  completions that are NOT CAM_REQ_CMP:
  - cam_periph.c:1313-1318 — `csio->scsi_status` in
    {SCSI_STATUS_OK, COND_MET, **INTERMED**, INTERMED_COND_MET} →
    `error = 0`;
  - cam_periph.c:1509-1513 — CHECK CONDITION whose sense maps to
    `SS_NOP` (e.g. RECOVERED ERROR) → `error = 0`.
  A device completing the attach-time probe MODE SENSE with SCSI status
  **INTERMED (0x10)** (CAM sets CAM_SCSI_STATUS_ERROR) therefore lands in
  **neither** branch: `announce_buf[0]` is **read uninitialized at :639**
  and, when non-zero, the buffer is printed to console/dmesg by
  `xpt_announce_periph()` → `kprintf("%s%d: %s\n", ..., announce_string)`
  (cam_xpt.c:1761-1763). %s walks until a NUL — potentially past the
  80-byte buffer.

## How it was reproduced (guest, EVIL kernel, evilcd3.ko rig)

`chdone()` executes on the CAM cambio SWI thread (xpt_done queues CCBs to
`sim->sim_doneq`, cam_xpt.c:4891-4920). Choreography (run.sh):

1. normal attach — chdone's success path runs `ksnprintf(announce_buf, ...)`
   on that thread (seeds the stack slot with live data);
2. `debug.evilcd.invalidate=1` destroys the ch periph;
3. `debug.evilcd.chprobe=1` (one-shot) + `debug.evilcd.rescan=1` —
   re-attach; the rig completes the probe MODE SENSE with
   CAM_SCSI_STATUS_ERROR + scsi_status 0x10 (INTERMED):
   `[EVILCH] MODE SENSE page 0x1d -> INTERMED (error==0 path)`;
4. chdone takes the uninit path; `xpt_announce_periph` prints whatever
   stale bytes occupy `announce_buf` on the SWI thread.

### Result (run.log / run.2.log — three independent cycles)
```
[EVILCH] MODE SENSE page 0x1d -> INTERMED (error==0 path)
ch0 at evilch0 bus 0 target 1 lun 0
ch0: <EVILCH OOBTEST 1.0 > Removable Changer SCSI-2 device
ch0: 3.300MB/s transfers
ch0:  x^NO            <-- uninitialized stack printed with %s
```
run #2: `ch0: M-0r^NO` (bytes 0xB0 0x72 0x8E 0x4E 0x4F),
run #3: `ch0: M-`^[BM^M` (bytes 0xC0 0x60 0x1B 0x42 0x0D) —
different stale kernel-stack bytes each cycle, high-bit bytes consistent
with pointer/metadata residue from prior completions on the cambio
thread. A normal attach in between prints the legitimate
`ch0: 8 slots, 0 drives, 1 picker, 0 portals` line — the INTERMED attach
prints garbage instead, proving the ksnprintf never ran.

## Impact

Uninitialized kernel-stack disclosure to the console/dmesg ring (up to 80
bytes, possibly further until a NUL). dmesg is world-readable on the
standard DragonFly configuration (`dmesg(8)` reads the msg buffer without
privilege), so any local user can read the leaked bytes; content is
residue of the cambio SWI thread's prior work (other devices' completion
paths — sense buffers, pointers). Low severity (bounded size, console
only), but it is a genuine CWE-457 read-and-print of uninitialized stack
triggered by device-controlled status bytes.

## Fix validation (done)

`fix.diff` adds `announce_buf[0] = '\0';` immediately after the
declaration. Guest /usr/src patched (together with DF-5332's M_ZERO fix,
same rebuilt kernel), rebooted, exact same choreography rerun
(`fixedA.log`): the INTERMED attach announces the device but prints **no**
garbage `ch0: <bytes>` line — the branch cannot fire on uninitialized
data anymore.

## Guest
DragonFly dfbsd 6.5-DEVELOPMENT #0: Fri Oct 2 22:55:55 UTC 2026
root@dfbsd:/usr/obj/usr/src/sys/EVIL x86_64 (GENERIC + `device ch`);
gcc (DragonFly) 8.3; evilcd3.ko 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.
