⬢ DragonFlyBSD Kernel Audit
DF-5332 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5332",
  "status": "reproduced",
  "verdict": "chgetelemstatus() allocates both READ ELEMENT STATUS transfer buffers without M_ZERO (scsi_ch.c:1089, :1129) and never checks csio->resid; an evil medium changer that completes the command GOOD but transfers only the 8-byte status header (legal SCSI; resid>0, CAM_REQ_CMP) leaves the page header and all descriptors as stale kernel heap, which copy_element_status() parses and copyout()s to the CHIOGSTATUS caller. Reproduced deterministically on the QEMU guest with the evilcd3 rig: marked runs returned the exact 0x26 stamp bytes in ces_int_addr/ces_flags/ces_sense*/ces_scsi_id (28 non-zero bytes per run, 3/3 runs), unmarked runs returned natural stale-heap bytes (0xa0cf, 0x805a, 0xff..., 17 non-zero bytes per run, 3/3 runs). Distinct from DF-1028/DF-2271 (those walk PAST the allocation; this discloses uninitialized memory INSIDE it) -- the DF-5272 class in the ch driver. Leak only; no write primitive.",
  "reproduced": true,
  "impact": "leak",
  "confidence": "certain",
  "exploit_chain": "",
  "evidence": "run.log (marked: LEAK CONFIRMED: 84 non-zero bytes across 3 runs, full per-element hexdumps showing 0x26 markers); run.2.log (unmarked: LEAK CONFIRMED: 51 non-zero bytes, natural heap 0xa0cf/0x805a/0xff); leak_sample.txt; dmesg lines in run.log [EVILCH] RES evil short: sea=256 count=4 dxfer=80 SENT=8B only; fixedA.log (patched kernel: all-zero structs, PoC exits 3 while rig still short-transfers, dmesg count SENT=8B = 13)",
  "kernel_refs": [
    "sys/bus/cam/scsi/scsi_ch.c:1089",
    "sys/bus/cam/scsi/scsi_ch.c:1129",
    "sys/bus/cam/scsi/scsi_ch.c:1115-1122",
    "sys/bus/cam/scsi/scsi_ch.c:1156-1188",
    "sys/bus/cam/scsi/scsi_ch.c:984-1051",
    "sys/bus/cam/cam_periph.c:960-969"
  ],
  "poc_changes": "Derived rig from DF-5327s evilcd2.c (kept fake T_CHANGER + SIM skeleton, added READ ELEMENT STATUS emulation, markres heap-stamping knob, invalidate knob); PoC written fresh (CHIOGSTATUS struct from guest <sys/chio.h>); marker bytes constrained to 0x21..0x2F so the stale page-header flags cannot set PVOLTAG/AVOLTAG and drag in the separately-filed OOB-walk class (DF-1028/2271)",
  "attempts": 6,
  "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": 2280,
  "guest_dirty": 1,
  "build_cmd": "cd /root/df5332 && sh build.sh",
  "run_cmd": "cd /root/df5332 && sh run.sh",
  "code_hash": "abe6b65e2d7ddf140e65bd4d5cde8015ee29b493e920dd285e9793fa5b6510f7",
  "notes": "Severity aligned with DF-5272 (Medium, kernleak): /dev/chN is 0600 root:operator, so the direct reader is root/operator, but the disclosed content is arbitrary prior M_DEVBUF-occupant data and the trigger is a fully device-controlled short transfer. desclen from a short buffer #1 (<12B) also feeds the size/stride computation. fix.diff adds M_ZERO to both allocations (matches what FreeBSDs ch driver does).",
  "recommended_fix": "Add M_ZERO to both chgetelemstatus() kmallocs (scsi_ch.c:1089, :1129) so short-but-GOOD transfers parse as zeros",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Applied fix.diff (M_ZERO x2) to the guest /usr/src, rebuilt EVIL kernel (#1 Fri Oct 2 23:30:20 UTC 2026), rebooted, re-ran the exact same PoC: marked and unmarked runs both return 0 non-zero bytes (PoC exits 3 all returned bytes zero) while the rig still performs the identical 8-byte short transfers (dmesg SENT=8B count 13) -- disclosure eliminated, device behaviour unchanged. Baseline on the vulnerable kernel: 84 (marked) + 51 (unmarked) non-zero bytes leaked.",
  "fix_evidence": "findings/poc/DF-5332/fixedA.log (patched-kernel run: all-zero hexdumps, MARKED_RC=0/UNMARKED_RC=3); findings/poc/DF-5332/run.log + run.2.log (vulnerable baseline)"
}