From c9b3d87eb7ef2ce99f659d7e567fc83629439142 Mon Sep 17 00:00:00 2001 From: ailin-nemui Date: Wed, 3 Jan 2018 12:26:02 +0100 Subject: Postpone server cleanup until after unref Add a new signal, server destroyed, that is supposed to run the clean up tasks of server disconnected. This is so that some structures will stay around longer. --- src/core/servers.c | 1 + src/fe-common/core/fe-common-core.c | 6 +++--- src/irc/core/irc-servers.c | 6 +++--- src/irc/flood/flood.c | 4 ++-- src/irc/notifylist/notifylist.c | 4 ++-- 5 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/core/servers.c b/src/core/servers.c index b9faab81..b1da9b7e 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -524,6 +524,7 @@ int server_unref(SERVER_REC *server) return TRUE; } + signal_emit("server destroyed", 1, server); MODULE_DATA_DEINIT(server); server_connect_unref(server->connrec); if (server->rawlog != NULL) rawlog_destroy(server->rawlog); diff --git a/src/fe-common/core/fe-common-core.c b/src/fe-common/core/fe-common-core.c index a3b7364c..d0d36490 100644 --- a/src/fe-common/core/fe-common-core.c +++ b/src/fe-common/core/fe-common-core.c @@ -104,7 +104,7 @@ static void sig_connected(SERVER_REC *server) MODULE_DATA_SET(server, g_new0(MODULE_SERVER_REC, 1)); } -static void sig_disconnected(SERVER_REC *server) +static void sig_destroyed(SERVER_REC *server) { void *data = MODULE_DATA(server); g_free(data); @@ -203,7 +203,7 @@ void fe_common_core_init(void) settings_check(); signal_add_first("server connected", (SIGNAL_FUNC) sig_connected); - signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected); + signal_add_last("server destroyed", (SIGNAL_FUNC) sig_destroyed); signal_add_first("channel created", (SIGNAL_FUNC) sig_channel_created); signal_add_last("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); @@ -249,7 +249,7 @@ void fe_common_core_deinit(void) signal_remove("setup changed", (SIGNAL_FUNC) sig_setup_changed); signal_remove("server connected", (SIGNAL_FUNC) sig_connected); - signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected); + signal_remove("server destroyed", (SIGNAL_FUNC) sig_destroyed); signal_remove("channel created", (SIGNAL_FUNC) sig_channel_created); signal_remove("channel destroyed", (SIGNAL_FUNC) sig_channel_destroyed); } diff --git a/src/irc/core/irc-servers.c b/src/irc/core/irc-servers.c index 4eaab712..5076a688 100644 --- a/src/irc/core/irc-servers.c +++ b/src/irc/core/irc-servers.c @@ -425,7 +425,7 @@ static void isupport_destroy_hash(void *key, void *value) g_free(value); } -static void sig_disconnected(IRC_SERVER_REC *server) +static void sig_destroyed(IRC_SERVER_REC *server) { GSList *tmp; @@ -1031,7 +1031,7 @@ void irc_servers_init(void) cmd_tag = -1; signal_add_first("server connected", (SIGNAL_FUNC) sig_connected); - signal_add_last("server disconnected", (SIGNAL_FUNC) sig_disconnected); + signal_add_last("server destroyed", (SIGNAL_FUNC) sig_destroyed); signal_add_last("server quit", (SIGNAL_FUNC) sig_server_quit); signal_add("event 001", (SIGNAL_FUNC) event_connected); signal_add("event 004", (SIGNAL_FUNC) event_server_info); @@ -1058,7 +1058,7 @@ void irc_servers_deinit(void) g_source_remove(cmd_tag); signal_remove("server connected", (SIGNAL_FUNC) sig_connected); - signal_remove("server disconnected", (SIGNAL_FUNC) sig_disconnected); + signal_remove("server destroyed", (SIGNAL_FUNC) sig_destroyed); signal_remove("server quit", (SIGNAL_FUNC) sig_server_quit); signal_remove("event 001", (SIGNAL_FUNC) event_connected); signal_remove("event 004", (SIGNAL_FUNC) event_server_info); diff --git a/src/irc/flood/flood.c b/src/irc/flood/flood.c index 0944a6eb..b528f707 100644 --- a/src/irc/flood/flood.c +++ b/src/irc/flood/flood.c @@ -324,7 +324,7 @@ void irc_flood_init(void) read_settings(); signal_add("setup changed", (SIGNAL_FUNC) read_settings); signal_add_first("server connected", (SIGNAL_FUNC) flood_init_server); - signal_add("server disconnected", (SIGNAL_FUNC) flood_deinit_server); + signal_add("server destroyed", (SIGNAL_FUNC) flood_deinit_server); autoignore_init(); settings_check(); @@ -344,5 +344,5 @@ void irc_flood_deinit(void) signal_remove("setup changed", (SIGNAL_FUNC) read_settings); signal_remove("server connected", (SIGNAL_FUNC) flood_init_server); - signal_remove("server disconnected", (SIGNAL_FUNC) flood_deinit_server); + signal_remove("server destroyed", (SIGNAL_FUNC) flood_deinit_server); } diff --git a/src/irc/notifylist/notifylist.c b/src/irc/notifylist/notifylist.c index 573f7a7f..4fd5ef1a 100644 --- a/src/irc/notifylist/notifylist.c +++ b/src/irc/notifylist/notifylist.c @@ -331,7 +331,7 @@ void irc_notifylist_init(void) notifylist_ison_init(); notifylist_whois_init(); signal_add("server connected", (SIGNAL_FUNC) notifylist_init_server); - signal_add("server disconnected", (SIGNAL_FUNC) notifylist_deinit_server); + signal_add("server destroyed", (SIGNAL_FUNC) notifylist_deinit_server); signal_add("event quit", (SIGNAL_FUNC) event_quit); signal_add("event privmsg", (SIGNAL_FUNC) event_privmsg); signal_add("event join", (SIGNAL_FUNC) event_join); @@ -349,7 +349,7 @@ void irc_notifylist_deinit(void) notifylist_whois_deinit(); signal_remove("server connected", (SIGNAL_FUNC) notifylist_init_server); - signal_remove("server disconnected", (SIGNAL_FUNC) notifylist_deinit_server); + signal_remove("server destroyed", (SIGNAL_FUNC) notifylist_deinit_server); signal_remove("event quit", (SIGNAL_FUNC) event_quit); signal_remove("event privmsg", (SIGNAL_FUNC) event_privmsg); signal_remove("event join", (SIGNAL_FUNC) event_join); -- cgit v1.2.3 From eb18904840cc7d4a6830bf0711f4682af50f1abc Mon Sep 17 00:00:00 2001 From: ailin-nemui Date: Fri, 5 Jan 2018 22:13:23 +0100 Subject: fix crash in notifylist --- src/core/servers.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/core/servers.c b/src/core/servers.c index b1da9b7e..0abfdfb0 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -524,7 +524,12 @@ int server_unref(SERVER_REC *server) return TRUE; } - signal_emit("server destroyed", 1, server); + /* since module initialisation uses server connected, only let + them know that the object got destroyed if the server was + disconnected */ + if (server->disconnected) + signal_emit("server destroyed", 1, server); + MODULE_DATA_DEINIT(server); server_connect_unref(server->connrec); if (server->rawlog != NULL) rawlog_destroy(server->rawlog); -- cgit v1.2.3 From 6e6a96d8c1eb8a51404d34809382e54eea46a9f7 Mon Sep 17 00:00:00 2001 From: ailin-nemui Date: Fri, 5 Jan 2018 23:05:39 +0100 Subject: fix uaf in signal path --- src/core/servers.c | 41 +++++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 18 deletions(-) diff --git a/src/core/servers.c b/src/core/servers.c index 0abfdfb0..11eccc53 100644 --- a/src/core/servers.c +++ b/src/core/servers.c @@ -460,8 +460,6 @@ static int server_remove_channels(SERVER_REC *server) void server_disconnect(SERVER_REC *server) { - int chans; - g_return_if_fail(IS_SERVER(server)); if (server->disconnected) @@ -480,21 +478,9 @@ void server_disconnect(SERVER_REC *server) server->disconnected = TRUE; signal_emit("server disconnected", 1, server); - /* close all channels */ - chans = server_remove_channels(server); - - if (server->handle != NULL) { - if (!chans || server->connection_lost) - net_sendbuffer_destroy(server->handle, TRUE); - else { - /* we were on some channels, try to let the server - disconnect so that our quit message is guaranteed - to get displayed */ - net_disconnect_later(net_sendbuffer_handle(server->handle)); - net_sendbuffer_destroy(server->handle, FALSE); - } - server->handle = NULL; - } + /* we used to destroy the handle here but it may be still in + use during signal processing, so destroy it on unref + instead */ if (server->readtag > 0) { g_source_remove(server->readtag); @@ -513,6 +499,8 @@ void server_ref(SERVER_REC *server) int server_unref(SERVER_REC *server) { + int chans; + g_return_val_if_fail(IS_SERVER(server), FALSE); if (--server->refcount > 0) @@ -524,11 +512,28 @@ int server_unref(SERVER_REC *server) return TRUE; } + /* close all channels */ + chans = server_remove_channels(server); + /* since module initialisation uses server connected, only let them know that the object got destroyed if the server was disconnected */ - if (server->disconnected) + if (server->disconnected) { signal_emit("server destroyed", 1, server); + } + + if (server->handle != NULL) { + if (!chans || server->connection_lost) + net_sendbuffer_destroy(server->handle, TRUE); + else { + /* we were on some channels, try to let the server + disconnect so that our quit message is guaranteed + to get displayed */ + net_disconnect_later(net_sendbuffer_handle(server->handle)); + net_sendbuffer_destroy(server->handle, FALSE); + } + server->handle = NULL; + } MODULE_DATA_DEINIT(server); server_connect_unref(server->connrec); -- cgit v1.2.3