summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNicholas Marriott <nicm@cvs.openbsd.org>2009-07-12 17:33:19 +0000
committerNicholas Marriott <nicm@cvs.openbsd.org>2009-07-12 17:33:19 +0000
commit3171f6dd9605b2063dfe56f53e3b2f9306e73337 (patch)
tree5cbe68d61a9bd2b41a59ad11b5f2adcb10eebc97
parent087350fcc5450e687751ec30b73252f3c0e13543 (diff)
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.
-rw-r--r--usr.bin/tmux/key-bindings.c31
-rw-r--r--usr.bin/tmux/server.c5
2 files changed, 26 insertions, 10 deletions
diff --git a/usr.bin/tmux/key-bindings.c b/usr.bin/tmux/key-bindings.c
index 743be1ee736..6f718d8c510 100644
--- a/usr.bin/tmux/key-bindings.c
+++ b/usr.bin/tmux/key-bindings.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: key-bindings.c,v 1.1 2009/06/01 22:58:49 nicm Exp $ */
+/* $OpenBSD: key-bindings.c,v 1.2 2009/07/12 17:33:18 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -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);
diff --git a/usr.bin/tmux/server.c b/usr.bin/tmux/server.c
index 1c45e64e71b..761f32161ac 100644
--- a/usr.bin/tmux/server.c
+++ b/usr.bin/tmux/server.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: server.c,v 1.7 2009/07/12 16:07:56 nicm Exp $ */
+/* $OpenBSD: server.c,v 1.8 2009/07/12 17:33:18 nicm Exp $ */
/*
* Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
@@ -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...