⬢ DragonFlyBSD Kernel Audit
DF-5272 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-5272",
  "status": "reproduced",
  "reproduced": true,
  "impact": "leak:20 bytes/call of uninitialized kernel heap to a privileged reader, unlimited calls; demonstrated disclosure of a live kernel sysctl OID name string fragment (...ilcd\\0spray_leak\\0) and of stable allocator/object remnants (ffffffff...01000000) across 64/64 ioctls x 3 rounds; evil-device-gated (same model as DF-0989, distinct path and mechanism: short transfer + device-inflated data_len clamp on a non-M_ZERO kmalloc).",
  "confidence": "certain",
  "verdict": "REPRODUCED on the stock guest kernel: the evil device completes READ SUBCHANNEL with a 4-byte short transfer while its returned header claims data_len=0x0014; cam_periph_runccb treats CAM_REQ_CMP with resid!=0 as success, cdioctl clamps the copyout length with the device-supplied data_len field (min(24, 0x14+4)=24) and copyouts 24 bytes of a kmallocd-without-M_ZERO buffer, so 20 uninitialized kernel-heap bytes reach userspace on every call. Plain rounds leaked the same stable kernel remnant 64/64 times; an in-process marker phase (alloc+mark+kfree of 24-byte chunks via sysctl between ioctls, no fork) leaked the kernels own sysctl OID name string spray_leak tail, proving live kernel heap data crosses the boundary. Patched kernel (M_ZERO) returns all-zero tail bytes on every run.",
  "exploit_chain": "evil CD-ROM + privileged open -> CDIOCREADSUBCHANNEL(data_len=24) -> kmalloc(24, M_SCSICD, M_WAITOK) without M_ZERO -> device DMAs only the 4-byte header with data_len=0x0014 -> success with resid=20 (unchecked) -> len=min(24, 20+4)=24 -> copyout of 20 uninitialized heap bytes -> repeatable heap-state sampling adjacent to the DF-5271 write primitive (reconnaissance).",
  "evidence": "run.log phase-1 lines [poc] 64/64 runs leaked non-zero heap bytes x3; phase-2 lines bytes[0..23] = 00110014ffffffff696c63640073707261795f6c65616b00 + uninit[4..23] = ....ilcd.spray_leak. (00 11 00 14 is the device header; the rest is leaked kernel heap containing the sysctl OID name string). Patched run: tail all-zero 64/64.",
  "kernel_refs": [
    "sys/bus/cam/scsi/scsi_cd.c:2141",
    "sys/bus/cam/scsi/scsi_cd.c:2148-2158",
    "sys/bus/cam/scsi/scsi_cd.c:2163-2164",
    "sys/bus/cam/scsi/scsi_cd.c:2174-2176",
    "sys/bus/cam/scsi/scsi_cd.c:2178",
    "sys/bus/cam/cam_periph.c:962-963"
  ],
  "poc_changes": "Built the short-transfer response into the shared evilcd SIM (armed>=2: 4-byte DMA + data_len field 0x0014 + resid=dxfer-4). Marker phase needed allocator-aware rigs: freed-chunk markers must avoid bytes [0..7] (SLChunk.c_Next linkage - overwriting them caused a BADFREE2 panic, preserved in notes), and the marker spray must run in-process (sysctlbyname from the PoC between ioctls, no fork) because fork/exec noise reallocates the recycled chunk.",
  "attempts": 4,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64",
  "runtime_sec": 900,
  "guest_dirty": 0,
  "build_cmd": "cd /root/df5271 && make && cc -O -o poc_leak poc_leak.c (see build.sh)",
  "run_cmd": "sysctl debug.evilcd.armed=2; usched :1 ./poc_leak /dev/cd1; usched :1 ./poc_leak /dev/cd1 $(sysctl -n debug.evilcd.kptr) mark (see run.sh)",
  "code_hash": "sha256:poc_leak.c=ead09eebcf5fbad5",
  "notes": "Distinction from known DF-0989: that finding is cdreaddvdstructure() length-arithmetic underflow (DVDIOCREADSTRUCTURE); this is CDIOCREADSUBCHANNELs non-zeroed allocation + device-controlled copyout clamp + unchecked short transfer - different function, different mechanism, same ioctl family. cdreportkey() contrasts: it kmallocs with M_ZERO.",
  "recommended_fix": "kmalloc the subchannel buffer with M_ZERO (one-word fix; fix.diff validated together with DF-5271s fix in a single rebuilt kernel).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Single-fix kernel (M_ZERO added, built+installed+rebooted together with DF-5271s fix): identical PoC returns bytes[4..23] all zero on every run (64/64), no heap remnants, no marker hits; guest stays up.",
  "fix_evidence": "run.patched.log / fix_run.log: zero tail bytes 64/64 on the patched kernel vs. leaked remnants 64/64 on baseline."
}