1

I'm trying to retrieve a linux group with some basic C code, but I encounter memory leaks with both getgrnam and getgrnam_r. The memory leaks only occurs when the linux group is missing in /etc/group.

#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <grp.h>
#include <pwd.h>

static char* group_name = "dummy";

static void test_getgrnam_r(void) {
    struct group grp;
    struct group* group = NULL;
    int bufsize = 0;
    int retval = -1;
    char* buf = NULL;

    bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
    if(bufsize == -1) {
        bufsize = 16384;
    }

    buf = calloc(1, bufsize);
    retval = getgrnam_r(group_name, &grp, buf, bufsize, &group);
    printf("retval = %d\n", retval);

    free(buf);
    free(group);
}

static void test_getgrnam(void) {
    struct group* group = getgrnam(group_name);
}

int main(void) {
    test_getgrnam();
    //test_getgrnam_r();
}

This code can be compiled with a simple gcc command:

gcc main.c -o out

and it can be run with valgrind to show the memory leaks:

valgrind --leak-check=full --show-leak-kinds=all ./out
==2255356== Memcheck, a memory error detector
==2255356== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==2255356== Using Valgrind-3.16.1 and LibVEX; rerun with -h for copyright info
==2255356== Command: ./out
==2255356== 
==2255356== 
==2255356== HEAP SUMMARY:
==2255356==     in use at exit: 5,177 bytes in 13 blocks
==2255356==   total heap usage: 77 allocs, 64 frees, 19,669 bytes allocated
==2255356== 
==2255356== 46 bytes in 1 blocks are still reachable in loss record 1 of 8
==2255356==    at 0x483877F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2255356==    by 0x401C26A: strdup (strdup.c:42)
==2255356==    by 0x40172FB: _dl_load_cache_lookup (dl-cache.c:338)
==2255356==    by 0x4009776: _dl_map_object (dl-load.c:2102)
==2255356==    by 0x4013D41: dl_open_worker (dl-open.c:513)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x40138F9: _dl_open (dl-open.c:837)
==2255356==    by 0x4986FCC: do_dlopen (dl-libc.c:96)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x4987C0E: _dl_catch_error (dl-error-skeleton.c:227)
==2255356==    by 0x49870A6: dlerror_run (dl-libc.c:46)
==2255356==    by 0x4987135: __libc_dlopen_mode (dl-libc.c:195)
==2255356== 
==2255356== 46 bytes in 1 blocks are still reachable in loss record 2 of 8
==2255356==    at 0x483877F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2255356==    by 0x400BFB7: _dl_new_object (dl-object.c:196)
==2255356==    by 0x4007255: _dl_map_object_from_fd (dl-load.c:997)
==2255356==    by 0x4009274: _dl_map_object (dl-load.c:2236)
==2255356==    by 0x4013D41: dl_open_worker (dl-open.c:513)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x40138F9: _dl_open (dl-open.c:837)
==2255356==    by 0x4986FCC: do_dlopen (dl-libc.c:96)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x4987C0E: _dl_catch_error (dl-error-skeleton.c:227)
==2255356==    by 0x49870A6: dlerror_run (dl-libc.c:46)
==2255356==    by 0x4987135: __libc_dlopen_mode (dl-libc.c:195)
==2255356== 
==2255356== 71 bytes in 2 blocks are still reachable in loss record 4 of 8
==2255356==    at 0x483877F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2255356==    by 0x401C26A: strdup (strdup.c:42)
==2255356==    by 0x40172FB: _dl_load_cache_lookup (dl-cache.c:338)
==2255356==    by 0x4009776: _dl_map_object (dl-load.c:2102)
==2255356==    by 0x400DDC0: openaux (dl-deps.c:64)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x400E138: _dl_map_object_deps (dl-deps.c:248)
==2255356==    by 0x4013DAA: dl_open_worker (dl-open.c:571)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x40138F9: _dl_open (dl-open.c:837)
==2255356==    by 0x4986FCC: do_dlopen (dl-libc.c:96)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356== 
==2255356== 71 bytes in 2 blocks are still reachable in loss record 5 of 8
==2255356==    at 0x483877F: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2255356==    by 0x400BFB7: _dl_new_object (dl-object.c:196)
==2255356==    by 0x4007255: _dl_map_object_from_fd (dl-load.c:997)
==2255356==    by 0x4009274: _dl_map_object (dl-load.c:2236)
==2255356==    by 0x400DDC0: openaux (dl-deps.c:64)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x400E138: _dl_map_object_deps (dl-deps.c:248)
==2255356==    by 0x4013DAA: dl_open_worker (dl-open.c:571)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x40138F9: _dl_open (dl-open.c:837)
==2255356==    by 0x4986FCC: do_dlopen (dl-libc.c:96)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356== 
==2255356== 1,204 bytes in 1 blocks are still reachable in loss record 6 of 8
==2255356==    at 0x483AB65: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2255356==    by 0x400BCDF: _dl_new_object (dl-object.c:89)
==2255356==    by 0x4007255: _dl_map_object_from_fd (dl-load.c:997)
==2255356==    by 0x4009274: _dl_map_object (dl-load.c:2236)
==2255356==    by 0x4013D41: dl_open_worker (dl-open.c:513)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x40138F9: _dl_open (dl-open.c:837)
==2255356==    by 0x4986FCC: do_dlopen (dl-libc.c:96)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x4987C0E: _dl_catch_error (dl-error-skeleton.c:227)
==2255356==    by 0x49870A6: dlerror_run (dl-libc.c:46)
==2255356==    by 0x4987135: __libc_dlopen_mode (dl-libc.c:195)
==2255356== 
==2255356== 1,296 bytes in 3 blocks are still reachable in loss record 7 of 8
==2255356==    at 0x483AB65: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2255356==    by 0x40116B6: _dl_check_map_versions (dl-version.c:274)
==2255356==    by 0x4013DF5: dl_open_worker (dl-open.c:577)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x40138F9: _dl_open (dl-open.c:837)
==2255356==    by 0x4986FCC: do_dlopen (dl-libc.c:96)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x4987C0E: _dl_catch_error (dl-error-skeleton.c:227)
==2255356==    by 0x49870A6: dlerror_run (dl-libc.c:46)
==2255356==    by 0x4987135: __libc_dlopen_mode (dl-libc.c:195)
==2255356==    by 0x496F355: nss_load_library (nsswitch.c:359)
==2255356==    by 0x496FBE8: __nss_lookup_function (nsswitch.c:467)
==2255356== 
==2255356== 2,395 bytes in 2 blocks are still reachable in loss record 8 of 8
==2255356==    at 0x483AB65: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==2255356==    by 0x400BCDF: _dl_new_object (dl-object.c:89)
==2255356==    by 0x4007255: _dl_map_object_from_fd (dl-load.c:997)
==2255356==    by 0x4009274: _dl_map_object (dl-load.c:2236)
==2255356==    by 0x400DDC0: openaux (dl-deps.c:64)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x400E138: _dl_map_object_deps (dl-deps.c:248)
==2255356==    by 0x4013DAA: dl_open_worker (dl-open.c:571)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356==    by 0x40138F9: _dl_open (dl-open.c:837)
==2255356==    by 0x4986FCC: do_dlopen (dl-libc.c:96)
==2255356==    by 0x4987B4F: _dl_catch_exception (dl-error-skeleton.c:208)
==2255356== 
==2255356== LEAK SUMMARY:
==2255356==    definitely lost: 0 bytes in 0 blocks
==2255356==    indirectly lost: 0 bytes in 0 blocks
==2255356==      possibly lost: 0 bytes in 0 blocks
==2255356==    still reachable: 5,129 bytes in 12 blocks
==2255356==         suppressed: 48 bytes in 1 blocks
==2255356== 
==2255356== For lists of detected and suppressed errors, rerun with: -s
==2255356== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

