evdns: fix race condition in evdns_getaddrinfo()
authorSergey Fionov <sfionov@adguard.com>
Wed, 1 Aug 2018 21:35:28 +0000 (00:35 +0300)
committerAzat Khuzhin <a3at.mail@gmail.com>
Thu, 2 Aug 2018 06:23:30 +0000 (09:23 +0300)
evdns_getaddrinfo() starts two parallel requests for A and AAAA record.
But if request is created from thread different from dns_base's, request of A record is
started immediately and may result in calling free_getaddrinfo_request() from
evdns_getaddrinfo_gotresolve() because `other_req' doesn't exist yet.

After that, request of AAAA record starts and finishes, and evdns_getaddrinfo_gotresolve()
is called again for structure that is already freed.

This commits adds locking into evdns_getaddrinfo() function.

evdns.c

diff --git a/evdns.c b/evdns.c
index 400429257f527f11c0e0cec657eb404e4dfe6f77..a3f1226aeeeab3ddd8f968a476a1c4bdd3ee1ea0 100644 (file)
--- a/evdns.c
+++ b/evdns.c
@@ -4637,6 +4637,7 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
        int err;
        int port = 0;
        int want_cname = 0;
+       int started = 0;
 
        if (!dns_base) {
                dns_base = current_base;
@@ -4715,6 +4716,8 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
         * launching those requests. (XXX we don't do that yet.)
         */
 
+       EVDNS_LOCK(dns_base);
+
        if (hints.ai_family != PF_INET6) {
                log(EVDNS_LOG_DEBUG, "Sending request for %s on ipv4 as %p",
                    nodename, &data->ipv4_request);
@@ -4741,7 +4744,11 @@ evdns_getaddrinfo(struct evdns_base *dns_base,
        evtimer_assign(&data->timeout, dns_base->event_base,
            evdns_getaddrinfo_timeout_cb, data);
 
-       if (data->ipv4_request.r || data->ipv6_request.r) {
+       started = (data->ipv4_request.r || data->ipv6_request.r);
+
+       EVDNS_UNLOCK(dns_base);
+
+       if (started) {
                return data;
        } else {
                mm_free(data);