input: handle XKB errors

* Don’t de-reference the xkb context/keymap/state if we failed to
  instantiate them.
* Don’t try to send a translated utf8 key sequence if the translation
  failed.
* Handle xkb_compose_state_get_utf8() and xkb_state_key_get_utf8()
  returning more than 64 bytes.

This _may_ fix #171.
This commit is contained in:
Daniel Eklöf 2020-10-20 21:01:33 +02:00
parent 4c3d2cfc7d
commit 0d319f8793
No known key found for this signature in database
GPG key ID: 5BBD4992C116573F
2 changed files with 76 additions and 60 deletions

View file

@ -53,6 +53,8 @@
(https://codeberg.org/dnkl/foot/issues/170). (https://codeberg.org/dnkl/foot/issues/170).
* Restored window size when window is un-tiled. * Restored window size when window is un-tiled.
* XCursor shape in CSD corners when window is tiled. * XCursor shape in CSD corners when window is tiled.
* Error handling when processing keyboard input (maybe
https://codeberg.org/dnkl/foot/issues/171).
### Security ### Security

134
input.c
View file

@ -487,29 +487,34 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
tll_free(seat->mouse.bindings); tll_free(seat->mouse.bindings);
seat->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS); seat->kbd.xkb = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
seat->kbd.xkb_keymap = xkb_keymap_new_from_buffer(
seat->kbd.xkb, map_str, size, XKB_KEYMAP_FORMAT_TEXT_V1,
XKB_KEYMAP_COMPILE_NO_FLAGS);
seat->kbd.xkb_state = xkb_state_new(seat->kbd.xkb_keymap); if (seat->kbd.xkb != NULL) {
seat->kbd.xkb_keymap = xkb_keymap_new_from_buffer(
seat->kbd.xkb, map_str, size, XKB_KEYMAP_FORMAT_TEXT_V1,
XKB_KEYMAP_COMPILE_NO_FLAGS);
seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Shift"); /* Compose (dead keys) */
seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod1") ; seat->kbd.xkb_compose_table = xkb_compose_table_new_from_locale(
seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Control"); seat->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS);
seat->kbd.mod_meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod4");
seat->kbd.key_arrow_up = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "UP"); if (seat->kbd.xkb_compose_table == NULL) {
seat->kbd.key_arrow_down = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "DOWN"); LOG_WARN("failed to instantiate compose table; dead keys will not work");
} else {
seat->kbd.xkb_compose_state = xkb_compose_state_new(
seat->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
}
}
/* Compose (dead keys) */ if (seat->kbd.xkb_keymap != NULL) {
seat->kbd.xkb_compose_table = xkb_compose_table_new_from_locale( seat->kbd.xkb_state = xkb_state_new(seat->kbd.xkb_keymap);
seat->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS);
if (seat->kbd.xkb_compose_table == NULL) { seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Shift");
LOG_WARN("failed to instantiate compose table; dead keys will not work"); seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod1") ;
} else { seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Control");
seat->kbd.xkb_compose_state = xkb_compose_state_new( seat->kbd.mod_meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod4");
seat->kbd.xkb_compose_table, XKB_COMPOSE_STATE_NO_FLAGS);
seat->kbd.key_arrow_up = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "UP");
seat->kbd.key_arrow_down = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "DOWN");
} }
munmap(map_str, size); munmap(map_str, size);
@ -749,8 +754,12 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
struct seat *seat = data; struct seat *seat = data;
struct terminal *term = seat->kbd_focus; struct terminal *term = seat->kbd_focus;
if (seat->kbd.xkb == NULL) if (seat->kbd.xkb == NULL ||
seat->kbd.xkb_keymap == NULL ||
seat->kbd.xkb_state == NULL)
{
return; return;
}
assert(term != NULL); assert(term != NULL);
@ -870,39 +879,43 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
goto maybe_repeat; goto maybe_repeat;
} }
if (compose_status == XKB_COMPOSE_CANCELLED)
goto maybe_repeat;
/* /*
* Compose, and maybe emit "normal" character * Compose, and maybe emit "normal" character
*/ */
uint8_t buf[64] = {0}; assert(seat->kbd.xkb_compose_state != NULL ||
int count = 0; compose_status != XKB_COMPOSE_COMPOSED);
if (compose_status == XKB_COMPOSE_COMPOSED) { int count = compose_status == XKB_COMPOSE_COMPOSED
assert(seat->kbd.xkb_compose_state != NULL); ? xkb_compose_state_get_utf8(seat->kbd.xkb_compose_state, NULL, 0)
: xkb_state_key_get_utf8(seat->kbd.xkb_state, key, NULL, 0);
count = xkb_compose_state_get_utf8( if (count <= 0)
seat->kbd.xkb_compose_state, (char *)buf, sizeof(buf)); goto maybe_repeat;
/* Buffer for translated key. Use a static buffer in most cases,
* and use a malloc:ed buffer when necessary */
uint8_t buf[32];
uint8_t *utf8 = count < sizeof(buf) ? buf : xmalloc(count + 1);
compose_status == XKB_COMPOSE_COMPOSED
? xkb_compose_state_get_utf8(
seat->kbd.xkb_compose_state, (char *)utf8, count + 1)
: xkb_state_key_get_utf8(
seat->kbd.xkb_state, key, (char *)utf8, count + 1);
if (seat->kbd.xkb_compose_state != NULL)
xkb_compose_state_reset(seat->kbd.xkb_compose_state); xkb_compose_state_reset(seat->kbd.xkb_compose_state);
}
else if (compose_status == XKB_COMPOSE_CANCELLED) {
goto maybe_repeat;
}
else {
count = xkb_state_key_get_utf8(
seat->kbd.xkb_state, key, (char *)buf, sizeof(buf));
}
if (count == 0)
goto maybe_repeat;
#define is_control_key(x) ((x) >= 0x40 && (x) <= 0x7f) #define is_control_key(x) ((x) >= 0x40 && (x) <= 0x7f)
#define IS_CTRL(x) ((x) < 0x20 || ((x) >= 0x7f && (x) <= 0x9f)) #define IS_CTRL(x) ((x) < 0x20 || ((x) >= 0x7f && (x) <= 0x9f))
if ((keymap_mods & MOD_CTRL) && if ((keymap_mods & MOD_CTRL) &&
!is_control_key(sym) && !is_control_key(sym) &&
(count == 1 && !IS_CTRL(buf[0])) && (count == 1 && !IS_CTRL(utf8[0])) &&
sym < 256) sym < 256)
{ {
static const int mod_param_map[32] = { static const int mod_param_map[32] = {
@ -953,11 +966,11 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
*/ */
if (term->meta.esc_prefix) { if (term->meta.esc_prefix) {
term_to_slave(term, "\x1b", 1); term_to_slave(term, "\x1b", 1);
term_to_slave(term, buf, count); term_to_slave(term, utf8, count);
} }
else if (term->meta.eight_bit && count == 1) { else if (term->meta.eight_bit && count == 1) {
const wchar_t wc = 0x80 | buf[0]; const wchar_t wc = 0x80 | utf8[0];
char utf8[8]; char utf8[8];
mbstate_t ps = {0}; mbstate_t ps = {0};
@ -966,17 +979,20 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
if (chars != (size_t)-1) if (chars != (size_t)-1)
term_to_slave(term, utf8, chars); term_to_slave(term, utf8, chars);
else else
term_to_slave(term, buf, count); term_to_slave(term, utf8, count);
} }
else { else {
/* Alt ignored */ /* Alt ignored */
term_to_slave(term, buf, count); term_to_slave(term, utf8, count);
} }
} else } else
term_to_slave(term, buf, count); term_to_slave(term, utf8, count);
} }
if (utf8 != buf)
free(utf8);
term_reset_view(term); term_reset_view(term);
selection_cancel(term); selection_cancel(term);
@ -986,7 +1002,6 @@ maybe_repeat:
if (should_repeat) if (should_repeat)
start_repeater(seat, key - 8); start_repeater(seat, key - 8);
} }
static void static void
@ -999,21 +1014,20 @@ keyboard_modifiers(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
LOG_DBG("modifiers: depressed=0x%x, latched=0x%x, locked=0x%x, group=%u", LOG_DBG("modifiers: depressed=0x%x, latched=0x%x, locked=0x%x, group=%u",
mods_depressed, mods_latched, mods_locked, group); mods_depressed, mods_latched, mods_locked, group);
if (seat->kbd.xkb == NULL) if (seat->kbd.xkb_state != NULL) {
return; xkb_state_update_mask(
seat->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
xkb_state_update_mask( /* Update state of modifiers we're interested in for e.g mouse events */
seat->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group); seat->kbd.shift = xkb_state_mod_index_is_active(
seat->kbd.xkb_state, seat->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED);
/* Update state of modifiers we're interested in for e.g mouse events */ seat->kbd.alt = xkb_state_mod_index_is_active(
seat->kbd.shift = xkb_state_mod_index_is_active( seat->kbd.xkb_state, seat->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED);
seat->kbd.xkb_state, seat->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED); seat->kbd.ctrl = xkb_state_mod_index_is_active(
seat->kbd.alt = xkb_state_mod_index_is_active( seat->kbd.xkb_state, seat->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED);
seat->kbd.xkb_state, seat->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED); seat->kbd.meta = xkb_state_mod_index_is_active(
seat->kbd.ctrl = xkb_state_mod_index_is_active( seat->kbd.xkb_state, seat->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED);
seat->kbd.xkb_state, seat->kbd.mod_ctrl, XKB_STATE_MODS_DEPRESSED); }
seat->kbd.meta = xkb_state_mod_index_is_active(
seat->kbd.xkb_state, seat->kbd.mod_meta, XKB_STATE_MODS_DEPRESSED);
if (seat->kbd_focus && seat->kbd_focus->active_surface == TERM_SURF_GRID) if (seat->kbd_focus && seat->kbd_focus->active_surface == TERM_SURF_GRID)
term_xcursor_update_for_seat(seat->kbd_focus, seat); term_xcursor_update_for_seat(seat->kbd_focus, seat);
@ -1571,7 +1585,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
switch (state) { switch (state) {
case WL_POINTER_BUTTON_STATE_PRESSED: { case WL_POINTER_BUTTON_STATE_PRESSED: {
if (!seat->mouse.consumed) { if (!seat->mouse.consumed) {
if (seat->wl_keyboard != NULL) { if (seat->wl_keyboard != NULL && seat->kbd.xkb_state != NULL) {
/* Seat has keyboard - use mouse bindings *with* modifiers */ /* Seat has keyboard - use mouse bindings *with* modifiers */
xkb_mod_mask_t mods = xkb_state_serialize_mods( xkb_mod_mask_t mods = xkb_state_serialize_mods(