Summary
Hyprland crashes with SIGSEGV inside handlePF() when a page-flip completion event
is delivered for a connector that is being torn down by a udev remove event. Hit
twice within 24h on a USB-C dock, both times during monitor hotplug, with
byte-identical relative offsets in the backtrace.
The faulting instruction is in the guard clause at the top of handlePF(), i.e.
the very check that is supposed to prevent this:
static void handlePF(int fd, unsigned seq, unsigned tv_sec, unsigned tv_usec, unsigned crtc_id, void* data) {
auto pageFlip = (SDRMPageFlip*)data;
if (!pageFlip || !pageFlip->connector) // <-- SIGSEGV here
return;
Disassembly at the crash site (libaquamarine.so.13+0xc05ee):
mov %r9,%rbx
test %rax,%rax
je 0x7f3c19dad6f0 ; null check passes -> not taken
=> cmpq $0x0,0x10(%rax) ; SIGSEGV: rax is non-null but dangling
je 0x7f3c19dad6f0
rax survives the null test and then faults on dereference, so this is a
use-after-free, not a null pointer. Given that SDRMPageFlip holds only a
CWeakPointer<SDRMConnector>, the faulting +0x10 access is consistent with the
weak pointer's validity check reading its control block — i.e. either the
SDRMPageFlip passed as data or the control block behind it is already freed by
the time the kernel delivers the event. A null guard cannot catch that.
Note that restoreAfterVT() already documents the general shape of this problem
for the VT-switch case:
For VT switch this is also safe: pending events from the old session are still
queued in the fd buffer and will fire handlePF() after restore, but
isPageFlipPending is already false so the = false assignment is a harmless no-op.
For a connector removed by udev the same "events still queued in the fd buffer"
situation is not harmless, because the SDRMPageFlip those events point at is
gone.
Backtrace
#4 <signal handler called>
#5 0x00007f3c19dad5ee in ?? () from /usr/lib/libaquamarine.so.13 (+0xc05ee, handlePF)
#6 0x00007f3c19791b65 in drmHandleEvent () from /usr/lib/libdrm.so.2
#7 0x00007f3c19dbcf6a in ?? () from /usr/lib/libaquamarine.so.13 (+0xcff6a, CDRMBackend::dispatchEvents)
#8 0x000056354f29171a in ?? () (Hyprland)
#9 0x00007f3c19b25612 in wl_event_loop_dispatch () from /usr/lib/libwayland-server.so.0
#10 0x00007f3c19b27cc7 in wl_display_run () from /usr/lib/libwayland-server.so.0
#11 CEventLoopManager::enterLoop()
#12 main
No debug symbols are available for aquamarine on this distro, hence the ??
frames; the offsets were resolved against the exported symbol table.
Both crashes (2026-08-20 and 2026-08-21) show the identical offsets +0xc05ee and
+0xcff6a, so it is the same code path each time.
Log tail immediately before the crash
drm: Not disconnecting connector DP-5 because it's already disconnected
drm: Not disconnecting connector DP-7 because it's already disconnected
drm: Rechecking CRTCs
drm: Skipping connector eDP-1, has crtc 59 and is connected
drm: connector DP-1, has crtc -1, will be rechecked
drm: slot 0 crtc 59 taken by eDP-1, skipping
drm: Cursor buffer imported into KMS with id 150
udev: new udev remove event for card1-DP-4
<crash>
Note the name mismatch in the last lines: the backend is rescanning DP-5/DP-7 (the
names the dock outputs have after re-enumeration) while the remove event arrives
for DP-4, a name from before the re-enumeration. Connector names had just
shifted (DP-3/DP-4 -> DP-5/DP-7).
The earlier crash on 2026-08-20 was preceded by repeated
ERR from aquamarine: drm: Cannot commit when a page-flip is awaiting.
Steps to reproduce
Not deterministic, but both occurrences share the same shape:
- Laptop (i915, Comet Lake) with internal eDP-1 plus two external displays on a
USB-C dock.
- Close and reopen the lid while dock enumeration is still in progress, or
re-plug the dock, so DP connectors are removed and re-added and their names
shift.
- Crash follows a few seconds into the hotplug sequence.
Timeline of the 2026-08-21 crash:
07:46:29 Lid closed
07:46:34 Lid opened
07:46:38 dock USB keyboard enumerated (dock still coming up)
07:46:45 SIGSEGV
Possibly related
Environment
- Hyprland 0.56.2 (tag v0.56.2, commit efb50993780079460b0cbed1363e2166a2de1d9f)
- Aquamarine 0.14.0 (built against 0.14.0, system has 0.14.0)
- libdrm 2.4.134, mesa 26.2.1
- GPU: Intel CometLake-U GT2 [UHD Graphics] (8086:9b41), i915, atomic KMS
- Outputs: eDP-1 1920x1080 (scale 1.5) + 2x 1920x1080 on a USB-C dock
- Kernel 7.1.8-1-cachyos, CachyOS (Arch-based)
- Hyprutils: built against 0.14.0, system has 0.14.1
- No plugins loaded
Crash reports and a coredump are on file; happy to provide more detail or test a
patch.
Summary
Hyprland crashes with SIGSEGV inside
handlePF()when a page-flip completion eventis delivered for a connector that is being torn down by a udev remove event. Hit
twice within 24h on a USB-C dock, both times during monitor hotplug, with
byte-identical relative offsets in the backtrace.
The faulting instruction is in the guard clause at the top of
handlePF(), i.e.the very check that is supposed to prevent this:
Disassembly at the crash site (
libaquamarine.so.13+0xc05ee):raxsurvives the null test and then faults on dereference, so this is ause-after-free, not a null pointer. Given that
SDRMPageFlipholds only aCWeakPointer<SDRMConnector>, the faulting+0x10access is consistent with theweak pointer's validity check reading its control block — i.e. either the
SDRMPageFlippassed asdataor the control block behind it is already freed bythe time the kernel delivers the event. A null guard cannot catch that.
Note that
restoreAfterVT()already documents the general shape of this problemfor the VT-switch case:
For a connector removed by udev the same "events still queued in the fd buffer"
situation is not harmless, because the
SDRMPageFlipthose events point at isgone.
Backtrace
No debug symbols are available for aquamarine on this distro, hence the
??frames; the offsets were resolved against the exported symbol table.
Both crashes (2026-08-20 and 2026-08-21) show the identical offsets
+0xc05eeand+0xcff6a, so it is the same code path each time.Log tail immediately before the crash
Note the name mismatch in the last lines: the backend is rescanning DP-5/DP-7 (the
names the dock outputs have after re-enumeration) while the remove event arrives
for DP-4, a name from before the re-enumeration. Connector names had just
shifted (DP-3/DP-4 -> DP-5/DP-7).
The earlier crash on 2026-08-20 was preceded by repeated
ERR from aquamarine: drm: Cannot commit when a page-flip is awaiting.Steps to reproduce
Not deterministic, but both occurrences share the same shape:
USB-C dock.
re-plug the dock, so DP connectors are removed and re-added and their names
shift.
Timeline of the 2026-08-21 crash:
Possibly related
CDRMOutput. Sameclass of bug, but the idle-dispatch path rather than the drm event path.
Cannot commit when a page-flip is awaitingprecondition,but ends in a futex deadlock instead of a segfault.
Environment
Crash reports and a coredump are on file; happy to provide more detail or test a
patch.