The following code is an excerpt of my C programm to connect to a server:
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <stdbool.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#define PORTNUMBER 443
#define HOSTNAME "www.google.com"
int main() {
struct addrinfo hints, * res, * matchingIP;
char addrstr[99];
char port[20];
snprintf(port, sizeof(port), "%d", PORTNUMBER); // PORTNUMBER as char*
int sock;
memset( & hints, 0, sizeof hints);
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = 0;
hints.ai_flags = AI_CANONNAME;
int status = getaddrinfo(HOSTNAME, port, & hints, & matchingIP); // Memory Leak occurs here?
if (status < 0) {
perror("No IP address found.");
}
for (res = matchingIP; res != NULL; res = res -> ai_next) {
sock = socket(res -> ai_family, res -> ai_socktype, res -> ai_protocol);
if (sock < 0) {
perror("No socket created.");
continue;
}
if (connect(sock, res -> ai_addr, res -> ai_addrlen) < 0) {
perror("Client not connected.");
continue;
}
break;
}
if (res == NULL) {
perror("Client could not connect.");
return EXIT_FAILURE;
}
inet_ntop(res -> ai_family, & ((struct sockaddr_in * ) res -> ai_addr) -> sin_addr, addrstr, sizeof addrstr);
printf("IPv4 address: %s (%s)\n", addrstr, res -> ai_canonname);
freeaddrinfo(matchingIP);
}
Now when I run valgrind, it shows there is "still reachable" memory, I've narrowed it down to the getaddrinfo
call (the leak does not appear before). Here is the full valgrind log:
==566543== HEAP SUMMARY:
==566543== in use at exit: 3,509 bytes in 8 blocks
==566543== total heap usage: 112 allocs, 104 frees, 112,649 bytes allocated
==566543==
==566543== 38 bytes in 1 blocks are still reachable in loss record 1 of 7
==566543== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==566543== by 0x401F5DE: strdup (strdup.c:42)
==566543== by 0x4019A91: _dl_load_cache_lookup (dl-cache.c:338)
==566543== by 0x400A989: _dl_map_object (dl-load.c:2102)
==566543== by 0x400F514: openaux (dl-deps.c:64)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x400F962: _dl_map_object_deps (dl-deps.c:248)
==566543== by 0x4015DAF: dl_open_worker (dl-open.c:571)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4015609: _dl_open (dl-open.c:837)
==566543== by 0x4A16AE0: do_dlopen (dl-libc.c:96)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543==
==566543== 38 bytes in 1 blocks are still reachable in loss record 2 of 7
==566543== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==566543== by 0x400D5B7: _dl_new_object (dl-object.c:196)
==566543== by 0x4006E96: _dl_map_object_from_fd (dl-load.c:997)
==566543== by 0x400A61A: _dl_map_object (dl-load.c:2236)
==566543== by 0x400F514: openaux (dl-deps.c:64)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x400F962: _dl_map_object_deps (dl-deps.c:248)
==566543== by 0x4015DAF: dl_open_worker (dl-open.c:571)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4015609: _dl_open (dl-open.c:837)
==566543== by 0x4A16AE0: do_dlopen (dl-libc.c:96)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543==
==566543== 45 bytes in 1 blocks are still reachable in loss record 3 of 7
==566543== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==566543== by 0x401F5DE: strdup (strdup.c:42)
==566543== by 0x4019A91: _dl_load_cache_lookup (dl-cache.c:338)
==566543== by 0x400A989: _dl_map_object (dl-load.c:2102)
==566543== by 0x4015D46: dl_open_worker (dl-open.c:513)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4015609: _dl_open (dl-open.c:837)
==566543== by 0x4A16AE0: do_dlopen (dl-libc.c:96)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4A17C12: _dl_catch_error (dl-error-skeleton.c:227)
==566543== by 0x4A16C14: dlerror_run (dl-libc.c:46)
==566543== by 0x4A16C14: __libc_dlopen_mode (dl-libc.c:195)
==566543== by 0x49FA8CB: nss_load_library (nsswitch.c:359)
==566543==
==566543== 45 bytes in 1 blocks are still reachable in loss record 4 of 7
==566543== at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==566543== by 0x400D5B7: _dl_new_object (dl-object.c:196)
==566543== by 0x4006E96: _dl_map_object_from_fd (dl-load.c:997)
==566543== by 0x400A61A: _dl_map_object (dl-load.c:2236)
==566543== by 0x4015D46: dl_open_worker (dl-open.c:513)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4015609: _dl_open (dl-open.c:837)
==566543== by 0x4A16AE0: do_dlopen (dl-libc.c:96)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4A17C12: _dl_catch_error (dl-error-skeleton.c:227)
==566543== by 0x4A16C14: dlerror_run (dl-libc.c:46)
==566543== by 0x4A16C14: __libc_dlopen_mode (dl-libc.c:195)
==566543== by 0x49FA8CB: nss_load_library (nsswitch.c:359)
==566543==
==566543== 936 bytes in 2 blocks are still reachable in loss record 5 of 7
==566543== at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==566543== by 0x401331C: _dl_check_map_versions (dl-version.c:274)
==566543== by 0x40160FC: dl_open_worker (dl-open.c:577)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4015609: _dl_open (dl-open.c:837)
==566543== by 0x4A16AE0: do_dlopen (dl-libc.c:96)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4A17C12: _dl_catch_error (dl-error-skeleton.c:227)
==566543== by 0x4A16C14: dlerror_run (dl-libc.c:46)
==566543== by 0x4A16C14: __libc_dlopen_mode (dl-libc.c:195)
==566543== by 0x49FA8CB: nss_load_library (nsswitch.c:359)
==566543== by 0x49FB178: __nss_lookup_function (nsswitch.c:467)
==566543== by 0x49BB4BE: gaih_inet.constprop.0 (getaddrinfo.c:800)
==566543==
==566543== 1,200 bytes in 1 blocks are still reachable in loss record 6 of 7
==566543== at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==566543== by 0x400D283: _dl_new_object (dl-object.c:89)
==566543== by 0x4006E96: _dl_map_object_from_fd (dl-load.c:997)
==566543== by 0x400A61A: _dl_map_object (dl-load.c:2236)
==566543== by 0x400F514: openaux (dl-deps.c:64)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x400F962: _dl_map_object_deps (dl-deps.c:248)
==566543== by 0x4015DAF: dl_open_worker (dl-open.c:571)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4015609: _dl_open (dl-open.c:837)
==566543== by 0x4A16AE0: do_dlopen (dl-libc.c:96)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543==
==566543== 1,207 bytes in 1 blocks are still reachable in loss record 7 of 7
==566543== at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==566543== by 0x400D283: _dl_new_object (dl-object.c:89)
==566543== by 0x4006E96: _dl_map_object_from_fd (dl-load.c:997)
==566543== by 0x400A61A: _dl_map_object (dl-load.c:2236)
==566543== by 0x4015D46: dl_open_worker (dl-open.c:513)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4015609: _dl_open (dl-open.c:837)
==566543== by 0x4A16AE0: do_dlopen (dl-libc.c:96)
==566543== by 0x4A17B47: _dl_catch_exception (dl-error-skeleton.c:208)
==566543== by 0x4A17C12: _dl_catch_error (dl-error-skeleton.c:227)
==566543== by 0x4A16C14: dlerror_run (dl-libc.c:46)
==566543== by 0x4A16C14: __libc_dlopen_mode (dl-libc.c:195)
==566543== by 0x49FA8CB: nss_load_library (nsswitch.c:359)
==566543==
==566543== LEAK SUMMARY:
==566543== definitely lost: 0 bytes in 0 blocks
==566543== indirectly lost: 0 bytes in 0 blocks
==566543== possibly lost: 0 bytes in 0 blocks
==566543== still reachable: 3,509 bytes in 8 blocks
==566543== suppressed: 0 bytes in 0 blocks
==566543==
==566543== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
I've read online several times that while "still reachable" memory leaks are memory leaks, they are not ones I should worry about. Sadly for me, it is a formal requirement that valgrind reports absolutely no memory leaks, including still reachable. Is there any chance I am able to fix it or is it the fault of the library? If the latter - what are suitable alternatives? As far as I am are the host runs using IPv6 - perhaps that's relevant.
Edit: The project is compiled with the -std=c99
flag and defines the posix C source as #define _POSIX_C_SOURCE 200809L
. The valgrind flags are valgrind --leak-check=full --show-leak-kinds=all --trace-children=yes --log-file=log.txt --track-origins=yes -s
and I'm developing on Ubuntu 20.04.6 LTS
Another edit: Minimal reproducible example
My opinion is that this is most likely a GNU libc bug.
Oh look.
Valgrind saying it's a GNU libc bug
GNU libc bug
GNU libc includes a cleanup function,
__libc_freeres()
that gets called by Valgrind upon a clean exit. It sounds like someone added some dynamic loading and there's missing cleanup of the dynamic loading name cache from __libc_freeres.I could add that to the Valgrind default supressions. That would only be the last resort if the GNU libc devs refuse to fix the issue. The above bug has been open for getting on for 2 years so it's not looking very optimistic.
If the goal there is to improve your software quality that's a good thing. But if the goal is only to give the appearance of quality and to enable the suits to make powerpoint presentatons with a page showing zero leaks then that's a bad thing. That's a slippery slope that leads to huge suppression files that risk hiding genuine issues.
Having said that, personally I would add a suppression for this.