I have found some similar posts, but none of them seem to have a solution for this issue.

Some of them suggest using

extern void __libc_freeres (void);

But calling this function also doesn't seem to help. Based on the valgrind documentation, I think this function is already called by default when running valgrind.

--run-libc-freeres=<yes|no> [default: yes]

https://valgrind.org/docs/manual/manual-core.html

Does this mean I am stuck with a memory leak or is there a solution I have not found yet? Please let me know if I can provide extra information. I tried to keep my example as simple as possible.

Johan
  • 80
  • 6
  • *Does this mean I am stuck with a memory leak ...* Why are you so certain it's a memory leak? It could just be data loaded when a search for a missing GID forces shared objects to load so things like LDAP or NIS can be searched. Those libraries will **stay** loaded, along with any memory they allocate. – Andrew Henle Oct 06 '22 at 15:24
  • 1
    It's normal for libraries of a general purpose modern operating system (which use virtual memory) to not do `free()` at program exit, because it's known that the operating system will make the memory not even exist after exit. – hyde Oct 06 '22 at 15:25
  • 1
    Look up valgrind suppression files. – hyde Oct 06 '22 at 15:25
  • Per @hyde's comment: see [Should I free memory before exit?](https://stackoverflow.com/questions/36584062) and [What REALLY happens when you don't free after malloc before program termination?](https://stackoverflow.com/questions/654754). – Steve Summit Oct 06 '22 at 15:28
  • Try a recent Valgrind and glibc. – Paul Floyd Oct 06 '22 at 16:53
  • I've tried to use a valgrind suppression file, but didn't manage to suppress the error. I suppose I did something wrong. I know that memory is freed when a program shuts down, but it is important that real memory leaks are detected without needing to manually check the output of valgrind, so I need some solution. – Johan Oct 06 '22 at 18:45
  • You could edit your question a bit, to be "How do I suppress this memory leak report with valgrind", and explain what you tried (like the commands you used to try and suppress it). – hyde Oct 07 '22 at 04:16
  • @Johan try a recent Valgrind and GNU libc. – Paul Floyd Oct 07 '22 at 07:35
  • I'm using valgrind version 3.16.1 and glibc version 3.21-13+deb11u4 – Johan Oct 07 '22 at 07:42
  • It looks like `getgrnam` tries to find the group using alternative methods when the linux group is not found (ldap, nsswitch,...). To do this it opens a bunch of shared objects with `dlopen`, but never closes them with `dlclose` – Johan Oct 07 '22 at 08:08
  • @Johan I didn't ask which version you are using. I can read your log. I asked you to try a recent version. I doubt that it will make any difference, but I don't want to spend time on a 2 year old version of Valgrind. – Paul Floyd Oct 07 '22 at 13:41
  • @Johan and assuming that the error still occurs, this looks like a GNU libc bug. You can try reporting a but to them (don't know how). In the meantime a suppression is your only workaround. – Paul Floyd Oct 07 '22 at 13:46
  • Do you have any recommendations for a viable way to suppress the warnings if I move this code to a library? Any component that links with the library and uses these functions indirectly will have these `dlopen` memory leaks. It doesn't make sense to add suppression files to each component that will ever use the library. – Johan Oct 07 '22 at 14:40

0 Answers0