From: Stephan Bosch Date: Mon, 1 Jul 2019 23:04:03 +0000 (-0400) Subject: lib-http: guard against hshared use-after-free X-Git-Tag: 2.3.9~405 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=6236f367d53c4b6a39128f692af6926019b826b5;p=thirdparty%2Fdovecot%2Fcore.git lib-http: guard against hshared use-after-free This fixes a race condition where the http_client_host_shared_idle_timeout() function would get called with an already freed hshared argument. Specifically, the situation arises from the hshared idle timeout calling http_client_host_shared_free(), which removes the timeout and then proceeds to free the client queue. The client queue freeing code indirectly calls http_client_host_shared_check_idle(), which notices that there is no idle timeout and allocates one. The backtrace at the point of this new timeout allocation: frame #3: 0x00007f0c775897f0 libdovecot.so.0`timeout_add_to(...) ioloop.c:280 frame #4: 0x00007f0c7751a45f libdovecot.so.0`http_client_host_shared_check_idle(hshared=) at http-client-host.c:69 frame #5: 0x00007f0c7750de89 libdovecot.so.0`http_client_request_error(_req=, status=9000, error="") at http-client-request.c:1525 frame #6: 0x00007f0c77517f38 libdovecot.so.0`http_client_queue_fail_full(queue=0x000055e13cff0e10, status=9000, error="", all=) at http-client-queue.c:183 frame #7: 0x00007f0c77518baa libdovecot.so.0`http_client_queue_free(queue=0x000055e13cff0e10) at http-client-queue.c:141 frame #8: 0x00007f0c7751a8bc libdovecot.so.0`http_client_host_free_shared(_host=) at http-client-host.c:391 frame #9: 0x00007f0c7751ab4c libdovecot.so.0`http_client_host_shared_free(_hshared=0x00007ffdac109e48) at http-client-host.c:294 frame #10: 0x00007f0c7751ace8 libdovecot.so.0`http_client_host_shared_idle_timeout(hshared=) at http-client-host.c:40 frame #11: 0x00007f0c7758a1a4 libdovecot.so.0`io_loop_handle_timeouts at ioloop.c:682 frame #12: 0x00007f0c7758a089 libdovecot.so.0`io_loop_handle_timeouts(ioloop=0x000055e13cfc8d80) at ioloop.c:696 frame #13: 0x00007f0c7758befc libdovecot.so.0`io_loop_handler_run_internal(ioloop=0x000055e13cfc8d80) at ioloop-select.c:126 frame #14: 0x00007f0c7758a56d libdovecot.so.0`io_loop_handler_run(ioloop=) at ioloop.c:767 frame #15: 0x00007f0c7758a798 libdovecot.so.0`io_loop_run(ioloop=0x000055e13cfc8d80) at ioloop.c:740 frame #16: 0x00007f0c774f61eb libdovecot.so.0`master_service_run(service=0x000055e13cfc8c10, callback=) at master-service.c:782 frame #17: 0x000055e13b48e3a5 stats`main(argc=, argv=) at main.c:99 frame #18: 0x00007f0c771092e1 libc.so.6`__libc_start_main + 241 frame #19: 0x000055e13b48e41a stats`_start + 42 --- diff --git a/src/lib-http/http-client-host.c b/src/lib-http/http-client-host.c index 701558e4f5..ebb909939c 100644 --- a/src/lib-http/http-client-host.c +++ b/src/lib-http/http-client-host.c @@ -44,6 +44,8 @@ http_client_host_shared_check_idle( struct http_client_host *host; int timeout = 0; + if (hshared->destroyed) + return; if (hshared->to_idle != NULL) return; @@ -266,6 +268,10 @@ void http_client_host_shared_free(struct http_client_host_shared **_hshared) struct http_client_host *host; const char *hostname = hshared->name; + if (hshared->destroyed) + return; + hshared->destroyed = TRUE; + e_debug(hshared->event, "Host destroy"); timeout_remove(&hshared->to_idle); diff --git a/src/lib-http/http-client-private.h b/src/lib-http/http-client-private.h index 9aa2bdd578..c870b2a36b 100644 --- a/src/lib-http/http-client-private.h +++ b/src/lib-http/http-client-private.h @@ -344,6 +344,7 @@ struct http_client_host_shared { /* timeouts */ struct timeout *to_idle; + bool destroyed:1; /* shared host object is being destroyed */ bool unix_local:1; bool explicit_ip:1; };