After freeing a GArray, Valgrind still complains about non-freed blocks

Valgring is reporting about non-freed heap memory even after a call to g_array_free().

I have ran valgrind using --suppressions=/usr/share/glib-2.0/valgrind/glib.supp, but the errors persist, hence I think that I’m doing something wrong while freeing memory.

Please, how can I fix ?

#include <stdlib.h>
#include <glib.h>

struct dataframe
{
	gfloat fNumber;
	gint   dNumber;
    GArray *vector;
};

struct vector
{
    gfloat x;
    gfloat y;
};

struct dataframe *
dataframe_init() {
	struct dataframe *df = malloc(sizeof *df);
	df->fNumber = 3.1415;
    df->dNumber = 42;
    df->vector = g_array_new(FALSE, FALSE, sizeof(df->vector));

	return df;
}

void
dataframe_free(struct dataframe *df) {
    if(df)
        if(df->vector)
            g_array_free(df->vector, TRUE);
        free(df);
}

void
dataframe_print(struct dataframe *df) {
    struct vector v;

    g_print("The float: %6.4f\n", df->fNumber);
    g_print("The int:   %06d\n", df->dNumber);
    
    g_print("Vector count %d\n", df->vector->len);

    for(size_t i = 0; i < df->vector->len; i++) {
        v = g_array_index(df->vector, struct vector, i);
        g_print("v_%02d: (%6.2f, %6.2f)\n", i, v.x, v.y);
    }
}
	
gint main(gint argc, gchar *argv[]) { 
	
    struct dataframe * df = dataframe_init();

    struct vector v;

    v = (struct vector){ 1.2f, 3.2f };
    g_array_append_val(df->vector, v);

    v = (struct vector){ 0.4f, 4.2f };
    g_array_append_val(df->vector, v);

    v = (struct vector){ 2.4f, 0.2f };
    g_array_append_val(df->vector, v);

    dataframe_print(df);

    dataframe_free(df);

    return 0;
}

I compile the code above with:

gcc -Wall --pedantic -ggdb gfree_test.c  -o gfree_test $(pkg-config --cflags --libs glib-2.0)

Checked with Clang’s static analyzer

    $ scan-build-11 -v gcc --std=c2x -Wall --pedantic  -ggdb gfree_test.c  -o gfree_test $(pkg-config --cflags --libs glib-2.0)
    scan-build: Using '/usr/lib/llvm-11/bin/clang' for static analysis
    scan-build: Emitting reports for this run to '/tmp/scan-build-2020-05-20-034333-30214-1'.
    scan-build: Analysis run complete.
    scan-build: Removing directory '/tmp/scan-build-2020-05-20-034333-30214-1' because it contains no reports.
    scan-build: No bugs found.

And run with

$ valgrind --suppressions=/usr/share/glib-2.0/valgrind/glib.supp   ./gfree_test 
==28121== Memcheck, a memory error detector
==28121== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==28121== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==28121== Command: ./gfree_test
==28121== 
The float: 3.1415
The int:   000042
Vector count 3
v_00: (  1.20,   3.20)
v_01: (  0.40,   4.20)
v_02: (  2.40,   0.20)
==28121== 
==28121== HEAP SUMMARY:
==28121==     in use at exit: 18,670 bytes in 10 blocks
==28121==   total heap usage: 53 allocs, 43 frees, 218,956 bytes allocated
==28121== 
==28121== LEAK SUMMARY:
==28121==    definitely lost: 0 bytes in 0 blocks
==28121==    indirectly lost: 0 bytes in 0 blocks
==28121==      possibly lost: 0 bytes in 0 blocks
==28121==    still reachable: 18,670 bytes in 10 blocks
==28121==         suppressed: 0 bytes in 0 blocks
==28121== Rerun with --leak-check=full to see details of leaked memory
==28121== 
==28121== For lists of detected and suppressed errors, rerun with: -s
==28121== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

For sake of completeness, the leaks shown by valgrind are:

