From: Stephan Bosch Date: Thu, 25 Feb 2016 21:46:11 +0000 (+0100) Subject: lib-http: client: Fixed crash happening when connection hits an error while handling... X-Git-Tag: 2.2.22.rc1~80 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=1f1ba791ba565d4a31b031fee95617490fe96b11;p=thirdparty%2Fdovecot%2Fcore.git lib-http: client: Fixed crash happening when connection hits an error while handling a new request. At this point the connection dies within the peer request handler loop. Before, the dying connection could free the peer when it ended up being obsolete. But because it was still in the loop, a segfault occurred as the loop continued. Fixed by deferring the the deallocation of the peer from the connection_lost() function to the request handler loop itself if it is active. --- diff --git a/src/lib-http/http-client-peer.c b/src/lib-http/http-client-peer.c index d2e000806f..53aebfcaa5 100644 --- a/src/lib-http/http-client-peer.c +++ b/src/lib-http/http-client-peer.c @@ -200,15 +200,6 @@ http_client_peer_disconnect(struct http_client_peer *peer) i_assert(array_count(&peer->conns) == 0); } -static void http_client_peer_check_idle(struct http_client_peer *peer) -{ - struct http_client_connection *const *conn_idx; - - array_foreach(&peer->conns, conn_idx) { - http_client_connection_check_idle(*conn_idx); - } -} - static unsigned int http_client_peer_requests_pending(struct http_client_peer *peer, unsigned int *num_urgent_r) @@ -226,6 +217,24 @@ http_client_peer_requests_pending(struct http_client_peer *peer, return num_requests; } +static void http_client_peer_check_idle(struct http_client_peer *peer) +{ + struct http_client_connection *const *conn_idx; + unsigned int num_urgent = 0; + + if (array_count(&peer->conns) == 0 && + http_client_peer_requests_pending(peer, &num_urgent) == 0) { + /* no connections or pending requests; die immediately */ + http_client_peer_free(&peer); + return; + } + + /* check all connections for idle status */ + array_foreach(&peer->conns, conn_idx) { + http_client_connection_check_idle(*conn_idx); + } +} + static void http_client_peer_handle_requests_real(struct http_client_peer *peer) { @@ -263,6 +272,7 @@ http_client_peer_handle_requests_real(struct http_client_peer *peer) return; } + peer->handling_requests = TRUE; t_array_init(&conns_avail, array_count(&peer->conns)); do { array_clear(&conns_avail); @@ -336,9 +346,13 @@ http_client_peer_handle_requests_real(struct http_client_peer *peer) "No more requests to service for this peer " "(%u connections exist)", array_count(&peer->conns)); http_client_peer_check_idle(peer); - return; + break; } } while (statistics_dirty); + peer->handling_requests = FALSE; + + if (num_pending == 0) + return; i_assert(idle == 0); @@ -679,13 +693,21 @@ void http_client_peer_connection_lost(struct http_client_peer *peer) http_client_peer_debug(peer, "Lost a connection (%d connections left)", array_count(&peer->conns)); - /* if there are pending requests for this peer, create a new connection - for them. */ - http_client_peer_trigger_request_handler(peer); + if (peer->handling_requests) { + /* we got here from the request handler loop */ + return; + } + /* check if peer is still relevant */ if (array_count(&peer->conns) == 0 && - http_client_peer_requests_pending(peer, &num_urgent) == 0) + http_client_peer_requests_pending(peer, &num_urgent) == 0) { http_client_peer_free(&peer); + return; + } + + /* if there are pending requests for this peer, create a new connection + for them. */ + http_client_peer_trigger_request_handler(peer); } unsigned int diff --git a/src/lib-http/http-client-private.h b/src/lib-http/http-client-private.h index f78011f6fa..965b81b155 100644 --- a/src/lib-http/http-client-private.h +++ b/src/lib-http/http-client-private.h @@ -190,6 +190,7 @@ struct http_client_peer { unsigned int seen_100_response:1;/* expect: 100-continue succeeded before */ unsigned int allows_pipelining:1;/* peer is known to allow persistent connections */ + unsigned int handling_requests:1;/* currently running request handler */ }; struct http_client_queue {