5

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?

Community
  • 1
  • 1
Martin B.
  • 53
  • 1
  • 3

2 Answers2

6

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
    
Dietrich Epp
  • 205,541
  • 37
  • 345
  • 415
  • `unsigned x = *p;` is undefined behavior and so `x` is not properly initialized. The compiler can effectively do whatever he wants after that, and it seems he does. This is not valgrind's fault but yours. – Jens Gustedt Sep 17 '11 at 18:59
  • 1
    @Jens Gustedt: It is not "undefined behavior" but an "indeterminate value" which is something entirely different, and may be a trap representation. The compiler cannot "do whatever he wants" in such a case but must rigorously follow both POSIX and C standards. The program, is in fact, correct by C standards on any platform which does not have a trap representation for unsigned integers. This includes all POSIX platforms. – Dietrich Epp Sep 17 '11 at 19:19
  • 1
    @SoapBox: You are incorrect. The errors occur while inside `printf`, but they are caused by `strlen`. You could put `i = 12;` just before the `printf` and the error messages would go away, *even though* i was already 12. This is because Valgrind incorrectly believes that `i` is uninitialized even though it is in fact initialized to the value 12. This is caused by Valgrind's mistaken interpretation of `strlen`. So `printf` has nothing to do with it, you could replace `printf` with `exit(i)` and you would also get an error. – Dietrich Epp Sep 17 '11 at 19:24
  • I refrain from my earlier statements that this is undefined behavior in C independent of the implementation. It appears to be only undefined behavior in C++. – Johannes Schaub - litb Sep 17 '11 at 19:41
  • However, in C89 it seems to have been undefined. It said that undefined behavior is "behavior, upon use of a nonportable or erroneous program construct, of erroneous data, or of indeterminately-valued objects, for which the Standard imposes no requirements". C99 appears to have changed that. – Johannes Schaub - litb Sep 17 '11 at 19:53
  • Hm, J2 (yes I know it is only informative, but still) simply states: *The behavior is undefined: ... The value of the object allocated by the malloc function is used* – Jens Gustedt Sep 17 '11 at 21:44
  • @DietrichEpp I thought that just assigning NULL to a pointer was enough to initialize it. The `conditional jump[...]` error disappeared in my code by implementing the third option you posted (by using memset, or calloc). Thank you! – Hernán Erasmo May 19 '14 at 23:32
0

Don't use valgrind with optimizations.

arsenm
  • 2,903
  • 1
  • 23
  • 23