2

I have been wrangling with a what seems to be a strange case of realloc not working well with valgrind. It seems as if I am either allocating too much somehow or am using realloc incorrectly. I take valgrind errors seriously and such an error worries me deeply.

Minimal working example:

#include <stdlib.h>

typedef struct test {
    size_t n;
    size_t r;
    int **ptrs;
} test;

test *new_test() {
    test *t = malloc(sizeof(test));
    t->n = 0; //number of elements
    t->r = 1; //reserve
    t->ptrs = calloc(t->r, sizeof(*(t->ptrs))); //calloc inits so we don't have to
    return t;
}

void push_back_test(test *t, int *ptr) {
    if (t->n == t->r) {
        t->r <<= 1;
        int **temp_ptr = realloc(t->ptrs, sizeof(t->ptrs) * t->r);
        if (temp_ptr) {
            t->ptrs = temp_ptr;
        } else {
            exit(EXIT_FAILURE);
        }
        //NULL out the rest 
        for (int **ptri = t->ptrs + t->n; ptri < t->ptrs + t->r; ++ptri) {
            (*ptri) = NULL;
        }
    }
    t->ptrs[t->n] = ptr;
    ++(t->n);
}

int main(int argc, char **argv) {

    test *t = new_test();
    int *a = calloc(2, sizeof(int)); //calloc inits
    int *b = calloc(4, sizeof(int));
    int *c = calloc(8, sizeof(int));

    push_back_test(t, a);
    push_back_test(t, b);
    push_back_test(t, c);
    push_back_test(t, a);
    push_back_test(t, b);

    exit(EXIT_SUCCESS);
}

Valgrind output:

==26528== Memcheck, a memory error detector
==26528== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26528== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==26528== Command: ./test
==26528== 
==26528== Conditional jump or move depends on uninitialised value(s)
==26528==    at 0x435A32: __linkin_atfork (in /----/----/test)
==26528==    by 0x414905: ptmalloc_init.part.8 (in /----/----/test)
==26528==    by 0x414C7F: malloc_hook_ini (in /----/----/test)
==26528==    by 0x465B1A: _dl_get_origin (in /----/----/test)
==26528==    by 0x436AB4: _dl_non_dynamic_init (in /----/----/test)
==26528==    by 0x437916: __libc_init_first (in /----/----/test)
==26528==    by 0x40140F: (below main) (in /----/----/test)
==26528== 
==26528== Conditional jump or move depends on uninitialised value(s)
==26528==    at 0x4104BA: _int_free (in /----/----/test)
==26528==    by 0x412C3B: _int_realloc (in /----/----/test)
==26528==    by 0x414046: realloc (in /----/----/test)
==26528==    by 0x40109D: push_back_test (test.c:20)
==26528==    by 0x4011FB: main (test.c:44)
==26528== 
==26528== Conditional jump or move depends on uninitialised value(s)
==26528==    at 0x410518: _int_free (in /----/----/test)
==26528==    by 0x412C3B: _int_realloc (in /----/----/test)
==26528==    by 0x414046: realloc (in /----/----/test)
==26528==    by 0x40109D: push_back_test (test.c:20)
==26528==    by 0x4011FB: main (test.c:44)
==26528== 
==26528== 
==26528== HEAP SUMMARY:
==26528==     in use at exit: 0 bytes in 0 blocks
==26528==   total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==26528== 
==26528== All heap blocks were freed -- no leaks are possible
==26528== 
==26528== For counts of detected and suppressed errors, rerun with: -v
==26528== Use --track-origins=yes to see where uninitialised values come from
==26528== ERROR SUMMARY: 3 errors from 3 contexts (suppressed: 0 from 0)

Now I know the first error is probably a problem with my libc, but the rest is essentially telling me that I am not nulling out all the memory I have allocated and am passing around garbage, which I believe cannot be as I am nulling out what has been newly allocated.

I have tried many variations on this, and they either (rightly) crash or give these messages. I am at a loss.

EDIT: original code that I have a problem in was more correct than this. and I have now fixed the MWE, still the same.

chqrlie
  • 131,814
  • 10
  • 121
  • 189
Nvirjskly
  • 33
  • 7

2 Answers2

6

The problem is that glibc is not Valgrind-clean. Normally, these errors would appear to come from your libc.so.6, which Valgrind has built-in suppressions for. When you link statically, these issues appear to originate from your binary, and so are not suppressed.

You can solve this by either not linking statically or by using suppressions and suppression files

If you look at what is being complained about, it is that some thread-local storage is being compared to NULL. If you attach a debugger, it turns out things are initialized just fine, and the issue is a false positive.

More information can be found at this SO thread.

Of course, multiple comments point out your original bug with new_test and an existing bug with the size you pass to realloc. Once those are fixed, the Valgrind warnings remain, and this is why.

dho
  • 2,310
  • 18
  • 20
  • 1
    Thanks, yes static is the issue. The bug with `new_test` was not present in the original code, which is why I started this question in the first place, however the size bug, though ultimately not a consequential one as `sizeof(void **)==sizeof(void *)` on my platform (and I am assuming most mainstream ones as well,) is one that could have maybe caused problems, though again, changed nothing for valgrind. – Nvirjskly Mar 13 '16 at 18:11
1

You seem to call realloc with an incorrect size:

realloc(t->ptrs, sizeof(t->ptrs) * t->r);

Should be instead:

realloc(t->ptrs, sizeof(*t->ptrs) * t->r);

Since t->ptrs is defined as int **ptrs;, the computation gives the same result on your and most modern platforms. Therefore this error has no consequences and does not explain the problem, as dho correctly commented.

chqrlie
  • 131,814
  • 10
  • 121
  • 189