Creating a key binding which replaces itself (such as "bind x bind x lsw")

frees the command list bound to the key while it is still being executed,
leading to a use after free. To prevent this, create a dead keys list and defer
freeing replaced or removed key bindings until the main loop when the key
binding will have finished executing.

Found by Johan Friis when creating a key binding to reload his configuration
file.
This commit is contained in:
Nicholas Marriott 2009-07-12 17:33:18 +00:00
parent 22d51ec1ea
commit 9e49ec6cd3
2 changed files with 24 additions and 8 deletions

View File

@ -27,6 +27,7 @@
SPLAY_GENERATE(key_bindings, key_binding, entry, key_bindings_cmp);
struct key_bindings key_bindings;
struct key_bindings dead_key_bindings;
int
key_bindings_cmp(struct key_binding *bd1, struct key_binding *bd2)
@ -48,12 +49,12 @@ key_bindings_add(int key, int can_repeat, struct cmd_list *cmdlist)
{
struct key_binding *bd;
if ((bd = key_bindings_lookup(key)) == NULL) {
bd = xmalloc(sizeof *bd);
bd->key = key;
SPLAY_INSERT(key_bindings, &key_bindings, bd);
} else
cmd_list_free(bd->cmdlist);
key_bindings_remove(key);
bd = xmalloc(sizeof *bd);
bd->key = key;
SPLAY_INSERT(key_bindings, &key_bindings, bd);
bd->can_repeat = can_repeat;
bd->cmdlist = cmdlist;
}
@ -66,9 +67,20 @@ key_bindings_remove(int key)
if ((bd = key_bindings_lookup(key)) == NULL)
return;
SPLAY_REMOVE(key_bindings, &key_bindings, bd);
SPLAY_INSERT(key_bindings, &dead_key_bindings, bd);
}
cmd_list_free(bd->cmdlist);
xfree(bd);
void
key_bindings_clean(void)
{
struct key_binding *bd;
while (!SPLAY_EMPTY(&dead_key_bindings)) {
bd = SPLAY_ROOT(&dead_key_bindings);
SPLAY_REMOVE(key_bindings, &dead_key_bindings, bd);
cmd_list_free(bd->cmdlist);
xfree(bd);
}
}
void
@ -162,6 +174,7 @@ key_bindings_free(void)
{
struct key_binding *bd;
key_bindings_clean();
while (!SPLAY_EMPTY(&key_bindings)) {
bd = SPLAY_ROOT(&key_bindings);
SPLAY_REMOVE(key_bindings, &key_bindings, bd);

View File

@ -346,6 +346,9 @@ server_main(int srv_fd)
server_handle_windows(&pfd);
server_handle_clients(&pfd);
/* Collect any unset key bindings. */
key_bindings_clean();
/*
* If we have no sessions and clients left, let's get out
* of here...