Search code examples
csocketstcpmemory-leaksvalgrind

Valgrind memory leaks because of getaddrinfo()


I connect a TCP stream socket to a given hostname and port. To get the host's IP address I use getaddrinfo() as follows

struct addrinfo hints;
struct addrinfo *result, *server;

memset(&hints, 0, sizeof(struct addrinfo)); // fill server with 0

hints.ai_family = AF_INET;   // set address family
hints.ai_protocol=0;    // set protocol type to auto
hints.ai_socktype = SOCK_STREAM;    // set type of socket to stream socket

if(getaddrinfo(HOSTNAME,PORT,&hints,&result)){
    perror("Could not resolve IP from given hostname and port (getaddrinfo)");
    freeaddrinfo(result);
    return -1;
}

if(print) printf("Connecting socket ... \n");
int connectFlag=1;
for(server=result;server!=NULL;server=server->ai_next){
    if(connect(sockfd,server->ai_addr,server->ai_addrlen)==-1){
        continue;
    }
    if(print){
        printf("Socket successfully connected\n");
    }
    break;
}
    
if(!connectFlag){
    printf("Could not connect to server %s.\n",HOSTNAME);
    close(sockfd);
    freeaddrinfo(result);
    return -1;
}

freeaddrinfo(result);

All this happens inside a function that reveivecs a parameter

int sockfd = socket(AF_INET,SOCK_STREAM,0);

from another function. Everything works out fine, the socket connects and I receive and send messages as expected.

The problem is, when I check my created binary with

valgrind --leak-check=full --trace-children=yes ./binary

I get 3313 bytes are still reachable. When resolve the host manually and connect directly using the IP I get no leaks. I replaced getaddrinfo() with the older gethostbyname() and got the exact same amount of still reachable bytes.

How can I get rid of these leaks?

Valgrind output

   % valgrind --leak-check=full --show-leak-kinds=all ./play
