From d52e8e1144a4abeffa0cf404d56d0c0d2850271b Mon Sep 17 00:00:00 2001 From: Stefan Becker Date: Sat, 28 Jun 2014 20:16:14 +0300 Subject: [PATCH] Fix #257: Windows 7: SIPE crashes after a minute When trying to simulate the same situation, i.e. all EWS autodiscover attempts fail with a DNS error, I noticed a slight difference between my debug logs and the debug logs provided by the reporters. I can only explain this difference if g_hash_table_remove() calls the value destroy callback *before* removing the hash table entry. In our case the destroy callback triggers the EWS HTTP callback which immediately generates another HTTP request. In one situation that new HTTP request has the same host:port key as the just "removed" entry and the SIPE HTTP stack tries to re-establish the connection which is actually already invalid. Circumvent this race condition by using g_hash_table_steal() and calling the value destroy callback by hand. (cherry picked from commit 3893f9589a0440d89f0c9519f5b46b2e6d947a37) --- src/core/sipe-http-transport.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/core/sipe-http-transport.c b/src/core/sipe-http-transport.c index c882736a..6903b944 100644 --- a/src/core/sipe-http-transport.c +++ b/src/core/sipe-http-transport.c @@ -111,9 +111,20 @@ static void sipe_http_transport_drop(struct sipe_http *http, conn->host_port, message ? message : "REASON UNKNOWN"); - /* this triggers sipe_http_transport_free */ - g_hash_table_remove(http->connections, - conn->host_port); + /* + * It seems that the value destroy function can be called *before* the + * entry is removed from the hash table. As it is possible that the user + * callback, triggered by sipe_http_transport_free(), generates a new + * HTTP request to the same host:port combination immediately, then + * sipe_http_transport_new() will find the *invalid* entry and try + * to re-establish the connection. This will lead to a crash as soon as + * the memory for the invalid entry will be freed. + * + * Therefore we don't use g_hash_table_remove() here. + */ + g_hash_table_steal(http->connections, conn->host_port); + sipe_http_transport_free(conn); + /* conn is no longer valid */ } static void start_timer(struct sipe_core_private *sipe_private, -- 2.11.4.GIT