DF-4241 / verdict.json
{ "finding_id": "DF-4241", "status": "reproduced", "reproduced": 1, "impact": "panic", "confidence": "certain", "verdict": "REPRODUCED with the full production path: a real xpt_alloc_ccbd XPT_SCSI_IO queued through the production dpt_action/dptexecuteccb (shipped kernel image) leaves dccb X in flight; the devices first EATA status packet completes it cleanly (comp=1, CAM_REQ_CMP, freed once). A byte-identical DUPLICATE packet (device-fed sp->ccb_busaddr, only range-checked at dpt_scsi.c:1518-1527) makes production dpt_intr re-complete X: dptfreeccb inserts X into free_dccb_list a second time -> SELFLOOP=1 (head->next==head, unbounded walk, free_dccbs inflated +1); two subsequent production XPT_SCSI_IOs then BOTH pop the same dccb (SHARED=1 -- two live CCBs on one command block, the shared-object UAF); and the second xpt_done(ccb) re-enqueues the completed CAM ccb on sim_doneq, where camisr_runqueue panics: Bad link elm 0xfffff801185f0ec0 prev->next != elm -- exactly the CCB queued at step 1 (QUEUE id=0 ccb=0xfffff801185f0ec0). System down in DDB. FIX VALIDATED on a rebuilt kernel: the added DCCB_ACTIVE guard prints dpt9: status packet for inactive CCB ignored, the free list stays healthy (SELFLOOP=0, dups=1), subsequent queues get DIFFERENT dccbs (SHARED=0), no panic (dupalias returns to userland), and the legitimate completion path still works.", "exploit_chain": "duplicate/fabricated device status packet (in-range ccb_busaddr, no in-flight check) -> double dptfreeccb -> free-list self-loop -> shared dccb across two in-flight CCBs (UAF-family aliasing; each later completion frees/recycles the others command block) -> double xpt_done on a CAM-owned ccb -> sim_doneq list corruption -> panic in camisr_runqueue (kernel down). On a hostile-device threat model the double xpt_done is a use-after-free of a recycled CAM ccb -- the demonstrated crash is the primitive detonating, not its ceiling.", "evidence": "run.log (serial console: QUEUE id=0 ccb=0xfffff801185f0ec0 ... DUP post SELFLOOP=1 dups=200 walk=200 ctr=1 ... ALIAS SHARED=1 ... panic: Bad link elm 0xfffff801185f0ec0 in camisr_runqueue+0x574 / swi_cambio); panic.txt (panic block); run.2.log (first, serendipitous reproduction in an earlier boot); fix_validation.log (inactive-CCB-ignored + SELFLOOP=0 + SHARED=0 + no panic)", "kernel_refs": [ "sys/dev/raid/dpt/dpt_scsi.c:1518", "sys/dev/raid/dpt/dpt_scsi.c:1525", "sys/dev/raid/dpt/dpt_scsi.c:1527", "sys/dev/raid/dpt/dpt_scsi.c:1571", "sys/dev/raid/dpt/dpt_scsi.c:1604", "sys/dev/raid/dpt/dpt_scsi.c:1605", "sys/dev/raid/dpt/dpt_scsi.c:238", "sys/dev/raid/dpt/dpt_scsi.c:252", "sys/dev/raid/dpt/dpt_scsi.c:1696", "sys/dev/raid/dpt/dpt.h:523" ], "poc_changes": "No seed PoC existed. Built the production-handler dispatch rig (real cam_sim with production dpt_action/dpt_poll over a fabricated post-attach softc with real busdma and a production-dptallocccbs-built free list; real CCBs via xpt_action; status packets DMA-written by the emulated device). Added a dupalias ioctl that performs the duplicate completion, the free-list capture, and two follow-up queues under one lock hold so the evidence is captured before the CAM SWI detonates.", "attempts": 3, "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": 1800, "guest_dirty": 0, "build_cmd": "cd /root/rig && make && cc -O2 -Wall -o dfdpt_ctl dfdpt_ctl.c && kldload ./dfdpt_rig.ko && ./dfdpt_ctl prime", "run_cmd": "./dfdpt_ctl queue 60000 && ./dfdpt_ctl intr 0 1 0 0 && ./dfdpt_ctl snap 0 && ./dfdpt_ctl dupalias 0 (panics)", "code_hash": "93d90103a5d6faf59e478ac7d74fbb3fcd04cb6fb799c747dc9e515eedf1d4ee", "notes": "Impact panic = the demonstrated terminal crash; the underlying class is kernel UAF (shared dccb + double xpt_done of a CAM-owned ccb) -- panic is the primitive detonating in the CAM completion path. The SMP variant (dpttimeout racing the hardirq, dpt_scsi.c:1696) shares the root cause (no in-flight check) and is covered by the same fix. dpt_intr was invoked from an ioctl thread rather than a hardirq -- immaterial: the corruption is inside dpt_intr and the detonation occurred on the CAM SWI thread as it would from a real interrupt. Guest reset clean afterwards.", "recommended_fix": "Reject status packets whose dccb is not DCCB_ACTIVE (clear IRQ and continue), as in fix.diff.", "fix_status": "fixed", "fix_baseline_reproduced": 1, "fix_patched_reproduced": 0, "fix_verdict": "Validated on the rebuilt kernel: duplicate packet rejected by the DCCB_ACTIVE guard (status packet for inactive CCB ignored), free list intact (no self-loop, counter correct), subsequent queues get distinct dccbs, no panic, and the normal completion path (comp=1 / CAM_REQ_CMP) unaffected.", "fix_evidence": "fix_validation.log" } |