1

The following program:

#include <stdlib.h>

int main(void)
{
    char *my_str = malloc(42 * sizeof(char));

    return 0;
}

Compiled as such:

gcc -g -o prog prog.c

And executed with Valgrind as such:

valgrind --leak-check=full ./prog

Produces the following (truncated) output:

...
==18424== HEAP SUMMARY:
==18424==     in use at exit: 42 bytes in 1 blocks
==18424==   total heap usage: 1 allocs, 0 frees, 42 bytes allocated
==18424== 
==18424== 42 bytes in 1 blocks are definitely lost in loss record 1 of 1
==18424==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==18424==    by 0x10865B: main (main.c:5)
==18424== 
==18424== LEAK SUMMARY:
==18424==    definitely lost: 42 bytes in 1 blocks
==18424==    indirectly lost: 0 bytes in 0 blocks
==18424==      possibly lost: 0 bytes in 0 blocks
==18424==    still reachable: 0 bytes in 0 blocks
==18424==         suppressed: 0 bytes in 0 blocks
...

Why does Valgrind say that the unfreed memory is "definitely lost"?

From the Valgrind Memcheck docs:

This means that no pointer to the block can be found. The block is classified as "lost", because the programmer could not possibly have freed it at program exit, since no pointer to it exists. This is likely a symptom of having lost the pointer at some earlier point in the program.

However, it's pretty clear that I could have easily freed the block before program exit. What am I missing here? Shouldn't the memory be "still reachable"?

Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
Saucy Goat
  • 1,587
  • 1
  • 11
  • 32
  • 5
    After `main` returns, there are no pointers to the allocated block. – Some programmer dude Aug 02 '20 at 16:03
  • 2
    For comparison, allocate memory and save the result to a global or static pointer (rather than one on the stack). – Jonathon Reinhart Aug 02 '20 at 16:05
  • @Someprogrammerdude but does the program not end at the `return` of `main`? – Saucy Goat Aug 02 '20 at 16:08
  • @JonathonReinhart interesting - those are indeed labeled as "still reachable", but how could that be the case if the programmer has no control over what happens after `main` exits? – Saucy Goat Aug 02 '20 at 16:10
  • @4386427 "... because the programmer could not possibly have freed it at program exit, since no pointer to it exists." But I could have freed it at program exit. I would have considered it to be "still reachable" because "the programmer could, at least in principle, have freed it before program exit". – Saucy Goat Aug 02 '20 at 16:25
  • 2
    The return from `main` is not the end of the program, nor is it the last chance for your code to `free` the memory. But after `main` returns, there is no pointer to the memory that would allow the code to `free` it. – user3386109 Aug 02 '20 at 16:28
  • @user3386109 How could I have `free`'d the memory after `main`? Thanks in advance! – Saucy Goat Aug 02 '20 at 16:29
  • See the documentation for `atexit`. – user3386109 Aug 02 '20 at 16:31
  • @4386427 I do realize that the pointer is lost upon exiting `main`, but if that is what we consider "definitely lost" memory to be, how can there be "still reachable" memory after program exit? – Saucy Goat Aug 02 '20 at 16:37
  • @4386427 I know it is not reachable. I think I misinterpreted the documentation as it pertains to "at program exit". Would you then please provide an example of "still reachable" memory? – Saucy Goat Aug 02 '20 at 16:42
  • @4386427 all has been explained, thank you to all of you who took time to reply! I am curious about the downvotes, could I get some *pointers* as to how could I improve my question? – Saucy Goat Aug 02 '20 at 17:33
  • 1
    Good programmers tidy up after themselves and free every ressource at program exit. But sometimes, good programmers have to use third party libraries from lazy programmers, that didn't care to implement a `cleanup()` method. So those libraries keep objects hanging around that are referenced via `static` or global variables. So, when the good guys analyse their code with `valgrind`, they can see via the "still reachable" metric, which memory leaks are attributable to the lazy guys, and which memory leaks are definitely leaks. – Kai Petzke Apr 26 '21 at 12:34

3 Answers3

8

Here's an example of "definitely lost" vs. "still reachable":

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

void *p;

int main()
{
    p = malloc(10);
    p = malloc(100);
    void *m = malloc(50);
    return 0;
}

What happens in this code is the following:

  • 10 bytes are allocated and the pointer to this memory is assigned to the global p.
  • 100 bytes are allocated and the pointer to this memory is assigned to the global p. This overwrites the pointer value that pointed to 10 allocated bytes, and there is no other reference to it. So that memory is "definitely lost".
  • 50 bytes are allocated and the pointer to this memory is assigned to the local m.
  • main returns causing the lifetime of m to end. As a result there is no reference stored to the memory pointer to 50 bytes so that memory is "definitely lost".
  • The pointer value pointing to 100 bytes still lives in the global p, so cleanup routines such as those called by atexit or other compiler specific methods could still clean up that memory. So it is "still reachable".

When running valgrind on this code, it outputs the following:

==60822== Memcheck, a memory error detector
==60822== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==60822== Using Valgrind-3.14.0 and LibVEX; rerun with -h for copyright info
==60822== Command: ./x1
==60822== 
==60822== 
==60822== HEAP SUMMARY:
==60822==     in use at exit: 160 bytes in 3 blocks
==60822==   total heap usage: 3 allocs, 0 frees, 160 bytes allocated
==60822== 
==60822== LEAK SUMMARY:
==60822==    definitely lost: 60 bytes in 2 blocks
==60822==    indirectly lost: 0 bytes in 0 blocks
==60822==      possibly lost: 0 bytes in 0 blocks
==60822==    still reachable: 100 bytes in 1 blocks
==60822==         suppressed: 0 bytes in 0 blocks
==60822== Rerun with --leak-check=full to see details of leaked memory
==60822== 
==60822== For counts of detected and suppressed errors, rerun with: -v
==60822== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Which is consistent with the description above.

So to summarize, memory is "still reachable" if a pointer to it is stored either in a file scope variable or is pointed to by any "still reachable" memory. Otherwise it is lost.

dbush
  • 205,898
  • 23
  • 218
  • 273
3

Why does Valgrind say that the unfreed memory is "definitely lost"?

Because - once main returns the variable my_str doesn't exist anymore. Consequently, there is no way to free the allocated memory. And there is no way to reach the memory. No one know where the memory is. It's lost.

Note: All modern OS systems will however take care of the memory clean up so it's not a real problem.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63
  • Thank you for answering - your comments made me realize that I had misunderstood what the documentation meant by "at program exit". – Saucy Goat Aug 02 '20 at 17:32
0

Valgrind is checking for memory leaks, and you are leaking memory as you didn't free the pointer before exit. You should free(my_str) .

int main(void)
{
    char *my_str = malloc(42 * sizeof(char));
    free (my_str);
    return 0;
}

Definitely Lost mean that nobody has a reference to that memory location anymore, so nobody could free that memory. In a context of a running program that will be a segment of memory that nobody could reuse (so it is lost). Of course at the end of the program nobody will ever use it again, but some of the memory could be referenced by another leaking object (and will be an indirect lost), so with those hints you can debug your program. The link bellow shows a discussion on the topic.

This is a complete response on why you should free memory before exit

Pablo Yaggi
  • 1,061
  • 5
  • 14