Keyboard handlers use a null xkb_state after a keymap that did not load, and each keymap leaks the previous one #167

Open
opened 2026-10-31 14:45:14 +00:00 by jorijnvdgraaf · 0 comments

Summary

Device::keyboard_keymap returns without making an xkb_state when the keymap is not XKB_V1 or cannot be mapped, and makes one from an unchecked xkb_keymap_new_from_string result. keyboard_modifiers and keyboard_key then pass xkb_state to libxkbcommon unchecked, which crashes on null. The same class of bug as #162: something the compositor decides, used unchecked. Line numbers refer to 670df31.

Details

  • implementations/Crafter.Graphics-Device.cpp:547-558: a format other than WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1 (the protocol also has NO_KEYMAP) or a failed mmap returns early, and xkb_state (interfaces/Crafter.Graphics-Device.cppm:108) stays null.
  • :561-565: xkb_keymap_new_from_string returns null for a keymap this libxkbcommon cannot compile, and xkb_state_new is called on it regardless.
  • :640: keyboard_modifiers calls xkb_state_update_mask(xkb_state, ...). A compositor sends modifiers right after a keyboard enter, so this crashes as soon as a window gets the keyboard focus, before a key is pressed.
  • :610: keyboard_key calls xkb_state_key_get_utf8(xkb_state, ...) on every press.

Also: every keymap event makes a new context (:560, besides the one the static initializer makes, Device.cppm:107), keymap and state, and releases none of the previous ones. The compositor sends a new keymap whenever the layout changes.

Found by reading the code; not reproduced.

Suggested fix

Key events need no keymap: the library reports raw kernel codes (:593), and XKB only produces the UTF-8 for onTextInput. Without a state, skip the XKB calls, so keys still fire onRawKeyDown and onRawKeyUp, just without text. In keyboard_keymap, check the keymap before making a state, unref the previous state and keymap once the new ones exist, and keep one context.

## Summary `Device::keyboard_keymap` returns without making an `xkb_state` when the keymap is not `XKB_V1` or cannot be mapped, and makes one from an unchecked `xkb_keymap_new_from_string` result. `keyboard_modifiers` and `keyboard_key` then pass `xkb_state` to libxkbcommon unchecked, which crashes on null. The same class of bug as #162: something the compositor decides, used unchecked. Line numbers refer to `670df31`. ## Details - `implementations/Crafter.Graphics-Device.cpp:547-558`: a format other than `WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1` (the protocol also has `NO_KEYMAP`) or a failed `mmap` returns early, and `xkb_state` (`interfaces/Crafter.Graphics-Device.cppm:108`) stays null. - `:561-565`: `xkb_keymap_new_from_string` returns null for a keymap this libxkbcommon cannot compile, and `xkb_state_new` is called on it regardless. - `:640`: `keyboard_modifiers` calls `xkb_state_update_mask(xkb_state, ...)`. A compositor sends modifiers right after a keyboard enter, so this crashes as soon as a window gets the keyboard focus, before a key is pressed. - `:610`: `keyboard_key` calls `xkb_state_key_get_utf8(xkb_state, ...)` on every press. Also: every keymap event makes a new context (`:560`, besides the one the static initializer makes, `Device.cppm:107`), keymap and state, and releases none of the previous ones. The compositor sends a new keymap whenever the layout changes. Found by reading the code; not reproduced. ## Suggested fix Key events need no keymap: the library reports raw kernel codes (`:593`), and XKB only produces the UTF-8 for `onTextInput`. Without a state, skip the XKB calls, so keys still fire `onRawKeyDown` and `onRawKeyUp`, just without text. In `keyboard_keymap`, check the keymap before making a state, unref the previous state and keymap once the new ones exist, and keep one context.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Catcrafts/Crafter.Graphics#167
No description provided.