Valgrind: can possibly lost be treated as definitely lost?

webminal.org picture webminal.org · Aug 21, 2010 · Viewed 18.7k times · Source

Can I treat the output of a Valgrind memcheck, "possibly lost" as "definitely lost"?

Possibly lost, or “dubious”: A pointer to the interior of the block is found. The pointer might originally have pointed to the start and have been moved along, or it might be entirely unrelated. Memcheck deems such a block as “dubious”, because it's unclear whether or not a pointer to it still exists.

Definitely lost, or “leaked”: The worst outcome is that no pointer to the block can be found. The block is classified as “leaked”, 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

Answer

lesmana picture lesmana · Aug 21, 2010

Yes, I recommend to treat possibly lost as severe as definitely lost. In other words, fix your code until there are no losts at all.

Possibly lost can happen when you traverse an array using the same pointer that is holding it. You know that you can reset the pointer by subtracting the index. But valgrind can't tell whether it is a programming error or you are being clever doing this deliberately. That is why it warns you.

Example

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

int main(int argc, char** argv) {
  char* s = "string";
  // this will allocate a new array
  char* p = strdup(s);
  // move the pointer into the array
  // we know we can reset the pointer by subtracting
  // but for valgrind the array is now lost
  p += 1;
  // crash the program
  abort();
  // reset the pointer to the beginning of the array
  p -= 1;
  // properly free the memory for the array
  free(p);
  return 0;
}

Compile

$ gcc -ggdb foo.c -o foo

Valgrind report

$ valgrind ./foo
...
==31539== Process terminating with default action of signal 6 (SIGABRT): dumping core
==31539==    at 0x48BBD7F: raise (in /usr/lib/libc-2.28.so)
==31539==    by 0x48A6671: abort (in /usr/lib/libc-2.28.so)
==31539==    by 0x10917C: main (foo.c:14)
==31539== 
==31539== HEAP SUMMARY:
==31539==     in use at exit: 7 bytes in 1 blocks
==31539==   total heap usage: 1 allocs, 0 frees, 7 bytes allocated
==31539== 
==31539== LEAK SUMMARY:
==31539==    definitely lost: 0 bytes in 0 blocks
==31539==    indirectly lost: 0 bytes in 0 blocks
==31539==      possibly lost: 7 bytes in 1 blocks
==31539==    still reachable: 0 bytes in 0 blocks
==31539==         suppressed: 0 bytes in 0 blocks

...

If you remove abort() then Valgrind will report no memory lost at all. Without abort, the pointer will return to the beginning of the array and the memory will be freed properly.

This is a trivial example. In sufficiently complicated code it is no longer obvious that the pointer can and will return to the beginning of the memory block. Changes in other part of the code can cause the possibly lost to be a definitely lost. That is why you should care about possibly lost.