Skip to content

Conversation

@tobixen
Copy link

@tobixen tobixen commented Feb 12, 2026

Summary

Fixes a segfault (SIGSEGV in libinput_device_ref) in the keyboard-state module when input devices appear or disappear in /dev/input/.

Three bugs fixed:

  • Missing NULL check (crash cause): tryAddDevice() passes the return value of libinput_path_add_device() directly to libinput_device_ref() without checking for NULL. On laptops, virtual input devices (power buttons, lid switch, etc.) appear and disappear; if libinput can't open one, the NULL return causes the segfault.
  • Missing cleanup on device removal: The IN_DELETE handler erased devices from the map without calling libinput_path_remove_device(), leaving dangling pointers in the libinput context.
  • Thread safety: Added std::mutex to protect libinput_devices_ which was accessed from 3 threads without synchronization.

Fixes #4851

Test plan

  • Build waybar with this patch
  • Verify keyboard-state module works normally
  • Unplug/replug a USB keyboard while waybar is running — should not crash
  • On a laptop: trigger device enumeration (e.g., suspend/resume) — should not crash

🤖 Generated with Claude Code

@tobixen
Copy link
Author

tobixen commented Feb 12, 2026

This pull request was "vibe-coded". This is a small fix, but the efforts of reviewing it is probably bigger than the efforts I've done creating this pull request. It's totally acceptable for me if this pull request is simply closed without any explanations given.

The crash I observed happened during heavy thrashing (but no OOM-killing - so that's not the reason why waybar crashed). I don't have a USB keyboard nor a bluetooth keyboard so it's hard for me to verify the fix manually. I'm not even sure if the analysis given by Claude is correct or not.

The keyboard-state module crashes with SIGSEGV in libinput_device_ref
when a new input device appears in /dev/input/.

Three bugs fixed:

1. Missing NULL check: tryAddDevice() calls libinput_path_add_device()
   which returns NULL on failure, then immediately passes the result to
   libinput_device_ref() without checking.  On laptops, virtual input
   devices (power buttons, lid switch, etc.) appear and disappear in
   /dev/input/ triggering the hotplug handler; if libinput can't open
   one of these, the NULL return causes the segfault.

2. Missing cleanup on device removal: The IN_DELETE handler erased
   devices from the map without calling libinput_path_remove_device(),
   leaving dangling pointers in the libinput context.

3. Thread safety: libinput_devices_ was accessed from 3 threads
   (main/GTK, libinput_thread_, hotplug_thread_) without any mutex.

Fixes Alexays#4851

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tobixen tobixen force-pushed the fix/keyboard-state-hotplug-crash branch from 879b6ab to 13469a8 Compare February 12, 2026 10:57
@tobixen
Copy link
Author

tobixen commented Feb 12, 2026

I prodded Claude on how I possibly can be struck by a bluetooth/usb hotplug issue when I have a laptop with an integrated keyboard, and it changed things a bit (including the issue description and pull request description).

Claude says:

The stack trace definitively shows the crash in tryAddDevice() called from hotplug_thread_ (an inotify IN_CREATE event), but I don't actually know what created the device. Could have been anything — some software creating a virtual input device, a udev rule, something else entirely.

Please give a hint if I should (ask Claude to) do more research into this. (note to self: claude --resume 707df72a-e9ca-4cd0-a451-641c75061bb6 to do more research here)

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

keyboard-state: segfault in libinput_device_ref due to race condition in hotplug handling

1 participant