DF-4557 / verdict.json
{ "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." } |