Simple examples to the different leak types reported by Valgrind

Viewed 116

While debugging a leaky application, I noticed many, many questions regarding valgrind and the different types of leaks. These questions usually have very specific examples that don't help understanding how the different types of leaks come to be. A reference is this read the manual answer.

What are the mcve for valgrind?

1 Answers

I set out to write a set of very simple functions that show the different types of leaks reported by valgrind and what they mean.

My main source was this Red Hat blog post by Mark Wielaard, I just decided to use simpler examples.

test.c is:

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

void leaky_definitely(void)
{
    char *p;

    /* 
    This is definitely lost because the pointer is a local variable and once we
    exit the function we lose the refence to the allocated block, so there's no
    way to free this block.
    */
    p = malloc(sizeof(char));
}

char *g_p;

void leaky_still_reachable(void)
{
    /*
    This is still reachable because the pointer is a global variable that's
    preserved until the end of our program, so we could have called free on it.
    */
    g_p = malloc(sizeof(char));
}

char *leaky_array;

void leaky_possibly(void)
{
    leaky_array = malloc(3*sizeof(char));

    /*
    This is possibly lost because the allocated base pointer was lost as we
    incremented it twice, but because leaky_array still points somwewhere in the
    allocated block, we could have done something to recover the base pointer
    and then free it.

    To transform this possibly lost leak into a definitely lost leak, just
    change '2' to '3' in the loop.
    */
    for(int i = 0; i < 2; i++) {
        *leaky_array = i;
        leaky_array++;
    }
}

char **double_pointer;

void leaky_indirectly(void)
{
    double_pointer = malloc(sizeof(char*));
    *double_pointer = malloc(sizeof(char));
    /*
    This will cause a definitely lost leak because double_pointer is lost. It
    will also cause an indirectly lost leak because double_pointer contined a
    pointer to another memory block.

    Indirectly lost leaks are definitely lost leaks indirectly caused by another
    definitely lost leak.
     */

    /*
    By changing the line bellow to `free(double_pointer)` we would create a
    definitely lost leak on `*double_pointer` instead of a indirectly lost.
     */
    // free(double_pointer);
    double_pointer = NULL;
}

int main(void)
{
    leaky_definitely();
    leaky_still_reachable();
    leaky_possibly();
    leaky_indirectly();

    return 0;
}

The code was built with:

gcc -Wall -O0 -g -o test test.c

Valgrind was run as

$ valgrind --show-reachable=yes --leak-check=full ./test
==26907== Memcheck, a memory error detector
==26907== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==26907== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==26907== Command: ./test
==26907== 
==26907== 
==26907== HEAP SUMMARY:
==26907==     in use at exit: 14 bytes in 5 blocks
==26907==   total heap usage: 5 allocs, 0 frees, 14 bytes allocated
==26907== 
==26907== 1 bytes in 1 blocks are still reachable in loss record 1 of 5
==26907==    at 0x4C336AD: malloc (vg_replace_malloc.c:381)
==26907==    by 0x400518: leaky_still_reachable (test.c:24)
==26907==    by 0x4005BF: main (test.c:74)
==26907== 
==26907== 1 bytes in 1 blocks are indirectly lost in loss record 2 of 5
==26907==    at 0x4C336AD: malloc (vg_replace_malloc.c:381)
==26907==    by 0x40059B: leaky_indirectly (test.c:53)
==26907==    by 0x4005C9: main (test.c:76)
==26907== 
==26907== 1 bytes in 1 blocks are definitely lost in loss record 3 of 5
==26907==    at 0x4C336AD: malloc (vg_replace_malloc.c:381)
==26907==    by 0x400503: leaky_definitely (test.c:13)
==26907==    by 0x4005BA: main (test.c:73)
==26907== 
==26907== 3 bytes in 1 blocks are possibly lost in loss record 4 of 5
==26907==    at 0x4C336AD: malloc (vg_replace_malloc.c:381)
==26907==    by 0x400534: leaky_possibly (test.c:31)
==26907==    by 0x4005C4: main (test.c:75)
==26907== 
==26907== 9 (8 direct, 1 indirect) bytes in 1 blocks are definitely lost in loss record 5 of 5
==26907==    at 0x4C336AD: malloc (vg_replace_malloc.c:381)
==26907==    by 0x400583: leaky_indirectly (test.c:52)
==26907==    by 0x4005C9: main (test.c:76)
==26907== 
==26907== LEAK SUMMARY:
==26907==    definitely lost: 9 bytes in 2 blocks
==26907==    indirectly lost: 1 bytes in 1 blocks
==26907==      possibly lost: 3 bytes in 1 blocks
==26907==    still reachable: 1 bytes in 1 blocks
==26907==         suppressed: 0 bytes in 0 blocks
==26907== 
==26907== For lists of detected and suppressed errors, rerun with: -s
==26907== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)
Related