From: Azat Khuzhin Date: Mon, 23 Nov 2015 12:05:19 +0000 (+0300) Subject: evdns: evdns_base_free(): fix UAF of evdns_base with @fail_requests X-Git-Tag: release-2.1.6-beta~90^2~9^2 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=00313c505079c0a93ceb9e26bce1a5be298062ef;p=libevent evdns: evdns_base_free(): fix UAF of evdns_base with @fail_requests 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 --- diff --git a/evdns.c b/evdns.c index 38cfd783..0955a289 100644 --- 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; }