⬢ DragonFlyBSD Kernel Audit
DF-2427 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-2427",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Fully reproduced on the verbatim driver through the real amd_intr() -> amd_Reselect() interrupt path. A reselection byte XORing with HostID_Bit to zero yields cur_target = -1 (signed int, amd.h:204) and every per-target array index goes out of bounds exactly as claimed: tinfo[-1] spans softc+556..567 (tail of untagged_srbs[7][6..7]), disc_count[-1][lun] lands in the tinfo[] tail (tinfo[6].goal .. tinfo[7].user depending on lun, including sync_period_reg/sync_offset_reg at lun=4), and untagged_srbs[-1][0] is 8 bytes BEFORE the softc object. Demonstrated primitives: OOB read of softc-8 loaded as the kernel active_srb pointer (wild pointer under device influence), OOB 8-byte NULL write at softc-8, OOB 16-bit decrement in the tinfo tail, OOB tinfo[-1] bytes programmed into chip config registers, a 2-byte SRB_DISCONNECT write through the wild active_srb into a victim object (write-at-address-taken-from-OOB-memory), field-alias variant zeroing cur_target/cur_lun with a non-canonical 0x00000006FFFFFFFF active_srb, and a final Fatal trap 12 supervisor write at 0x70000016f = wild+offsetof(SRBState) inside amd_MsgInPhase0. The multi-bit variant (byte 0xA8 from a device at ID 5) decodes to target 3 and binds the reselection nexus to target 3 SRB (cross-target confused deputy). Attacker class is a malicious or signal-garbled SCSI peripheral on an AMD53c974 bus - hardware attacker; no software-only or unprivileged-local route exists (guest has no such HBA). On hosts with the HBA the demonstrated chain (groomable 8 bytes before the softc -> forged active_srb -> fixed-offset stores of FIFO-chosen bytes through MSG IN/STATUS handlers, plus TAILQ ops in SRBdone) is a plausible path to arbitrary kernel write and host uid=0, but a local unpriv user cannot trigger it; realistic ceiling: peripheral-driven kernel memory corruption / cross-target data diversion / guaranteed panic.",
  "exploit_chain": "malicious SCSI target drives reselection with ID byte 0x80 (initiator bit only) -> amd_intr RESELECTED -> amd_Reselect: xor 0x80^0x80=0 -> ffs(0)-1 -> cur_target=-1 -> active_srb = *(softc-8) (OOB read of heap bytes preceding the softc; groomable) -> disc_count[-1][0]-- corrupts tinfo[6].goal; untagged_srbs[-1][0]=NULL zeroes softc-8..-1; chip regs loaded from tinfo[-1] -> target then drives MSG IN (MSG_DISCONNECT): amdparsemsg writes SRB_STATE at forged_ptr+0x170 (2-byte store of a chosen constant; StatusPhase0 similarly stores FIFO-chosen bytes at fixed offsets) -> repeated arbitrary-offset corruption or, ungroomed, Fatal trap 12 supervisor write (captured). Multi-bit byte 0xA8 (attacker at ID 5) instead decodes target 3 and steals target 3 SRB nexus (cross-target data diversion).",
  "evidence": "run_battery1_clean.log: layout table (offsetof-verified alias map) + scenario A/A2/B/C with 0 checks failed - PASS lines for the OOB read at softc-8, the 8-byte NULL write at softc-8, the tinfo[6].goal 0x0000->0xFFFF decrement, chip regs DESTID=ff SYNCPERIOD=77 SYNCOFF=66 C1=aa C3=99 C4=88 loaded from tinfo[-1], and the SRB_DISCONNECT write through the wild active_srb into the victim. panic_2427.txt: Fatal trap 12 page fault, fault virtual address 0x70000016f, supervisor write data, Stopped at amd_MsgInPhase0+0xae: movw %si,0x170(%rax). run_fixed_dmesg.log: fixed kernel prints amd0: Invalid reselection ID, resetting bus for every poison byte, canary rings and tinfo tail untouched, valid reselect still binds, 0 checks failed, no fault.",
  "kernel_refs": ["sys/dev/disk/amd/amd.c:1831", "sys/dev/disk/amd/amd.c:1833", "sys/dev/disk/amd/amd.c:1834", "sys/dev/disk/amd/amd.c:1844", "sys/dev/disk/amd/amd.c:1845", "sys/dev/disk/amd/amd.c:1848", "sys/dev/disk/amd/amd.c:1218", "sys/dev/disk/amd/amd.h:204", "sys/dev/disk/amd/amd.h:207"],
  "poc_changes": "No runnable sketch existed; built the kernel-compiled verbatim rig for amd.c (shared with DF-2426). Key rig elements: bus_space MEM tag + fabricated register block so the verbatim amd_read8/amd_write8 inline bus_space code runs unmodified; only the SCSI FIFO register is shimmed to pop-on-read (real chip FIFO semantics) so the two consecutive SCSIFIFOREG reads at amd.c:1829/1832 return the poisoned reselect byte then the identify byte; canary-ringed softc wrapper (pre[64]/amd/post[64]) to expose any access outside the object; entry through the real amd_intr dispatcher. Iterations: fixed an off-by-one in my tinfo[-1] field-offset check constants, re-set last_phase before the second follow-on MSG IN, and scoped the scenario-A fixed-branch canary check to pre[0..55] (the harness itself pre-loads the victim pointer at pre[56..63]).",
  "attempts": 2,
  "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #6: Mon Oct 19 13:14:51 UTC 2026     root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC  x86_64",
  "runtime_sec": 3900,
  "guest_dirty": 0,
  "build_cmd": "cd findings/poc/DF-2426 && ./build.sh   (shared harness kernel)",
  "run_cmd": "cd findings/poc/DF-2427 && ./run.sh && ../DF-2426/make_fixed.sh && ./run_fixed.sh",
  "code_hash": "0f8d806544a25f3cd427a56538e582a56091350161cb8e421453c7507cdcc5d2",
  "notes": "No apostrophes. Impact recorded as panic because the captured end-state of the uncontained chain is the Fatal trap 12 supervisor write; the battery additionally demonstrates the controlled corruption primitives (write-through-wild-pointer at an address taken from OOB heap bytes; repeated OOB RMW steering tinfo tail bytes; cross-target SRB binding) that a hardware attacker could develop toward arbitrary write on hosts with this HBA. Privesc from a local unprivileged user is NOT reachable (trigger requires wire-level SCSI input on AMD53c974 hardware). fix.diff rejects any xor remainder that is not exactly one bit (kills both the -1 and the multi-bit wrong-target decodes) and resets the bus; A/B validated (baseline kernel #3 corruption, fixed kernel #6 clean, valid-reselect control unchanged). Guest reset with-src after the run.",
  "recommended_fix": "Validate the reselect ID decode in amd_Reselect before any array use: after amd->cur_target ^= amd->HostID_Bit, reject cur_target==0 or multi-bit remainders (reset the bus and return) before ffs()-1 indexes tinfo/untagged_srbs/disc_count."
}