==16353== Memcheck, a memory error detector
==16353== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==16353== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==16353== Command: ./a.out
==16353== 
==16353== 
==16353== HEAP SUMMARY:
==16353==     in use at exit: 3,313 bytes in 10 blocks
==16353==   total heap usage: 96 allocs, 86 frees, 54,234 bytes allocated
==16353== 
==16353== 64 bytes in 2 blocks are still reachable in loss record 1 of 5
==16353==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353==    by 0x4016790: _dl_close_worker.part.0 (dl-close.c:393)
==16353==    by 0x4017029: _dl_close_worker (dl-close.c:125)
==16353==    by 0x4017029: _dl_close (dl-close.c:822)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==    by 0x4F7CBAE: dlerror_run (dl-libc.c:46)
==16353==    by 0x4F7CBAE: __libc_dlclose (dl-libc.c:222)
==16353==    by 0x4FADB66: free_mem (in /lib/x86_64-linux-gnu/libc-2.23.so)
==16353==    by 0x4FAD78F: __libc_freeres (in /lib/x86_64-linux-gnu/libc-2.23.so)
==16353==    by 0x4A2868C: _vgnU_freeres (in /usr/lib/valgrind/vgpreload_core-amd64-linux.so)
==16353==    by 0x4E73FAA: __run_exit_handlers (exit.c:97)
==16353==    by 0x4E74044: exit (exit.c:104)
==16353==    by 0x404162: cleanUp (in /home/a)
==16353==    by 0x401CB7: dispatch (in /home/a)
==16353== 
==16353== 71 bytes in 2 blocks are still reachable in loss record 2 of 5
==16353==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353==    by 0x401CD89: strdup (strdup.c:42)
==16353==    by 0x401860E: _dl_load_cache_lookup (dl-cache.c:311)
==16353==    by 0x4008F98: _dl_map_object (dl-load.c:2342)
==16353==    by 0x400D9D1: openaux (dl-deps.c:63)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==    by 0x400E011: _dl_map_object_deps (dl-deps.c:254)
==16353==    by 0x4015411: dl_open_worker (dl-open.c:280)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==    by 0x4014BD8: _dl_open (dl-open.c:660)
==16353==    by 0x4F7C9BC: do_dlopen (dl-libc.c:87)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== 
==16353== 71 bytes in 2 blocks are still reachable in loss record 3 of 5
==16353==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353==    by 0x400BD23: _dl_new_object (dl-object.c:165)
==16353==    by 0x400633C: _dl_map_object_from_fd (dl-load.c:1006)
==16353==    by 0x4008A56: _dl_map_object (dl-load.c:2476)
==16353==    by 0x400D9D1: openaux (dl-deps.c:63)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==    by 0x400E011: _dl_map_object_deps (dl-deps.c:254)
==16353==    by 0x4015411: dl_open_worker (dl-open.c:280)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==    by 0x4014BD8: _dl_open (dl-open.c:660)
==16353==    by 0x4F7C9BC: do_dlopen (dl-libc.c:87)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== 
==16353== 744 bytes in 2 blocks are still reachable in loss record 4 of 5
==16353==    at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353==    by 0x4011EED: _dl_check_map_versions (dl-version.c:293)
==16353==    by 0x4015948: dl_open_worker (dl-open.c:286)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==    by 0x4014BD8: _dl_open (dl-open.c:660)
==16353==    by 0x4F7C9BC: do_dlopen (dl-libc.c:87)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==    by 0x4F7CA73: dlerror_run (dl-libc.c:46)
==16353==    by 0x4F7CA73: __libc_dlopen_mode (dl-libc.c:163)
==16353==    by 0x4F623BD: nss_load_library (nsswitch.c:358)
==16353==    by 0x4F630F3: __nss_lookup_function (nsswitch.c:466)
==16353==    by 0x4F630F3: __nss_next2 (nsswitch.c:242)
==16353==    by 0x4F535FA: gethostbyname2_r@@GLIBC_2.2.5 (getXXbyYY_r.c:281)
==16353==    by 0x4F259DE: gaih_inet (getaddrinfo.c:622)
==16353== 
==16353== 2,363 bytes in 2 blocks are still reachable in loss record 5 of 5
==16353==    at 0x4C2FB55: calloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==16353==    by 0x400BA25: _dl_new_object (dl-object.c:75)
==16353==    by 0x400633C: _dl_map_object_from_fd (dl-load.c:1006)
==16353==    by 0x4008A56: _dl_map_object (dl-load.c:2476)
==16353==    by 0x400D9D1: openaux (dl-deps.c:63)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==    by 0x400E011: _dl_map_object_deps (dl-deps.c:254)
==16353==    by 0x4015411: dl_open_worker (dl-open.c:280)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353==    by 0x4014BD8: _dl_open (dl-open.c:660)
==16353==    by 0x4F7C9BC: do_dlopen (dl-libc.c:87)
==16353==    by 0x4010393: _dl_catch_error (dl-error.c:187)
==16353== 
==16353== LEAK SUMMARY:
==16353==    definitely lost: 0 bytes in 0 blocks
==16353==    indirectly lost: 0 bytes in 0 blocks
==16353==      possibly lost: 0 bytes in 0 blocks
==16353==    still reachable: 3,313 bytes in 10 blocks
==16353==         suppressed: 0 bytes in 0 blocks
==16353== 
==16353== For counts of detected and suppressed errors, rerun with: -v
==16353== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

If I add the valgrind option --trace-children=yes valgrind checks a child process. I did not output this because the leak is happening in this parent process.

EDIT

It turned out we were not able to reproduce the valgrind output on other hardware. On this one server getaddrinfo() produced this valgrind output as well as gethostbyname(). We did not further observed where this behaviour might come from.


Solution

  • "Still reachable" means that the program has allocated the memory AND it still has a reference to it at the time of termination. It probably means that getaddrinfo() and gethostbyname() on the first call allocates memory which is reused.

    This SO-answer might also covers the same issue Valgrind showing still reachable memory leak with getaddrinfo