⬢ DragonFlyBSD Kernel Audit
DF-4349 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4349",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "REPRODUCED against the shipped kernel binary via production-handler KLD dispatch: advansys.c is compiled into /boot/kernel/kernel, so the rig dispatches the real adv_intr()/adv_done() (nm-resolved, no KASLR) over a fabricated post-attach softc (real busdma, production non-zeroed ccb_infos kmalloc) with the ASC LRAM emulated byte-exactly (x86 watchpoint #DB service; self-test proves the production accessors round-trip every crafted field). Facet A: a duplicated done-queue entry naming an already-completed slot drives a second production adv_done - panic Bad link elm prev->next != elm with a double adv_done frame under adv_intr (the stale LIST_REMOVE at advansys.c:1127; the INVARIANTS queue guard converts the filed silent list corruption into a deterministic kernel crash on this debug kernel, and the recycled-slot variant still corrupts silently: live callout stopped, live dmamap unloaded, slot double-returned, early completion -> double-xpt_done family). Facet B: a done entry naming an in-bounds never-allocated slot writes through groomed heap garbage - Fatal trap 12, fault address exactly 0x41df41df42f3, supervisor WRITE, stopped at adv_intr+0x101: movl %edi,0x114(%rsi), which is the ccb->csio.resid store of advansys.c:1108. Control run completes a legit entry cleanly, so only the crafted device bytes differ. Fix validated on a really rebuilt kernel (fix.diff applied in guest /usr/src, nativekernel+installkernel): both facets dropped, no panic, control still works, guest alive. Device-fed trigger (buggy/hostile firmware or corrupt LRAM) - no unprivileged-user or remote path; Medium severity confirmed.",
  "exploit_chain": "Crafted device done-queue entry (duplicated ccb_index, or in-bounds index >= ccb_infos_allocated) -> production adv_run_doneq maps it to a kernel object with no allocation/state check (advansys.c:1106-1109) -> facet A: second adv_done on a stale ccb = stale LIST_REMOVE (kernel list corruption; deterministic panic on INVARIANTS kernels), callout_stop/dmamap_unload on recycled live resources, second xpt_done (CAM UAF family); facet B: write through uninitialized cinfo->ccb (kernel-memory write at a heap-groomed address, demonstrated fault at 0x41df41df42f3). Impact ceiling on this rig: kernel panic from device-fed input; escalation to controlled kernel write requires physical AdvanSys HBA + firmware/hostile-peripheral control of the done-queue bytes.",
  "evidence": "panic.facetA.txt (double adv_done backtrace), panic.facetB.txt (trap 12 at 0x41df41df42f3, supervisor write, stopped at adv_intr+0x101 movl %edi,0x114(%rsi)), run.baseline.log (self-test PASS + control completion + facetA panic), run.forensic.log (recycled-slot silent enumeration + stale-variant guard panic), run.bothfixed.log (patched kernel: drops, no panic, budget-bounded cycle), build_fixed.sh + fix.diff",
  "kernel_refs": [
    "sys/dev/disk/advansys/advansys.c:1106",
    "sys/dev/disk/advansys/advansys.c:1108",
    "sys/dev/disk/advansys/advansys.c:1109",
    "sys/dev/disk/advansys/advansys.c:1127",
    "sys/dev/disk/advansys/advansys.c:1261",
    "sys/dev/disk/advansys/advansys.c:1266",
    "sys/dev/disk/advansys/advansys.c:1296",
    "sys/dev/disk/advansys/advansys.c:642",
    "sys/dev/disk/advansys/advlib.c:808"
  ],
  "poc_changes": "Replaced the seed userspace model with a production-handler KLD dispatch rig: fabricated post-attach softc with real bus_dma tag/dmamaps, production-exact ccb_infos kmalloc (M_DEVBUF, no M_ZERO) with deterministic heap grooming, ASC chip emulation (flat register page + LRAM latch/auto-increment protocol via x86 #DB watchpoints with a temporary IDT[1] gate), nm-resolved dispatch of the kernels own adv_intr/adv_done/adv_copy_lram_doneq, sysctl-driven scenarios with control/selftest/forensic isolation, and real rebuilt-kernel fix validation.",
  "attempts": 6,
  "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 (6 cpus, 4GB)",
  "runtime_sec": 4800,
  "guest_dirty": 0,
  "build_cmd": "vm.sh run_root sh /root/df43xx/build.sh (make -m /usr/src/share/mk; KLD df43xx_rig.ko)",
  "run_cmd": "vm.sh run_root sh /root/df43xx/prime.sh && sysctl -w kern.df43xx.go=2 (0=selftest 1=control 2=facetA 3=facetB 5=forensic)",
  "code_hash": "9c695ff5010595bb6fa4e6bf63389821b2095410373f736efd3579b8e6ca9444",
  "notes": "Shipped kernel has INVARIANTS: DFlys LIST_REMOVE sanity check (sys/sys/queue.h:392-397) converts the stale-removal into the observed deterministic panic; on release builds the same production path silently corrupts pending_ccbs as originally filed - the recycled-slot forensic variant (5b) executes the silent sequence even on this kernel. Facet Bs wild write is BEFORE any list op and is INVARIANTS-independent - reproduced at the exact groomed address. Rig pitfalls solved: bounce-zone SYSCTL_ADD_NODE inside a sysctl write handler deadlocks the oid tree (pre-create at MOD_LOAD); bounce-pool lowaddr must allow fast contigmalloc (a low ceiling wedges vm_contig under the vm token). The bpages-based observation of the recycled-dmamap unload printed 0/0 (inconclusive); the callout/slot/status legs of the recycled-slot enumeration are directly printed. Guest reset to pristine with-src snapshot and left up.",
  "recommended_fix": "In adv_run_doneq reject ccb_index >= adv->ccb_infos_allocated or !(state & ACCB_ACTIVE) before completing (re-advance the walk and continue); kmalloc ccb_infos with M_ZERO as defense in depth.",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_kernel_uname": "DragonFly dfbsd 6.5-DEVELOPMENT #2: Thu Oct 15 14:56:52 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC (both fixes) / #3 15:11 (4349-only)",
  "fix_verdict": "fix.diff (predicate + M_ZERO) applied in guest /usr/src with DF-4350s budget fix, nativekernel+installkernel, reboot. Baseline kernel: go=2 panic (double adv_done), go=3 trap 12 at 0x41df41df42f3. Patched kernel: go=2 -> adv9: done queue ccb_index 0 not active, dropping completion (guest alive), go=3 dropped, go=1 control completion still correct, go=4 cycle bounded (276-entry budget). Identical PoC bytes both runs.",
  "fix_evidence": "run.bothfixed.log: patched-kernel boot (#2), scenario matrix RC=0 with dropping completion messages and done queue walk exceeded 276 entries, stopping then adv_intr RETURNED after 4273 data-port services; panic.facetA.txt / panic.facetB.txt for the baseline reproductions"
}