Lock-order inversion sc->sc_lock <-> evdev ev_mtx: ums_intr_callback pushes evdev events under sc_lock while evdev open/close/detach call ums_ev_open/ums_ev_close (taking sc_lock) under the evdev lock -> permanent unkillable deadlock
Summary
Data path sc_lock->ev_mtx vs control path ev_mtx->sc_lock (cdev open/close -> evdev -> ums_ev_open/ums_ev_close): two sleepable lockmgr locks in opposite orders - interrupt report crossing an open()/close() wedges both forever; opener unkillable (no PCATCH) and the device callback thread wedges holding sc_lock. REPRODUCED 2/2 stock runs (open/close loop under stream froze at 21,200 and 1,400 opens; ps D1 'ums lock'; kill -9 ineffective). Routine trigger: any privileged input consumer (X11/libinput on VT switch/resume/re-enumeration). evdev_register_mtx(sc_lock) fix validated: 171,473 cycles clean, monotonic, killable.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5369 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.5 KB | β raw | |
| VERDICT.md | β | 3.6 KB | β raw | |
| ocprobe.py | β | 2.0 KB | view raw | |
| run.sh | β | 186 B | view raw | |
| build.sh | β | 161 B | view raw | |
| run.log | β | 590 B | view raw | |
| fix_run.log | β | 602 B | view raw | |
| fix.diff | β | 638 B | view raw | |
| ps_forensic.txt | β | 336 B | view raw | |
| env.txt | β | 339 B | view raw | |
| sha256sums.txt | β | 77 B | view raw |
DF-5369 β ums sc_lock <-> evdev ev_mtx lock-order inversion (unkillable open deadlock)
What
ums_intr_callback() runs holding sc->sc_lock (the xfer priv lock,
ums.c:606-608) and calls ums_evdev_push() (ums.c:363, also
ums.c:207 from the SBU callout) which takes the evdev state lock
(ev_mtx, because attach uses evdev_register(), ums.c:743 ->
evdev.c:345-347). Lock order A: sc_lock -> ev_mtx.
evdev_register_client() (evdev.c:984-991, from cdev open,
cdev.c:132 under EVDEV_LOCK) and evdev_unregister() ->
evdev_dispose_client() (evdev.c:375-388, evdev.c:1004-1008,
detach) call the driver methods ums_ev_open()/ums_ev_close()
which take sc->sc_lock (ums.c:964, ums.c:983). Lock order B:
ev_mtx -> sc_lock.
Two sleepable lockmgr locks acquired in opposite orders -> classic
AB-BA. When an interrupt report and an open()/close() (or detach) hit
the Β΅s-crossing simultaneously, BOTH sides block forever: the opener is
stuck in an unkillable D-state (lockmgr sleep has no PCATCH), the
USB callback thread is wedged holding sc_lock, and the input device
is dead.
Reproduced (2/2 stock runs)
# stock module, root open/close loop of the ums0 evdev node while # the QEMU mouse streams (~90 reports/s): t= 5s opens=6200 t=10s opens=12600 t=15s opens=19200 t=20s opens=21200 <- froze; stream still flowing ... (counter never moves again) kill check: pids=31041 ; kill -9 ineffective ; loop never exits # ps forensics: PID STAT WCHAN COMMAND 31041 D1 ums lock /bin/sh /tmp/ocloop.sh
WCHAN "ums lock" is sc->sc_lock (lockinit'ed as "ums lock",
ums.c:593) β the opener is blocked exactly in ums_ev_open()'s
lockmgr(&sc->sc_lock) under ev_mtx while the callback holds
sc_lock and waits for ev_mtx. Unkillable (D1), survived kill -9.
Second stock run froze even faster (1400 opens).
Rig
Same as DF-5368 (QEMU usb-mouse on uhci, X86_64_GENERIC guest,
EVDEV_SUPPORT). ocprobe.py drives everything.
Run
python3 ocprobe.py # stock /boot/kernel/ums.ko UMS_KO=/usr/src/.../ums.ko python3 ocprobe.py # patched build
Fix validation
fix.diff: register the evdev with the driver's lock β
evdev_register_mtx(sc->sc_evdev, &sc->sc_lock) β so both orders
become one recursive lock (sc_lock is LK_CANRECURSE, ums.c:593).
Patched run: 171,473 open/close cycles under continuous stream, counter monotonic, loop exits on time, process killable, guest healthy (fix_run.log).
DF-5369 VERDICT β REPRODUCED (unkillable kernel deadlock, local DoS)
Result
| item | value |
|---|---|
| status | reproduced |
| impact | dos (permanent unkillable D-state hang + wedged USB input) |
| baseline | stock ums.ko: deadlock at 21,200 opens (run 1) / 1,400 opens (run 2), 2/2 |
| patched | evdev_register_mtx(&sc->sc_lock): 171,473 opens, monotonic, clean exit |
| confidence | certain |
Mechanism (path:line)
Order A (data path, per delivered report):
ums_intr_callback holds sc->sc_lock (xfer lock from
ums.c:606-608) -> ums_evdev_push ums.c:363 -> evdev_push_rel
(evdev.h:199) -> evdev_push_event -> EVDEV_ENTER =
lockmgr(ev_mtx) (evdev_private.h:147-155; ev_mtx because
ums.c:743 used evdev_register, evdev.c:345-347).
Order B (control path, open/close/detach):
cdev open cdev.c:132 EVDEV_LOCK -> evdev_register_client
evdev.c:984-991 -> ums_ev_open -> lockmgr(&sc->sc_lock)
ums.c:964; detach: evdev_unregister evdev.c:375 ->
evdev_dispose_client evdev.c:1004-1008 -> ums_ev_close ->
lockmgr(&sc->sc_lock) ums.c:983.
A(then)B(then) crossing = both sleepable lockmgr locks held
mutually -> permanent wedge. lockmgr blocking sleeps are not
signal-interruptible here -> kill -9 cannot reap the opener.
Reproduction detail
- Run 1 (from session log, ocprobe against stock): counter 6200 /
12600 / 19200 / 21200 then frozen from t=20s through t=140s; loop's
own 150s deadline passed without exit;
kill -9ineffective (pgrepstill lists the pid 1s later). - Run 2 (run.log, stock): frozen at 1,400 opens; identical kill failure.
- Forensics:
ps -o pid,stat,wchan,command -p 31041->STAT D1, WCHAN "ums lock"β the exact lock fromums.c:593(lockinit(&sc->sc_lock, "ums lock", ...)), i.e. blocked insideums_ev_open/ums_ev_closeunderev_mtx. - Guest otherwise alive (ssh fine); the ums device and its USB callback thread are dead for the session β matches the wedge being device-scoped, not system-wide.
Trigger reachability
Opening the ums0 evdev node requires root or group input
(/dev/input/eventN crw-rw---- root:input, cdev.c:714 make_dev
0600->0660 via devfs rules). Real-world: an X11/wayland/libinput
session (root or seat-privileged) opening/closing or re-scanning the
mouse while it streams β routine on VT switch, resume, device
re-enumeration. The detach-side variant additionally overlaps with
the DF-5368 window.
Fix validation
fix.diff: evdev_register_mtx(sc->sc_evdev, &sc->sc_lock) β the
evdev state lock becomes sc_lock itself; both former orders
collapse into recursive acquisition of one LK_CANRECURSE lock.
- Patched module: 171,473 open/close cycles under continuous stream,
monotonic progress every 5s sample, loop exited at its deadline,
process gone (loop-gone), guest healthy.
- No behavioral regressions: events still flow (client open/push/close
exercised 171k times), detach cycles during DF-5368 work unaffected.
Artifacts
- ocprobe.py β the probe (stock vs UMS_KO=... patched)
- run.log β stock run 2 (froze at 1400)
- fix_run.log β patched run (171,473 opens, clean)
- fix.diff β evdev_register_mtx change
- ps_forensic.txt β D1 / "ums lock" evidence
- run1 session excerpt embedded in README/VERDICT
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
fixedPatched module: 171,473 open/close cycles under continuous report stream, progress monotonic at every 5s sample, loop exited at its deadline, process killable, guest healthy - vs stock deadlocking in 1.4k-21.2k opens (2/2).
fix.diff; fix_run.log (t=5..140s monotonic counts to 171,473, kill check 'loop-gone', alive: True)
Confirmed kernel references
- sys/bus/u4b/input/ums.c:363
- sys/bus/u4b/input/ums.c:606
- sys/bus/u4b/input/ums.c:743
- sys/bus/u4b/input/ums.c:964
- sys/bus/u4b/input/ums.c:983
- sys/bus/u4b/input/ums.c:593
- sys/dev/misc/evdev/evdev.c:345
- sys/dev/misc/evdev/evdev.c:375
- sys/dev/misc/evdev/evdev.c:984
- sys/dev/misc/evdev/evdev.c:1004
- sys/dev/misc/evdev/cdev.c:132
- sys/dev/misc/evdev/evdev_private.h:147
Detail
Exploit chain
root/seat-privileged process (X11/libinput-class consumer) opens/closes or re-scans the evdev node of a streaming ums device; a report completing in the Β΅s crossing deadlocks the opener permanently (unkillable D-state) and kills the input device's callback thread. No memory corruption; permanent local hang.
Evidence (decisive lines)
run.log (stock run 2: counter frozen at 1400 from t=20s to t=140s, kill -9 ineffective); README.md embeds run 1 (froze at 21,200); ps_forensic.txt (STAT D1, WCHAN 'ums lock'); fix_run.log (patched: 171,473 opens monotonic, loop-gone)
PoC changes
probe was self-contained; required root ssh through /bin/sh (csh login shell) and the QEMU monitor mouse_move spammer shared with the DF-5368 rig.
Verified recommended fix
Register the evdev with the driver lock: evdev_register_mtx(sc->sc_evdev, &sc->sc_lock) at ums.c:743 (sc_lock is LK_CANRECURSE), eliminating the sc_lock<->ev_mtx nesting.
Verdict
REPRODUCED (unkillable kernel deadlock / local DoS). ums takes sc->sc_lock and then the evdev internal lock in ums_intr_callback()->ums_evdev_push() (ums.c:363, xfer lock from ums.c:606), while evdev takes its lock and then sc->sc_lock in evdev_register_client()->ums_ev_open() (ums.c:964) and evdev_unregister()->evdev_dispose_client()->ums_ev_close() (ums.c:983) - a sleepable AB-BA. A root open/close loop on the ums0 evdev node while the mouse streams wedged both sides permanently in 2/2 stock runs (froze after 21,200 and 1,400 opens): the opener stuck in D-state, WCHAN 'ums lock' (= sc->sc_lock, ums.c:593), kill -9 ineffective; the USB callback thread holds sc_lock waiting for ev_mtx forever. fix.diff (evdev_register_mtx(&sc->sc_lock)) collapses both orders into the recursive sc_lock: 171,473 open/close cycles under continuous stream with monotonic progress and clean exit.
No comments yet.