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)
PoC verification
Evidence pack
findings/poc/DF-2267 Β· 3 files| File | Type | Description | Size | |
|---|---|---|---|---|
| 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
fixedConfirmed 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.
No comments yet.