]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MEDIUM: standard: Fix memory leak in str2ip2()
authorTim Duesterhus <tim@bastelstu.be>
Sun, 21 Jan 2018 21:11:17 +0000 (22:11 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 5 Feb 2018 20:04:15 +0000 (21:04 +0100)
An haproxy compiled with:

> make -j4 all TARGET=linux2628 USE_GETADDRINFO=1

And running with a configuration like this:

  defaults
   log global
   mode http
   option httplog
   option dontlognull
   timeout connect 5000
   timeout client  50000
   timeout server  50000

  frontend fe
   bind :::8080 v4v6

   default_backend be

  backend be
   server s example.com:80 check

Will leak memory inside `str2ip2()`, because the list `result` is not
properly freed in success cases:

==18875== 140 (76 direct, 64 indirect) bytes in 1 blocks are definitely lost in loss record 87 of 111
==18875==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18875==    by 0x537A565: gaih_inet (getaddrinfo.c:1223)
==18875==    by 0x537DD5D: getaddrinfo (getaddrinfo.c:2425)
==18875==    by 0x4868E5: str2ip2 (standard.c:733)
==18875==    by 0x43F28B: srv_set_addr_via_libc (server.c:3767)
==18875==    by 0x43F50A: srv_iterate_initaddr (server.c:3879)
==18875==    by 0x43F50A: srv_init_addr (server.c:3944)
==18875==    by 0x475B30: init (haproxy.c:1595)
==18875==    by 0x40406D: main (haproxy.c:2479)

The exists as long as the usage of getaddrinfo in that function exists,
it was introduced in commit:
d5f4328efd5f4eaa7c89cad9773124959195430a

v1.5-dev8 is the first tag containing this comment, the fix
should be backported to haproxy 1.5 and newer.

src/standard.c

index 707e2c716664eed6021d58a6f2d0c7f465340568..b6bb1bc019b33e947e6fa362b094b3678ceea729 100644 (file)
@@ -722,6 +722,7 @@ struct sockaddr_storage *str2ip2(const char *str, struct sockaddr_storage *sa, i
 #ifdef USE_GETADDRINFO
        if (global.tune.options & GTUNE_USE_GAI) {
                struct addrinfo hints, *result;
+               int success = 0;
 
                memset(&result, 0, sizeof(result));
                memset(&hints, 0, sizeof(hints));
@@ -733,23 +734,30 @@ struct sockaddr_storage *str2ip2(const char *str, struct sockaddr_storage *sa, i
                if (getaddrinfo(str, NULL, &hints, &result) == 0) {
                        if (!sa->ss_family || sa->ss_family == AF_UNSPEC)
                                sa->ss_family = result->ai_family;
-                       else if (sa->ss_family != result->ai_family)
+                       else if (sa->ss_family != result->ai_family) {
+                               freeaddrinfo(result);
                                goto fail;
+                       }
 
                        switch (result->ai_family) {
                        case AF_INET:
                                memcpy((struct sockaddr_in *)sa, result->ai_addr, result->ai_addrlen);
                                set_host_port(sa, port);
-                               return sa;
+                               success = 1;
+                               break;
                        case AF_INET6:
                                memcpy((struct sockaddr_in6 *)sa, result->ai_addr, result->ai_addrlen);
                                set_host_port(sa, port);
-                               return sa;
+                               success = 1;
+                               break;
                        }
                }
 
                if (result)
                        freeaddrinfo(result);
+
+               if (success)
+                       return sa;
        }
 #endif
        /* try to resolve an IPv4/IPv6 hostname */