β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-2267

CAMGETPASSTHRU can spin forever holding the SIM lock (privileged local DoS)

Summary

CAMGETPASSTHRU handler uses two nested loops: inner while(status==CAM_GDEVLIST_MORE_DEVS) outer while(found==0). Outer loop only exits when status==CAM_GDEVLIST_LAST_DEVICE or pass periph matched. If xpt_action returns CAM_GDEVLIST_ERROR or CAM_GDEVLIST_LIST_CHANGED on every iteration (happens when device periph list empty/mutated concurrently) outer loop spins forever. Every caller of cam_periph_ioctl (scsi_pass scsi_cd scsi_ch scsi_sa scsi_pt scsi_ses) holds cam_periph_lock (SIM lock) across the call so spin wedges all I/O on that SIM not just calling device. Local DoS requiring SYSCAP_RESTRICTEDROOT plus concurrent operation mutating periph list (bus rescan USB unplug devd rescan). Most reliable trigger: LUN whose last periph detached between index increments xpt returns ERROR outer while never exits. [VERIFIED pass2: reproduced on real code - empty periph list spins forever with SIM-lock NOWAIT probe EBUSY on all 60 watchdog samples; practical trigger needs sustained churn, honest caveat recorded; terminate-plus-bound fix-validated ioctl returns]

Discussion (0)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-2267 Β· 3 files
FileTypeDescriptionSize
spin_console_flood.txt run-log 60+ watchdog samples: spin still running, lock held (rc=16) 6.3 KB view raw
../DF-2261/rig/dfcam_harness.c trigger-source fabricated periph + spin kthread + watchdog 19.9 KB view raw
fix.diff suggested-fix terminate on ERROR + bound restarts 1.9 KB view raw

Fix verification

fixed
↓ fix.diffper-fix-DF-2267

Confirmed kernel references

Detail

Exploit chain

privileged ioctl on pass/ses/ch/cd/sa/pt with concurrent periph churn -> CAM_GDEVLIST_ERROR/CHANGED every iteration -> unbounded outer loop holding SIM lock -> all I/O on that SIM wedged

Evidence (decisive lines)

spin_console_flood.txt: repeated DF67 spin still running + SIM-lock NOWAIT probe rc=16 LOCK STILL HELD; control return in stage_05_2266_leak.log

PoC changes

Authored fabricated-periph rig; first attempt crashed (fabricated CCB lacked ccb_h.path for xpt_setup_ccb) -- fixed by presetting path on the pooled CCB

Verified recommended fix

exit the walk on CAM_GDEVLIST_ERROR and cap LIST_CHANGED restarts

Verdict

REAL cam_periph_ioctl(CAMGETPASSTHRU) executed on a device whose periph list is empty (verbatim-extracted cam_ed layout) never returns: the outer while(found==0) loop only exits on CAM_GDEVLIST_LAST_DEVICE or a pass match while xpt_action(XPT_GDEVLIST) returns ERROR forever; the spinning thread held the SIM lock for the entire observation window (LK_NOWAIT probe EBUSY/rc=16 on all 60+ five-second samples) while burning CPU. Control (valid list) returns immediately. Practical triggering on a real device requires sustained concurrent periph-list mutation (single LIST_CHANGED events merely restart the walk) or an independent lifecycle bug; privileged-only gate. Fix (ERROR terminates + restart bound) validated by A/B. A/B: Fixed kernel: same fabricated empty-list state makes cam_periph_ioctl RETURN promptly; SIM-lock NOWAIT probe rc=0 (free) versus baseline never-returns with rc=16 (held) on 60+ samples.