]> granicus.if.org Git - libevent/commitdiff
evdns: evdns_base_free(): fix UAF of evdns_base with @fail_requests
authorAzat Khuzhin <a3at.mail@gmail.com>
Mon, 23 Nov 2015 12:05:19 +0000 (15:05 +0300)
committerAzat Khuzhin <a3at.mail@gmail.com>
Wed, 25 Nov 2015 10:09:02 +0000 (13:09 +0300)
If you call evdns_base_free() with @fail_requests == 1, then it will defer
callback with DNS_ERR_SHUTDOWN, but that callback (internal) uses
data->evdns_base, but we already freed that evdns base, so we can't do
this, fix this by checking @result to DNS_ERR_SHUTDOWN.

Fixes: regress dns/client_fail_requests_getaddrinfo
Fixes: #269
evdns.c

diff --git a/evdns.c b/evdns.c
index 38cfd78315ab230c766c4104179b3b25af50fba3..0955a28984045aec29d399ce19def7a71154c2d7 100644 (file)
--- a/evdns.c
+++ b/evdns.c
@@ -4403,17 +4403,23 @@ evdns_getaddrinfo_gotresolve(int result, char type, int count,
                other_req = &data->ipv4_request;
        }
 
-       EVDNS_LOCK(data->evdns_base);
-       if (evdns_result_is_answer(result)) {
-               if (req->type == DNS_IPv4_A)
-                       ++data->evdns_base->getaddrinfo_ipv4_answered;
-               else
-                       ++data->evdns_base->getaddrinfo_ipv6_answered;
+       /** Called from evdns_base_free() with @fail_requests == 1 */
+       if (result != DNS_ERR_SHUTDOWN) {
+               EVDNS_LOCK(data->evdns_base);
+               if (evdns_result_is_answer(result)) {
+                       if (req->type == DNS_IPv4_A)
+                               ++data->evdns_base->getaddrinfo_ipv4_answered;
+                       else
+                               ++data->evdns_base->getaddrinfo_ipv6_answered;
+               }
+               user_canceled = data->user_canceled;
+               if (other_req->r == NULL)
+                       data->request_done = 1;
+               EVDNS_UNLOCK(data->evdns_base);
+       } else {
+               data->evdns_base = NULL;
+               user_canceled = data->user_canceled;
        }
-       user_canceled = data->user_canceled;
-       if (other_req->r == NULL)
-               data->request_done = 1;
-       EVDNS_UNLOCK(data->evdns_base);
 
        req->r = NULL;
 
@@ -4447,7 +4453,9 @@ evdns_getaddrinfo_gotresolve(int result, char type, int count,
                        /* The other request is still working; maybe it will
                         * succeed. */
                        /* XXXX handle failure from set_timeout */
-                       evdns_getaddrinfo_set_timeout(data->evdns_base, data);
+                       if (result != DNS_ERR_SHUTDOWN) {
+                               evdns_getaddrinfo_set_timeout(data->evdns_base, data);
+                       }
                        data->pending_error = err;
                        return;
                }