Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Valgrind errors on simple C string functions

Let's consider this simple test program:

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

int main(int argc, char *argv[])
{
        char buf[256];
        int i;

        strcpy(buf,"Hello world!");
        i = strlen(buf);
        printf("Length of string is %d.\n",i);
        return 0;
}

When compiling it with the Intel c++ compiler and optimizations turned on (O3), I get the following errors from valgrind:

==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4009EF: main (strtest.cpp:11)
==8727== Use of uninitialised value of size 8
==8727==    at 0x4FC61ED: _itoa_word (in /lib64/libc-2.4.so)
==8727==    by 0x4FC9317: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC61F7: _itoa_word (in /lib64/libc-2.4.so)
==8727==    by 0x4FC9317: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC9386: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC990F: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)
==8727== Conditional jump or move depends on uninitialised value(s)
==8727==    at 0x4FC82F2: vfprintf (in /lib64/libc-2.4.so)
==8727==    by 0x4FD02A9: printf (in /lib64/libc-2.4.so)
==8727==    by 0x400A09: main (strtest.cpp:13)

I am using the most recent version of valgrind (3.6.1). This does not happen when turning optimizations off (-O0), and it does not happen with g++. However, it appears with all Intel compilers I have tried out so far (11.0, 11.1, 12).

It seems that the errors are related to SIMD-acceleration of the string functions, like discussed in C strings, strlen and Valgrind.

There it was stated that this was a bug in valgrind and is fixed now. However, although using the newest valgrind version, I still have these errors. Does anybody know some help about this?

like image 853
Martin B. Avatar asked Sep 17 '11 18:09

Martin B.


1 Answers

Valgrind is trying to determine whether a value depends on initialized memory or not, which is not a tractable problem in general. Valgrind does a "best effort" by tracking which bits are set, and allowing them to cascade. There are many ways to fool it. For example, I just cooked up this code:

#include <stdlib.h>

int main(int argc, char *argv[])
{
    unsigned *p = malloc(sizeof(unsigned));
    unsigned x = *p;
    free(p);
    unsigned f = x == 0;
    unsigned g = x == 1;
    return f & g;
}

Side Note: The above program, strictly speaking, is correct on any platform that does not have trap representations for an unsigned int, which is true on our platform (Valgrind is x86 only). Undefined behavior is not invoked on these platforms, and main is guaranteed by the C standard to return 0 on such platforms.

Citation: n1256: 7.20.3.3: malloc returns objects whose values are "indeterminate". n1256 3.17.2: An indeterminate value is either a "trap representation" or an "unspecified value". Note that on x86 there are no trap representations for unsigned integers.

According to Valgrind, neither f nor g are properly initialized, so f & g cannot be initialized either. However, the result is always zero, as anybody with an ounce of logic will tell you. Valgrind does not understand logic, it just follows bits around according to some simple rules.

What is probably happening here is that Intel's C compiler gave you an opitimized version of strlen which uses SSE or some kind of trick, which caused the "uninitialized" bits in Valgrind to get set in the result. This would naturally happen, of course, if the optimized code read past the initialized part of buf and then performed an series of operations like those above. And of course, it would do something like that, because it's faster to do it that way (and it's always safe to read past the end of arrays on x86, as long as you don't cross a page boundary). You have some options.

  • Turn off optimizations when using Valgrind and Intel's compiler.

  • Add code to Valgrind to catch this particular kind of error. (Valgrind has special cases in it already.)

  • Modify your code conditionally to get Valgrind to give the right result. For example, put this at the top:

    // This only fixes the error if sizeof(buf) is at least as large
    // as the largest multiple of 16 larger than strlen(buf)
    #if VALGRIND
    memset(buf, '\0', sizeof(buf));
    #endif
    
like image 50
Dietrich Epp Avatar answered Sep 26 '22 15:09

Dietrich Epp