==30135== 4 bytes in 1 blocks are still reachable in loss record 1 of 10
==30135==    at 0x483677F: malloc (vg_replace_malloc.c:309)
==30135==    by 0x4908D03: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x49092FB: g_private_get (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48DB1CC: g_slice_alloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48AAC9D: g_hash_table_new_full (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48CD88A: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x400F369: call_init.part.0 (dl-init.c:72)
==30135==    by 0x400F468: call_init (dl-init.c:30)
==30135==    by 0x400F468: _dl_init (dl-init.c:119)
==30135==    by 0x40010C9: ??? (in /usr/lib/x86_64-linux-gnu/ld-2.30.so)
==30135== 
==30135== 4 bytes in 1 blocks are still reachable in loss record 2 of 10
==30135==    at 0x483677F: malloc (vg_replace_malloc.c:309)
==30135==    by 0x4908D03: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x49092FB: g_private_get (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x4895E18: g_get_charset (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48C6439: g_print (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x109244: dataframe_print (gfree_test.c:43)
==30135==    by 0x1093B4: main (gfree_test.c:69)
==30135== 
==30135== 15 bytes in 1 blocks are still reachable in loss record 3 of 10
==30135==    at 0x483677F: malloc (vg_replace_malloc.c:309)
==30135==    by 0x48C31C8: g_malloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48DCE3F: g_strdup (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x4895E98: g_get_charset (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48C6439: g_print (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x109244: dataframe_print (gfree_test.c:43)
==30135==    by 0x1093B4: main (gfree_test.c:69)
==30135== 
==30135== 15 bytes in 1 blocks are still reachable in loss record 4 of 10
==30135==    at 0x483677F: malloc (vg_replace_malloc.c:309)
==30135==    by 0x48C31C8: g_malloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48DCE3F: g_strdup (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x4895F05: g_get_charset (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48C6439: g_print (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x109244: dataframe_print (gfree_test.c:43)
==30135==    by 0x1093B4: main (gfree_test.c:69)
==30135== 
==30135== 24 bytes in 1 blocks are still reachable in loss record 5 of 10
==30135==    at 0x4838B65: calloc (vg_replace_malloc.c:762)
==30135==    by 0x48C3220: g_malloc0 (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48E65A1: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x4895F30: g_get_charset (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48C6439: g_print (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x109244: dataframe_print (gfree_test.c:43)
==30135==    by 0x1093B4: main (gfree_test.c:69)
==30135== 
==30135== 32 bytes in 1 blocks are still reachable in loss record 6 of 10
==30135==    at 0x4838B65: calloc (vg_replace_malloc.c:762)
==30135==    by 0x48C3220: g_malloc0 (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48A9AAF: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48AACDC: g_hash_table_new_full (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48CD88A: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x400F369: call_init.part.0 (dl-init.c:72)
==30135==    by 0x400F468: call_init (dl-init.c:30)
==30135==    by 0x400F468: _dl_init (dl-init.c:119)
==30135==    by 0x40010C9: ??? (in /usr/lib/x86_64-linux-gnu/ld-2.30.so)
==30135== 
==30135== 64 bytes in 1 blocks are still reachable in loss record 7 of 10
==30135==    at 0x48366AF: malloc (vg_replace_malloc.c:308)
==30135==    by 0x4838DE7: realloc (vg_replace_malloc.c:836)
==30135==    by 0x48C3267: g_realloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48A9A9A: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48AACDC: g_hash_table_new_full (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48CD88A: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x400F369: call_init.part.0 (dl-init.c:72)
==30135==    by 0x400F468: call_init (dl-init.c:30)
==30135==    by 0x400F468: _dl_init (dl-init.c:119)
==30135==    by 0x40010C9: ??? (in /usr/lib/x86_64-linux-gnu/ld-2.30.so)
==30135== 
==30135== 96 bytes in 1 blocks are still reachable in loss record 8 of 10
==30135==    at 0x483677F: malloc (vg_replace_malloc.c:309)
==30135==    by 0x48C31C8: g_malloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48DB1F1: g_slice_alloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48AAC9D: g_hash_table_new_full (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48CD88A: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x400F369: call_init.part.0 (dl-init.c:72)
==30135==    by 0x400F468: call_init (dl-init.c:30)
==30135==    by 0x400F468: _dl_init (dl-init.c:119)
==30135==    by 0x40010C9: ??? (in /usr/lib/x86_64-linux-gnu/ld-2.30.so)
==30135== 
==30135== 2,032 bytes in 1 blocks are still reachable in loss record 9 of 10
==30135==    at 0x4838B65: calloc (vg_replace_malloc.c:762)
==30135==    by 0x48C3220: g_malloc0 (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48E65A1: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48DB442: g_slice_alloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48AAC9D: g_hash_table_new_full (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48CD88A: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x400F369: call_init.part.0 (dl-init.c:72)
==30135==    by 0x400F468: call_init (dl-init.c:30)
==30135==    by 0x400F468: _dl_init (dl-init.c:119)
==30135==    by 0x40010C9: ??? (in /usr/lib/x86_64-linux-gnu/ld-2.30.so)
==30135== 
==30135== 16,384 bytes in 1 blocks are still reachable in loss record 10 of 10
==30135==    at 0x483677F: malloc (vg_replace_malloc.c:309)
==30135==    by 0x48C31C8: g_malloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x48CD89B: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.6400.2)
==30135==    by 0x400F369: call_init.part.0 (dl-init.c:72)
==30135==    by 0x400F468: call_init (dl-init.c:30)
==30135==    by 0x400F468: _dl_init (dl-init.c:119)
==30135==    by 0x40010C9: ??? (in /usr/lib/x86_64-linux-gnu/ld-2.30.so)
==30135==

It only really makes sense to look at the ‘definitely lost’ reports from valgrind. Stuff which is still reachable isn’t really leaked.

I run valgrind with --show-leak-kinds=definite.

3 Likes

Apart from the good suggestion you already received from @pwithnall, I’d like to point out the above code is probably a straight error because you are defining an array of GArray * items but you are storing struct vector items. It just happens to work on 64 bits platform, but what you really want is:

df->vector = g_array_new(FALSE, FALSE, sizeof(struct vector));

There isn’t a way to use the type of object is pointing to instead name directly the struct?

As I often do with mallocs

struct foo_t * myvar = malloc(sizeof *myvar);

I don’t understand what you want. In your example myvar is of type struct foo_t *, so *myvar is struct foo_t. In your original example, df->vector is of type GArray * and *df->vector would be of type GArray: I cannot see how you would go from there to struct vector.

You’re asking for the size of a pointer to a GArray, but the contents of the df->vector are the struct vector.

Yes. you’re both right. The question makes no sense. I was thinking about that. Thank you both for answers.