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).
* Restored window size when window is un-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

134
input.c
View file

@ -487,29 +487,34 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
tll_free(seat->mouse.bindings);
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");
seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod1") ;
seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Control");
seat->kbd.mod_meta = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod4");
/* Compose (dead keys) */
seat->kbd.xkb_compose_table = xkb_compose_table_new_from_locale(
seat->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_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");
if (seat->kbd.xkb_compose_table == NULL) {
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) */
seat->kbd.xkb_compose_table = xkb_compose_table_new_from_locale(
seat->kbd.xkb, setlocale(LC_CTYPE, NULL), XKB_COMPOSE_COMPILE_NO_FLAGS);
if (seat->kbd.xkb_keymap != NULL) {
seat->kbd.xkb_state = xkb_state_new(seat->kbd.xkb_keymap);
if (seat->kbd.xkb_compose_table == NULL) {
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);
seat->kbd.mod_shift = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Shift");
seat->kbd.mod_alt = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Mod1") ;
seat->kbd.mod_ctrl = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, "Control");
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");
seat->kbd.key_arrow_down = xkb_keymap_key_by_name(seat->kbd.xkb_keymap, "DOWN");
}
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 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;
}
assert(term != NULL);
@ -870,39 +879,43 @@ keyboard_key(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
goto maybe_repeat;
}
if (compose_status == XKB_COMPOSE_CANCELLED)
goto maybe_repeat;
/*
* Compose, and maybe emit "normal" character
*/
uint8_t buf[64] = {0};
int count = 0;
assert(seat->kbd.xkb_compose_state != NULL ||
compose_status != XKB_COMPOSE_COMPOSED);
if (compose_status == XKB_COMPOSE_COMPOSED) {
assert(seat->kbd.xkb_compose_state != NULL);
int count = compose_status == XKB_COMPOSE_COMPOSED
? 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(
seat->kbd.xkb_compose_state, (char *)buf, sizeof(buf));
if (count <= 0)
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);
}
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_CTRL(x) ((x) < 0x20 || ((x) >= 0x7f && (x) <= 0x9f))
if ((keymap_mods & MOD_CTRL) &&
!is_control_key(sym) &&
(count == 1 && !IS_CTRL(buf[0])) &&
(count == 1 && !IS_CTRL(utf8[0])) &&
sym < 256)
{
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) {
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) {
const wchar_t wc = 0x80 | buf[0];
const wchar_t wc = 0x80 | utf8[0];
char utf8[8];
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)
term_to_slave(term, utf8, chars);
else
term_to_slave(term, buf, count);
term_to_slave(term, utf8, count);
}
else {
/* Alt ignored */
term_to_slave(term, buf, count);
term_to_slave(term, utf8, count);
}
} else
term_to_slave(term, buf, count);
term_to_slave(term, utf8, count);
}
if (utf8 != buf)
free(utf8);
term_reset_view(term);
selection_cancel(term);
@ -986,7 +1002,6 @@ maybe_repeat:
if (should_repeat)
start_repeater(seat, key - 8);
}
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",
mods_depressed, mods_latched, mods_locked, group);
if (seat->kbd.xkb == NULL)
return;
if (seat->kbd.xkb_state != NULL) {
xkb_state_update_mask(
seat->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
xkb_state_update_mask(
seat->kbd.xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
/* Update state of modifiers we're interested in for e.g mouse events */
seat->kbd.shift = xkb_state_mod_index_is_active(
seat->kbd.xkb_state, seat->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED);
seat->kbd.alt = xkb_state_mod_index_is_active(
seat->kbd.xkb_state, seat->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED);
seat->kbd.ctrl = xkb_state_mod_index_is_active(
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);
/* Update state of modifiers we're interested in for e.g mouse events */
seat->kbd.shift = xkb_state_mod_index_is_active(
seat->kbd.xkb_state, seat->kbd.mod_shift, XKB_STATE_MODS_DEPRESSED);
seat->kbd.alt = xkb_state_mod_index_is_active(
seat->kbd.xkb_state, seat->kbd.mod_alt, XKB_STATE_MODS_DEPRESSED);
seat->kbd.ctrl = xkb_state_mod_index_is_active(
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)
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) {
case WL_POINTER_BUTTON_STATE_PRESSED: {
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 */
xkb_mod_mask_t mods = xkb_state_serialize_mods(