⬢ DragonFlyBSD Kernel Audit
DF-2522 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2522",
  "status": "reproduced",
  "reproduced": true,
  "impact": "leak",
  "confidence": "certain",
  "verdict": "Reproduced with the production handler: kldloadd the real /boot/kernel/trm.ko and dispatched its own trm_Interrupt() over a fabricated post-attach ACB/DCB/SRB (trm.h structs) with the TRM-S1040 register file emulated behind the kernels bus_space accessors. An auto-request-sense completion with csio->sense_len=255 (user-controlled u_int8_t, no CAM/pass clamp) made the production bcopy at trm.c:2529-2530 read 223 bytes past the 32-byte DMA sense slot - past the entire 256-slot sense_buffers array into adjacent kernel heap, byte-proven by pattern provenance (0xA7 exists only beyond the array) - and write those 223 bytes over the live ccb_scsiio tail: sense_len, cdb_len, sglist_cnt, scsi_status (set to 0x02 by trm.c:2528 one instruction earlier, then overwritten), sense_resid, resid, cdb_io.cdb_ptr, msg_ptr, msg_len, tag_action, tag_id, init_id. On this platform sizeof(union ccb)=720 > offsetof(sense_data)+255, so the write stays inside the CCB allocation (intra-object overflow incl. two pointer fields); the read side hands 223 adjacent kernel-heap bytes back to the CAM client via CAM_AUTOSNS_VALID (userland-visible with pass(4)). Guest survives. One-line clamp (imin with sizeof(pcsio->sense_data)) validated on a rebuilt module: 0/223 overflow bytes, all fields intact, legal 32-byte copy preserved.",
  "exploit_chain": "Not pursued beyond the demonstrated primitives: the trigger requires CAM command submission (pass(4) /dev/passN is root/operator) on a machine with the Tekram HBA, so a user->root chain adds nothing; the demonstrated primitives are (a) kernel-heap info leak of 223 bytes/command into the returned CCB and (b) corruption of live CCB fields incl. cdb_io.cdb_ptr and msg_ptr pointers.",
  "evidence": "run.log: CONFIRMED (223/223 bytes) OOB-read marker + clobbered LIVE ccb fields: ... cdb_io.cdb_ptr=0xa7a7... msg_ptr=0xa7a7... ; control run (sense_len=32) in same log shows zero corruption; run.patched.log: ABSENT - COPY WAS CLAMPED (0/223 bytes), scsi_status=0x02 intact; fix.diff; fix_build.log (nativekernel, patched trm.ko md5 ea902f81 vs stock 7211861d).",
  "kernel_refs": [
    "sys/dev/disk/trm/trm.c:2529",
    "sys/dev/disk/trm/trm.c:2511",
    "sys/dev/disk/trm/trm.c:2844",
    "sys/dev/disk/trm/trm.c:2837",
    "sys/dev/disk/trm/trm.c:3458",
    "sys/bus/cam/cam_ccb.h:601",
    "sys/bus/cam/cam_xpt.c:4933"
  ],
  "poc_changes": "No seed PoC existed (empty pack). Built from scratch: KLD rig with zero driver code that kldloads the production /boot/kernel/trm.ko, resolves its inlined trm_SRBdone via trm_Interrupt (kldstat base + nm offset 0x1d40, sysctl-injected), fabricates the driver state per trm.h, emulates the chip registers behind real bus_space MEM accessors, and dispatches.",
  "attempts": 2,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 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": 600,
  "guest_dirty": 0,
  "build_cmd": "cd /root/df252x && make -m /usr/src/share/mk   (kldload trm; kldload ./df252x_trm_rig.ko)",
  "run_cmd": "sh run.sh   (sysctl kern.df252x.addr=<base+0x1d40>; sysctl kern.df252x.go=2 control; go=1 sense_len=255)",
  "code_hash": "75af3495eff0b1836d378d698019b7fd7e88c49a36b8c267c8f8ac3cfe6faa70",
  "notes": "Fix validated on a module carrying all three trm fixes (fix.all.diff in the shared build); this scenario dispatches exclusively through the bcopy hunk. The 0x51/0xA7/0xDB sentinel scheme proves source-destination byte provenance. Incidental: xpt_done is a no-op for the fabricated CCB via func_code=XPT_NOOP (rig hygiene). The REQUEST_SENSE CDB alloc-length write at trm.c:2844 is noted but not itself an overflow (DMA SG-bounded to 32B at trm.c:2838).",
  "recommended_fix": "Clamp the auto-sense copy: bcopy(..., imin(pcsio->sense_len, sizeof(pcsio->sense_data))).",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Rebuilt the driver module from the patched source (make nativekernel KERNCONF=X86_64_GENERIC with fix applied; full log fix_build.log) and re-ran the identical rig against the patched modules own trm_Interrupt: sense_len=255 now copies exactly 32 bytes - ABSENT - COPY WAS CLAMPED (0/223 bytes), all live ccb fields intact (scsi_status=0x02 CHECK_COND preserved, cdb_ptr/msg_ptr zero), and the legal sense bytes still delivered. Baseline bad behavior gone; control semantics unchanged.",
  "fix_evidence": "run.patched.log lines ABSENT - COPY WAS CLAMPED (0/223 bytes) and clobbered LIVE ccb fields: sense_len=0xff cdb_len=0x00 ... scsi_status=0x02 ... msg_ptr=0x0 vs stock run.log CONFIRMED (223/223 bytes); module md5s in env.txt (stock 7211861de4b5473e974ea7f26aae6e79 vs patched ea902f81add1d968180a761ea3a38956)."
}