]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-http: client: Fixed crash happening when connection hits an error while handling...
authorStephan Bosch <stephan@rename-it.nl>
Thu, 25 Feb 2016 21:46:11 +0000 (22:46 +0100)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 25 Feb 2016 22:00:57 +0000 (00:00 +0200)
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.

src/lib-http/http-client-peer.c
src/lib-http/http-client-private.h

index d2e000806f93da9ebe388781f762de0090731583..53aebfcaa5299dca3f2d7927dbcf5b78c1fe0600 100644 (file)
@@ -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
index f78011f6fa22413acfa24d8fde5b26a11da11980..965b81b155ca9759b8df4a370f265b24e857b813 100644 (file)
@@ -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 {