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
possibly lost: heap-allocated memory that was never freed to which valgrind cannot be sure whether there is a pointer or not. still reachable: heap-allocated memory that was never freed to which the program still has a pointer at exit.
"indirectly lost" means your program is leaking memory in a pointer-based structure. (E.g. if the root node of a binary tree is "definitely lost", all the children will be "indirectly lost".) If you fix the "definitely lost" leaks, the "indirectly lost" leaks should go away.
Yes, there are false positives with Valgrind, that's why it has suppression files for particular glibc and gcc versions, for example. The false positives may arise if you are using older valgrind with newer gcc and glibc, i.e., valgrind 3.3 with glibc 2.9.
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.
#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 free
d 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With