Search code examples
cmemory-leaksenvironment-variablesvalgrind

In C, why is the pointer returned by getenv automatically reclaimed?


I use the following code to test the pointer returned by getenv, if not free, testing will cause a memory leak.

#include <stdio.h>
#include <stdlib.h>

void demo() {
    const char *home = getenv("HOME");
    printf("%s\n", home);
}

int main() {
    demo();
    return 0;
}

I use Valgrind to detect memory leaks:

$ gcc main.c -o main
$ valgrind --tool=memcheck --leak-check=full --show-leak-kinds=all ./main

The result is as follows:

==134679== Memcheck, a memory error detector
==134679== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==134679== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==134679== Command: ./demo
==134679== 
/home/aszswaz
==134679== 
==134679== HEAP SUMMARY:
==134679==     in use at exit: 0 bytes in 0 blocks
==134679==   total heap usage: 1 allocs, 1 frees, 1,024 bytes allocated
==134679== 
==134679== All heap blocks were freed -- no leaks are possible
==134679== 
==134679== For lists of detected and suppressed errors, rerun with: -s
==134679== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

It shows that a piece of memory has been applied for and a piece of memory has been reclaimed. Why can the pointer obtained with getenv be automatically recycled?

If I add free((void *)home) to the code, will it affect the global environment variable?


Solution

  • https://man7.org/linux/man-pages/man3/getenv.3.html does not mention anything that getenv()'s return value is allocated on the heap. Hence, think of it like argv.

    Although rarely and myself never seen this happen, in some implementations, getenv() may allocate memory on the heap and return a pointer to it. Therefore, its best to review your operating system's manual pages.

    Only memory allocated on the heap must be free()d.

    And, pointer to memory allocated on the heap is usually only returned when you call malloc(), calloc(), realloc(), strdup(), etc.

    Anyways:

    If you do call free() on the pointer returned by getenv(), it is undefined behavior if your operating system did not return a pointer to memory on heap.

    And whoa, I almost didn't see this!:
    I think this is what is confusing you:

    usage: 1 allocs, 1 frees
    

    printf() may allocate on heap and free it before returning back.

    Edit:

    Using gdb, we can find that:

    #0  __GI___libc_malloc (1024) // and indeed it calls with 1024 bytes
    #1  __GI__IO_file_doallocate ()
    #2  __GI__IO_doallocbuf ()
    #4  _IO_new_file_xsputn ()
    #5  _IO_new_file_xsputn ()
    #6  __GI__IO_puts
    #7  demo ()
    #8  main ()
    

    printf() seems to be replaced by puts(), and puts() calls malloc() through a long function call chain.

    But, it doesn't seem to call free(). I think it calls some other function that frees the memory. I'm still doing my research.