Skip to content

SIGSEGV in handlePF() when a page-flip event arrives for a connector removed by udev (i915, dock hotplug) #361

Description

@dkIT25

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:

  1. Laptop (i915, Comet Lake) with internal eDP-1 plus two external displays on a
    USB-C dock.
  2. 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.
  3. 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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions