⬢ DragonFlyBSD Kernel Audit
DF-4557 / verdict.json
← back to finding ↓ download raw
{
  "finding_id": "DF-4557",
  "status": "reproduced",
  "reproduced": 1,
  "impact": "panic",
  "confidence": "certain",
  "verdict": "Unbounded ccb_h->target_id indexes softc->atadev[2] in atapi_action XPT_SCSI_IO (:490,:615,:618) and XPT_PATH_INQ (:385,:391); tid=5 deterministically reads state_lock.lk_lockholder (= curthread while the lock is held at :482, non-NULL by construction), whose ->dev (first qword of struct thread, a NULL queue link) becomes request->dev=NULL -> ata_queue_request -> device_get_parent(NULL) -> supervisor read fault at VA 0x28. Reproduced live: camcontrol rescan 1:5:0 (XPT_SCAN_LUN is whitelisted in xptioctl cam_xpt.c:1052; xpt_compile_path does not bound target_id cam_xpt.c:3993) panicked the guest with exactly that signature. Trigger is root-only (/dev/xpt0 is 0600 root:operator), so impact ceiling is privileged panic/DoS, not uid0. fix.diff (bounds-check tid 0..1, wildcard-exempt in PATH_INQ) build-validated (nativekernel RC=0) and behavior-validated on guest: patched kernel returns CAM_DEV_NOT_THERE (0x8) for tids 2,3,4,5,6,9,14, legit 1:0:0 still rescans, guest stays up, boot/wildcard PATH_INQ unaffected.",
  "exploit_chain": "root opens /dev/xpt0 -> XPT_SCAN_LUN on (scbus1,tid=5,lun0) -> probe XPT_SCSI_IO -> atadev[5]=state_lock.lk_lockholder=curthread -> ->dev=NULL -> device_get_parent(NULL) -> trap 12 page fault (panic). Escalation beyond panic not applicable: trigger already requires root.",
  "evidence": "panic.txt (Fatal trap 12, VA=0x28, Stopped at device_get_parent, process=camcontrol); run.log (baseline: 1:0:0 OK then 1:5:0 kills guest); run.2.log (patched: error 0x8 for all OOB tids, STILL_ALIVE); VERDICT.md (full chain + fix validation); fix.diff (3 hunks, git apply --check OK)",
  "kernel_refs": [
    "sys/dev/disk/nata/atapi-cam.c:490",
    "sys/dev/disk/nata/atapi-cam.c:615",
    "sys/dev/disk/nata/atapi-cam.c:618",
    "sys/dev/disk/nata/atapi-cam.c:385",
    "sys/dev/disk/nata/atapi-cam.c:391",
    "sys/dev/disk/nata/atapi-cam.c:73",
    "sys/bus/cam/cam_xpt.c:3993",
    "sys/bus/cam/cam_xpt.c:1052",
    "sys/sys/lock.h:67"
  ],
  "poc_changes": "No code PoC needed: the stock base-system camcontrol binary is the trigger (libcam/userland headers absent on guest; a hand-rolled CCB program would add nothing). run.sh encodes baseline+trigger; note csh used for guest-side scripts.",
  "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": 3120,
  "guest_dirty": 0,
  "build_cmd": "none (stock /sbin/camcontrol); fix kernel: cd /usr/src && make -j8 nativekernel KERNCONF=X86_64_GENERIC && make installkernel KERNCONF=X86_64_GENERIC",
  "run_cmd": "camcontrol rescan 1:0:0; camcontrol rescan 1:5:0",
  "code_hash": "f8ce411b79827da03da4b27b1bc37b2fce3a54d3b1bf0ef0bda130c9f7fedb62",
  "fix_status": "fixed",
  "fix_baseline_reproduced": 1,
  "fix_patched_reproduced": 0,
  "fix_verdict": "Patched kernel (X86_64_GENERIC #1 Mon Sep 21 13:22:39 2026, fix.diff applied via patch -p1, nativekernel RC=0) re-ran the exact PoC: camcontrol rescan 1:5:0 (and tids 2,3,4,6,9,14) now returns error 0x8 CAM_DEV_NOT_THERE with RC=1, no panic, guest up; legitimate rescan 1:0:0 still succeeds; 3x repeat stable.",
  "fix_evidence": "findings/poc/DF-4557/run.2.log (patched behavior), /root/build.log on guest had BUILD_DONE_RC=0, fix.diff in pack (git apply --check OK)",
  "recommended_fix": "Bounds-check ccb_h->target_id against NELEM(softc->atadev) in XPT_SCSI_IO, XPT_PATH_INQ (wildcard-exempt) and XPT_RESET_DEV before indexing atadev[]; return CAM_DEV_NOT_THERE",
  "notes": "Guest reset to clean with-src snapshot after validation. Distinct from DF-1695/DF-2501 (XPT_RESET_DEV only): these two paths have NULL checks but no bounds check, and tid=5s slot (lk_lockholder) is non-NULL by construction while the lock is held."
}