⬢ DragonFlyBSD Kernel Audit
DF-2425 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2425",
  "status": "reproduced",
  "reproduced": true,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "The unchecked pcsio->cdb_len bcopy into CmdBlock[12] (amd.c:400) is a real, deterministic SRB heap overflow: cdb_len (u_int8_t, copied verbatim from the CAMIOCOMMAND payload by xpt_merge_ccb, cam_xpt.c:3888 \u2014 no validation anywhere in the chain) writes up to 255 bytes, and bytes CmdBlock[16..24) land exactly on amd_srb.pccb with the fully user-controlled csio.msg_ptr as their source. Verified on the production amd.c source (KLD rig, driver code verbatim): amdexecutesrb dereferences the forged pointer immediately (amd.c:287-288) \u2014 forged 0x414141414141 gave Fatal trap 12, fault 0x4141414141c9 (attacker bytes + 0x88), stopped at amdexecutesrb+0x12 \u2014 and amd.c:355 writes status |= CAM_SIM_QUEUED through the forged pointer (write-what-where-class OR primitive, byte-exact SRB smash: dmamap=0x00c0ffee005a1337, pSGlist=0x1234abcd). The real driver attaches to QEMU 7.2s emulated am53c974 (amd0 at pci0:3.0, same PCI ID 0x1022:0x2020), but the XPT scan wedges forever in the emulation (no driver timeout handler, amd.c:356), so the end-to-end pass(4) path could not be driven on this guest; the rig (sanctioned for hardware-absent drivers) drove the real amd_action instead. Honest reachability: non-root users cannot trigger (tested: /dev/pass0 is root:operator 0600, EACCES as user); kernel-issued CDBs never exceed 16 bytes (da(4) READ_16s cdb_len=16 writes only 4 bytes of inter-field padding); and the driver must be attached (AMD 53C974/Tekram DC-390 PCI HBA, compiled into GENERIC but dormant without the card). So the practical attacker is root on HBA-equipped hardware \u2014 realistic severity Low-Medium vs the filed High (PR:L does not hold); uid0 escalation was not pursued because the trigger is already root-gated. Fix (reject cdb_len > sizeof(CmdBlock) with CAM_REQ_INVALID) validated at module level AND on a full nativekernel rebuild+reboot: identical inputs now reject cleanly (func=2305 status=0x6), no write, no panic, valid cdb_len=6 commands unaffected.",
  "exploit_chain": "root with /dev/passN open on a machine with an attached AMD 53C974 HBA -> CAMIOCOMMAND with XPT_SCSI_IO, CAM_DIR_NONE, cdb_len=255, cdb_bytes pattern, csio.msg_ptr=<target kernel address> -> xpt_merge_ccb copies payload verbatim -> amd_action:400 bcopy overflows CmdBlock[12]; msg_ptr lands on amd_srb.pccb -> amdexecutesrb:287 loads it, :288 dereferences (read at +0x88), :347 reads, :355 OR-writes CAM_SIM_QUEUED at the chosen address -> controlled kernel-address write primitive (plus panic on unmapped target; deeper cdb_len also smashes dmamap/pSGlist consumed on later paths). Not reachable from non-root (pass node 0600) nor without the HBA; uid0 chain N/A (trigger already root).",
  "evidence": "panic.txt (verbatim baseline transcript): Fatal trap 12 ... fault virtual address = 0x4141414141c9 ... Stopped at amdexecutesrb+0x12: movq 0x88(%r13),%r12 preceded by RIGW1 post victim status=0x200 -> WRITE-THROUGH-FORGED-PTR-CONFIRMED and RIGW2 CmdBlock=4c4c.. pccb=victim dmamap=0xc0ffee005a1337 pSGlist=0x1234abcd ScsiCmdLen=255 and RIGS1 sanity; attach-am53c974-boot.log:103 amd0: <Tekram DC390(T)/AMD53c974 SCSI Host Adapter> ... pci0; fix-run2-serial-full.log: RIGDONE: shim saw func=2305 status=0x6 + RIGW2 CmdBlock=0000... pccb=0 dmamap=<valid> + mode-1 no panic on patched kernel #1; env.txt: /dev/pass0 crw------- root:operator",
  "kernel_refs": [
    "sys/dev/disk/amd/amd.c:399",
    "sys/dev/disk/amd/amd.c:400",
    "sys/dev/disk/amd/amd.c:287",
    "sys/dev/disk/amd/amd.c:288",
    "sys/dev/disk/amd/amd.c:355",
    "sys/dev/disk/amd/amd.h:159",
    "sys/bus/cam/cam_xpt.c:3888",
    "sys/bus/cam/scsi/scsi_pass.c:462",
    "sys/bus/cam/cam_ccb.h:603",
    "sys/config/X86_64_GENERIC:88"
  ],
  "poc_changes": "Pass-1 pack did not exist; built from scratch. Rig includes production /usr/src amd.c verbatim with three documented, non-audited-path adaptations: (1) xpt_done shim (records completions of path-less fabricated ccbs, forwards real ones) \u2014 baseline crash/write never reach xpt_done, only the fixed builds reject path does; (2) DRIVER_MODULE stubbed (kernel already owns module pci/amd, kldload would refuse); (3) #define amd amdrig_chip identifier rename for module-scope symbols. Rig bug found during validation: fire mode 3 (rig_reject_case) memsets an SRB still on free_srbs, corrupting the TAILQ \u2014 its panic on the fixed kernel (amd_action+0x335) is a harness artifact, documented; modes 0/1/2 carry the verdict. The userland trigger would be a plain CAMIOCOMMAND ioctl; end-to-end pass(4) drive was impossible on the guest because QEMUs am53c974 emulation wedges the drivers no-timeout XPT scan at boot.",
  "attempts": 4,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #1: Mon Oct 12 08:57:10 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64 (baseline runs: #0 Thu Jul 2 06:02:54 UTC 2026)",
  "runtime_sec": 3120,
  "guest_dirty": 1,
  "build_cmd": "make -C /usr/src/sys/modules/amdrig  (KLD rig including verbatim sys/dev/disk/amd/amd.c); fix: patch -p1 < fix.diff in /usr/src + make -j6 nativekernel KERNCONF=X86_64_GENERIC + make installkernel",
  "run_cmd": "kldload /usr/src/sys/modules/amdrig/amdrig.ko   # write-demo (survives); sysctl dev.amdrig_fire=1  # panic on baseline, clean CAM_REQ_INVALID reject on patched; sysctl dev.amdrig_fire=2  # sanity",
  "code_hash": "1b81d73c574e7a59b07bc42cb38cc3337bf3ffd13efc68e3095c7da78b58de18",
  "notes": "Severity honestly reassessed: filed High with PR:L, but the only >16-byte-cdb issuer is the CAMIOCOMMAND ioctl path gated by /dev/passN root:operator 0600 (non-root EACCES tested live), and amd(4) must be attached (AMD 53c974 PCI HBA; proven attaching under QEMU -device am53c974). Realistic severity Low-Medium (already-privileged trigger + obsolete hardware), code defect certain, primitive strong (controlled kernel-address write + byte-exact pointer control at amd.c:288). Related same-file findings (not re-verified here): DF-1348 is the same sink reported in pass 1; DF-2424 sglist_cnt, DF-2426 div0, DF-2427 reselect. Also observed: driver has NO timeout handler (amd.c:356 #if 0) \u2014 any wedged command hangs the SIM queue forever (this is what froze boot under the QEMU emulation). Guest was reset (vm.sh reset with-src) after evidence collection; guest_dirty=1 reflects that panics + patched kernel occurred during the run.",
  "recommended_fix": "Reject XPT_SCSI_IO with cdb_len > sizeof(pSRB->CmdBlock) before the bcopy (CAM_REQ_INVALID + xpt_done), see fix.diff",
  "fix_status": "fixed",
  "fix_baseline_reproduced": true,
  "fix_patched_reproduced": false,
  "fix_verdict": "fix.diff (clamp cdb_len to sizeof(CmdBlock), reject with CAM_REQ_INVALID) applied to guest /usr/src (Hunk #1 succeeded at 393). Module A/B: identical cdb_len=255 input went from WRITE-THROUGH-FORGED-PTR-CONFIRMED + full SRB smash to RIGDONE shim saw func=2305 status=0x6, CmdBlock zeroed, pccb=0, dmamap=legitimate, no write. Full kernel A/B: make -j6 nativekernel + installkernel + reboot into #1 Mon Oct 12 08:57:10 (sha256 1aecdc66d61423b7fd570f698e4df6a4d6d335c7779b95b30c609248910ee63b) \u2014 the exact input that panicked the baseline (Fatal trap 12 @ amdexecutesrb+0x12, fault 0x4141414141c9) now rejects cleanly and the guest stays up; cdb_len=6 sanity still queues normally (no regression).",
  "fix_evidence": "fix-run2-serial-full.log: RIGP0: forging pccb=0x414141414141 ... followed by RIGDONE: shim saw func=2305 status=0x6 and RIGP1: UNEXPECTED survival (no Fatal trap, guest up; RIGP1 text is the rigs vulnerable-build wording), plus RIGS1: cdb_len=6 returned; waiting_srbs non-empty; contrast panic.txt baseline Fatal trap 12 ... 0x4141414141c9 ... amdexecutesrb+0x12; fix-kernel-build.log: >>> Kernel build for X86_64_GENERIC completed; kernel-patched.sha256"
}