Sleepable lockmgr acquisition from the GPU interrupt dispatch path: DM_IRQ_TABLE_LOCK in amdgpu_dm_irq_immediate_work/amdgpu_dm_irq_schedule_work runs lockmgr(LK_EXCLUSIVE) inside the serialized DRM ithread (DragonFly port divergence)
Summary
On this port spin_lock_irqsave = lockmgr under cpu_disable_intr - a sleepable acquisition; the DM irq table lock is taken from .process context (the serializer-backed ithread) and held across every high-context vblank/pflip handler, while suspend holds it across MMIO - a concurrent DCE interrupt makes the ithread attempt to sleep inside lockmgr while holding the interrupt serializer: panic/wedged IRQ thread. Upstream uses a true spinlock here (the port swapped lockinit for spin_lock_init). Local DoS ceiling on AMD-graphics systems under interrupt pressure concurrent with suspend. Acquisition chain code-certain; convert-to-true-spinlock fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4732 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| README.md | β | 2.4 KB | β raw | |
| VERDICT.md | β | 4.5 KB | β raw | |
| fix.diff | β | 2.1 KB | view raw | |
| verdict.json | β | 3.3 KB | view raw | |
| env.txt | β | 483 B | view raw |
DF-4732 β amdgpu_dm_irq.c takes a sleepable lockmgr lock from the GPU interrupt dispatch path (DragonFly port)
- File:
sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c - Severity: Low (local DoS ceiling β panic/deadlock; requires AMD GPU + interrupt-vs-suspend race)
- Confidence: likely (protocol violation is code-certain; runtime manifestation reasoned, not demonstrated β no AMD hardware on the audit guest)
The defect
On this port spinlock_t is struct lock and spin_lock_irqsave() is
local_irq_save(flags); lockmgr(lock, LK_EXCLUSIVE);
(sys/dev/drm/include/linux/spinlock_types.h:38, sys/dev/drm/include/linux/spinlock.h:75-78)
β a sleepable lock acquired with hardware interrupts force-disabled on the
local CPU.
DM_IRQ_TABLE_LOCK (amdgpu_dm_irq.c:50-54) is taken from the GPU ISR
dispatch chain:
request_irq() -> bus_setup_intr(..., INTR_MPSAFE, linux_irq_handler, ..., &serializer)
(sys/dev/drm/linux_irq.c:84-86 β serialized ithread)
-> amdgpu_irq_handler (amdgpu_irq.c:189)
-> amdgpu_irq_dispatch -> .process = amdgpu_dm_irq_handler (amdgpu_dm_irq.c:588-601, :476-495)
-> amdgpu_dm_irq_immediate_work (:452) -- lockmgr LK_EXCLUSIVE in ithread ctx
-> amdgpu_dm_irq_schedule_work (:427) -- same
Process-context holders keep this lock for non-trivial durations:
amdgpu_dm_irq_suspend (:345-363) holds it across dc_interrupt_set() MMIO
read-modify-writes; amdgpu_dm_irq_immediate_work itself holds it while
dispatching every high-context handler (:464) β vblank/pageflip work.
On contention the serialized ithread attempts to sleep inside lockmgr while
holding the interrupt serializer and with cpu_disable_intr() active from
local_irq_save β sleeping in an ithread / with a serializer held is illegal
on DragonFly (panic/deadlock). Upstream Linux uses a true spinlock here, where
spin_lock_irqsave in hardirq context is correct.
Why not reproduced
The audit guest has no AMD GPU (env.txt). Building amdgpu into the guest kernel cannot conjure the hardware; the ISR path never runs. Static proof of the acquisition chain is in VERDICT.md.
Fix
fix.diff converts irq_handler_list_table_lock to a true DragonFly
struct spinlock (crit_enter + spin_lock in the table-lock macros), matching
upstream's non-sleeping irqsave semantics. Not applied to sys/ (read-only
audit tree).
DF-4732 VERDICT
Status: untested β no AMD GPU on the audit guest; acquisition chain proven statically
What was proven (static, line-cited)
-
The lock is sleepable on this port. -
sys/dev/drm/include/linux/spinlock_types.h:38βtypedef struct lock spinlock_t;-sys/dev/drm/include/linux/spinlock.h:75-78β#define spin_lock_irqsave(lock, flags) ({ local_irq_save(flags); lockmgr(lock, LK_EXCLUSIVE); })-amdgpu_dm_irq.c:305β the lock is initialized withlockinit(&adev->dm.irq_handler_list_table_lock, "agdmihltl", 0, LK_CANRECURSE)(a DFly sleepable lockmgr lock; upstream v4.17 hasspin_lock_inithere β confirmed by diff against torvalds/linux v4.17 during this audit). -
The ISR dispatch path acquires it. -
sys/dev/drm/linux_irq.c:84-86βrequest_irq()installslinux_irq_handlerviabus_setup_intr(..., INTR_MPSAFE, ..., &irq_entry->irq_lock)with alwkt_serializeβ the handler runs in serialized interrupt-thread context. -amdgpu_irq.c:189-199βamdgpu_irq_dispatch(:397-436, no NULL.processguard β that is DF-4713's finding) β the sources registered by this file (amdgpu_dm_irq.c:588-601:dm_crtc_irq_funcs,dm_pageflip_irq_funcs,dm_hpd_irq_funcsβ all three carry BOTH.setand.process, so this file does not hit the DF-4713 hole) dispatch toamdgpu_dm_irq_handler(:476-495). -amdgpu_dm_irq_handlerβamdgpu_dm_irq_immediate_work(:452, takesDM_IRQ_TABLE_LOCK, runs handlers under it at:464) andamdgpu_dm_irq_schedule_work(:427, takes the lock to testlist_empty). Both run in ithread context. -
The lock is held long enough in process context to make contention real. -
amdgpu_dm_irq_suspend(:345-363): holds the lock acrossdc_interrupt_set()for each HPD source β an MMIO read-modify-write per source (irq_service.c:88-99). - Any concurrent DCE interrupt (vblank/pflip storm during modeset, HPD glitch during suspend) takes the same lock from the ithread. -
Why sleeping in that context is fatal: DragonFly lockmgr blocks via
tsleepon contention; a serialized ithread must not sleep (it holds the interrupt serializer installed bybus_setup_intr), and the shim has also disabled local hardware IRQs (local_irq_saveβcpu_disable_intr). Result ceiling: kernel panic ("sleep in ithread" assertion) or a wedged interrupt path β local DoS. This is a DFly-port-specific divergence (upstream's real spinlock is legal in hardirq).
What was NOT demonstrated
No runtime reproduction: the guest has no AMD display adapter
(pciconf: only vgapci0 chip=0x11111234 QEMU virtual VGA; no /dev/dri;
no drm kld β env.txt). The amdgpu module cannot bind, so neither the ISR path
nor the suspend race can be triggered. No kernel was built for the same reason
(a single-fix kernel build proves nothing without the device).
Exploit chain (theoretical ceiling)
Local unprivileged user in a desktop session on AMD graphics hardware:
generate page-flip/vblank interrupt pressure (compositing) or hotplug events
(DP cable / USB-C dock emulation by root) while the system enters
suspend/resume (amdgpu_dm_irq_suspend holds the table lock across MMIO) β
GPU ithread blocks in lockmgr β panic. Impact: local DoS (panic). No path to
privilege escalation identified: the corruption is control-flow-internal (no
attacker-controlled data crosses the boundary).
Fix validation
fix_status: not_testable (hardware absent). fix.diff converts the table
lock to a true struct spinlock with crit_enter()/spin_lock() in the
DM_IRQ_TABLE_LOCK macros β matching upstream's non-sleeping semantics and the
file's actual usage (no recursion on this lock exists; CANRECURSE was
unnecessary β audited: no high-context handler re-acquires the table lock).
Bottom line
Code-certain protocol violation introduced by the port (sleepable lockmgr + forced irq-disable inside the GPU ithread), DFly-specific, panic ceiling under an interrupt-vs-suspend race on AMD hardware; untestable on this guest for lack of the device.
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
not_testableCannot kernel-validate without AMD GPU. fix.diff restores upstream non-sleeping semantics; no recursion on this lock exists in the file (audited: no high-context handler re-acquires the table lock), so the loss of LK_CANRECURSE is safe.
fix.diff; VERDICT.md 'Fix validation' section
Confirmed kernel references
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c:50
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c:305
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c:345
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c:427
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c:452
- sys/dev/drm/amd/display/amdgpu_dm/amdgpu_dm_irq.c:476
- sys/dev/drm/include/linux/spinlock.h:75
- sys/dev/drm/linux_irq.c:84
Detail
Exploit chain
theoretical only: unprivileged desktop user on AMD hardware generates flip/vblank/HPD interrupt pressure concurrent with amdgpu_dm_irq_suspend (suspend/resume cycle) -> GPU ithread blocks in lockmgr -> panic (local DoS). No privesc path: no attacker-controlled data crosses the lock boundary.
Evidence (decisive lines)
VERDICT.md acquisition-chain walk with path:line for every hop (linux_irq.c:84 -> amdgpu_irq.c:189/:397 -> amdgpu_dm_irq.c:476/:452/:427); env.txt documents absent hardware
PoC changes
n/a - no runtime PoC possible (hardware-gated); static acquisition-chain proof only
Verified recommended fix
Convert irq_handler_list_table_lock to a true struct spinlock (crit_enter+spin_lock in DM_IRQ_TABLE_LOCK macros; spin_init at amdgpu_dm_irq_init), matching upstream irqsave semantics - see fix.diff
Verdict
DragonFly-port protocol violation: DM_IRQ_TABLE_LOCK (amdgpu_dm_irq.c:50) maps to local_irq_save+lockmgr(LK_EXCLUSIVE) (spinlock.h:75-78) on a lock initialized with lockinit/LK_CANRECURSE (:305), and is acquired from the GPU interrupt dispatch path (amdgpu_dm_irq_handler :476 -> amdgpu_dm_irq_immediate_work :452 / amdgpu_dm_irq_schedule_work :427), which runs in the serializer-backed ithread installed by request_irq->bus_setup_intr (linux_irq.c:84-86). lockmgr may sleep; sleeping in a serialized ithread with the interrupt serializer held and hardware IRQs force-disabled is illegal on DragonFly -> panic/deadlock ceiling when an interrupt contends with a process-context holder (amdgpu_dm_irq_suspend holds the lock across dc_interrupt_set MMIO at :345-:363; immediate_work itself holds it across vblank/pflip handler dispatch at :464). Untestable on this guest: no AMD GPU, no /dev/dri, no drm kld (env.txt). DFly-specific divergence - upstream v4.17 uses a true spinlock (diffed).
No comments yet.