DF-5333 / verdict.json
{ "finding_id": "DF-5333", "status": "reproduced", "verdict": "chdone()s CH_CCB_PROBE handler declares char announce_buf[80] uninitialized and only fills it in the CAM_REQ_CMP branch or sets announce_buf[0]=\\0 in the error!=0 branch; cam_periph_error() returns 0 for SCSI-status INTERMED (cam_periph.c:1313-1318) and for RECOVERED-ERROR sense (SS_NOP, cam_periph.c:1509-1513) even though the CCB is not CAM_REQ_CMP, so a changer completing the attach-time probe MODE SENSE with INTERMED drives chdone into neither branch: announce_buf[0] is read uninitialized (scsi_ch.c:639) and the buffer is printed to console/dmesg with %s by xpt_announce_periph (cam_xpt.c:1761-1763). Reproduced 3/3 attach cycles on the QEMU guest (evilcd3 rig, one-shot INTERMED mode sense): the announce line after the INTERMED marker printed stale kernel-stack bytes that differ per cycle ( x^NO, 0xB0 0x72 0x8E 0x4E 0x4F, 0xC0 0x60 0x1B 0x42 0x0D) while a normal attach prints the legitimate counts string -- proving ksnprintf never ran and the printed bytes are uninitialized cambio-SWI-thread stack residue (readable by any local user via dmesg).", "reproduced": true, "impact": "leak", "confidence": "certain", "exploit_chain": "", "evidence": "run.log line ch0: x^NO immediately after [EVILCH] MODE SENSE page 0x1d -> INTERMED (error==0 path); run.2.log shows two further cycles with ch0: M-0r^NO and ch0: M-`^[BM^M (cat -v); fixedA.log (patched kernel) shows the INTERMED marker followed by NO announce block and NO garbage line across 3 cycles", "kernel_refs": [ "sys/bus/cam/scsi/scsi_ch.c:541", "sys/bus/cam/scsi/scsi_ch.c:549-573", "sys/bus/cam/scsi/scsi_ch.c:574-637", "sys/bus/cam/scsi/scsi_ch.c:639-640", "sys/bus/cam/cam_periph.c:1313-1318", "sys/bus/cam/cam_periph.c:1509-1513", "sys/bus/cam/cam_xpt.c:1761-1763" ], "poc_changes": "Derived rig from DF-5327s evilcd2.c; added one-shot INTERMED MODE SENSE knob, invalidate knob (needed to destroy the periph so a rescan re-runs the attach probe), and made XPT_GET_TRAN_SETTINGS succeed (xpt_announce_periph bails before printing the string otherwise). Stack-paint approach abandoned after confirming chdone runs on the cambio SWI thread (xpt_done queues to sim_doneq, cam_xpt.c:4891); replaced with normal-attach-then-INTERMED-attach choreography on the same completion thread.", "attempts": 4, "guest_uname": "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)", "runtime_sec": 2400, "guest_dirty": 1, "build_cmd": "cd /root/df5333 && sh build.sh", "run_cmd": "cd /root/df5333 && sh run.sh", "code_hash": "feb56ee9fa0bcf4e0a95edf210374ba75262da5d2b2f81a328c56fad497fcc8d", "notes": "Low severity: bounded disclosure (~80 bytes) to console/dmesg, which is world-readable on default DragonFly. Content is residue of prior completion processing on the cambio thread (high-bit bytes observed, consistent with pointer fragments). Same trigger family as DF-5332 (evil-device); also reachable at any later re-probe (rescan/hot-plug), not just boot.", "recommended_fix": "Initialize announce_buf[0]=\\0 at declaration in chdone()s CH_CCB_PROBE case (scsi_ch.c:541-543)", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Applied fix.diff (announce_buf[0]=\\0 up front, same rebuilt kernel as DF-5332s fix) and re-ran the exact same choreography 3 times on the patched kernel: every INTERMED attach is followed by NO announce block and NO garbage ch0: <bytes> line (announce_buf[0] is unconditionally written before any read, so xpt_announce_periph is never reached from the failed-probe path). Vulnerable baseline: 3/4 cycles printed garbage (one cycle hit a zero first byte, matching the branch condition).", "fix_evidence": "findings/poc/DF-5333/fixedA.log (patched run: INTERMED marker is the last line, no announce after); findings/poc/DF-5333/run.log + run.2.log (vulnerable baseline with three distinct garbage strings)" } |