]> granicus.if.org Git - libevent/commitdiff
evutil: Fix evutil_freeaddrinfo
authorTomas Gonzalez <tomas@fuel7.com>
Mon, 1 Nov 2021 17:01:06 +0000 (10:01 -0700)
committerTomas Gonzalez <tomas@fuel7.com>
Thu, 4 Nov 2021 22:46:59 +0000 (15:46 -0700)
During testing on win32, util/getaddrinfo failed with NULL hint info

     r = evutil_getaddrinfo("www.google.com", NULL, NULL, &ai);

throwing a critical heap exception when evutil_freeaddrinfo is called.
This is because of improper use of freeaddrinfo when nodes within the
ai structure are allocated using mm_malloc or mm_calloc
(EVUTIL_AI_LIBEVENT_ALLOCATED)

This adds the flag in apply_socktype_protocol_hack and walks the linked
list in evutil_freeaddrinfo removing linked list nodes that are custom
allocated before calling freeaddrinfo.

evutil.c
test/regress_util.c

index 69c6f205a623f581961323c7dfc05096eb7ca6d6..c321eb60fe82badf1e389e70eed5c66389e1cf9d 100644 (file)
--- a/evutil.c
+++ b/evutil.c
@@ -1568,6 +1568,14 @@ apply_socktype_protocol_hack(struct evutil_addrinfo *ai)
                ai->ai_protocol = IPPROTO_TCP;
                ai_new->ai_socktype = SOCK_DGRAM;
                ai_new->ai_protocol = IPPROTO_UDP;
+               ai_new->ai_flags = EVUTIL_AI_LIBEVENT_ALLOCATED;
+               if (ai_new->ai_canonname != NULL) {
+                       ai_new->ai_canonname = mm_strdup(ai_new->ai_canonname);
+                       if (ai_new->ai_canonname == NULL) {
+                               mm_free(ai_new);
+                               return -1;
+                       }
+               }
 
                ai_new->ai_next = ai->ai_next;
                ai->ai_next = ai_new;
@@ -1771,11 +1779,33 @@ void
 evutil_freeaddrinfo(struct evutil_addrinfo *ai)
 {
 #ifdef EVENT__HAVE_GETADDRINFO
-       if (!(ai->ai_flags & EVUTIL_AI_LIBEVENT_ALLOCATED)) {
-               freeaddrinfo(ai);
-               return;
+       struct evutil_addrinfo *ai_prev = NULL;
+       struct evutil_addrinfo *ai_temp = ai;
+       /* Linked list may be the result of a native getaddrinfo() call plus
+        * locally allocated nodes, Before releasing it using freeaddrinfo(),
+        * these custom structs need to be freed separately.
+        */
+       while (ai_temp) {
+               struct evutil_addrinfo *next = ai_temp->ai_next;
+               if (ai_temp->ai_flags & EVUTIL_AI_LIBEVENT_ALLOCATED) {
+                       /* Remove this node from the linked list */
+                       if (ai_temp->ai_canonname)
+                               mm_free(ai_temp->ai_canonname);
+                       mm_free(ai_temp);
+                       if (ai_prev == NULL) {
+                               ai = next;
+                       } else {
+                               ai_prev->ai_next = next;
+                       }
+
+               } else {
+                       ai_prev = ai_temp;
+               }
+               ai_temp = next;
        }
-#endif
+       if (ai != NULL)
+               freeaddrinfo(ai);
+#else
        while (ai) {
                struct evutil_addrinfo *next = ai->ai_next;
                if (ai->ai_canonname)
@@ -1783,6 +1813,7 @@ evutil_freeaddrinfo(struct evutil_addrinfo *ai)
                mm_free(ai);
                ai = next;
        }
+#endif
 }
 
 static evdns_getaddrinfo_fn evdns_getaddrinfo_impl = NULL;
index 02cf5e1972644f1f1ac6f52f0b7e9a8a86dcae75..1f919fe88951a9bfd521a398611d26f9ff4f8225 100644 (file)
@@ -1005,6 +1005,15 @@ test_evutil_getaddrinfo(void *arg)
        struct evutil_addrinfo hints;
        int r;
 
+       /* Try NULL hint (win32 bug) */
+       hints.ai_family = PF_UNSPEC;
+       hints.ai_socktype = SOCK_STREAM;
+       r = evutil_getaddrinfo("www.google.com", NULL, NULL, &ai);
+       tt_int_op(r, ==, 0);
+       tt_assert(ai);
+       evutil_freeaddrinfo(ai);
+       ai = NULL;
+
        /* Try using it as a pton. */
        memset(&hints, 0, sizeof(hints));
        hints.ai_family = PF_UNSPEC;