From bd6ce102e6afcdd0c0fe523f4f4ac8c415b8a9be Mon Sep 17 00:00:00 2001 From: Christian Decker Date: Mon, 3 Dec 2018 15:12:09 +0100 Subject: [PATCH] plugin: Better cleanup when a plugin fails This used to be a use-after-free bug in which we'd free the plugin and then still have two connections that expect to be able to operate on the plugin. This now signals the connections to exit and cleans up once they do. Signed-off-by: Christian Decker --- lightningd/plugin.c | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/lightningd/plugin.c b/lightningd/plugin.c index d0ceb1cc4..121ddf42f 100644 --- a/lightningd/plugin.c +++ b/lightningd/plugin.c @@ -196,7 +196,6 @@ static void PRINTF_FMT(2,3) plugin_kill(struct plugin *plugin, char *fmt, ...) io_wake(plugin); kill(plugin->pid, SIGKILL); list_del(&plugin->list); - tal_free(plugin); } /** @@ -372,17 +371,37 @@ static struct io_plan *plugin_write_json(struct io_conn *conn, { if (tal_count(plugin->js_arr)) { return json_stream_output(plugin->js_arr[0], plugin->stdin_conn, plugin_stream_complete, plugin); + } else if (plugin->stop) { + return io_close(conn); } return io_out_wait(conn, plugin, plugin_write_json, plugin); } +/** + * Finalizer for both stdin and stdout connections. + * + * Takes care of final cleanup, once the plugin is definitely dead. + */ +static void plugin_conn_finish(struct io_conn *conn, struct plugin *plugin) +{ + if (conn == plugin->stdin_conn) + plugin->stdin_conn = NULL; + + else if (conn == plugin->stdout_conn) + plugin->stdout_conn = NULL; + + if (plugin->stdin_conn == NULL && plugin->stdout_conn == NULL) + tal_free(plugin); +} + static struct io_plan *plugin_stdin_conn_init(struct io_conn *conn, struct plugin *plugin) { /* We write to their stdin */ /* We don't have anything queued yet, wait for notification */ plugin->stdin_conn = conn; + io_set_finish(conn, plugin_conn_finish, plugin); return io_wait(plugin->stdin_conn, plugin, plugin_write_json, plugin); } @@ -391,6 +410,7 @@ static struct io_plan *plugin_stdout_conn_init(struct io_conn *conn, { /* We read from their stdout */ plugin->stdout_conn = conn; + io_set_finish(conn, plugin_conn_finish, plugin); return io_read_partial(plugin->stdout_conn, plugin->buffer, tal_bytelen(plugin->buffer), &plugin->len_read, plugin_read_json, plugin);