Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

“Uninitialized use” warning in the g++ compiler

I’m using g++ with warning level -Wall -Wextra and treating warnings as errors (-Werror).

Now I’m sometimes getting an error “variable may be used uninitialized in this function”.

By “sometimes” I mean that I have two independent compilation units that both include the same header file. One compilation unit compiles without error, the other gives the above error.

The relevant piece of code in the header files is as follows. Since the function is pretty long, I’ve only reproduced the relevant bit below.

The exact error is:

'cmpres' may be used uninitialized in this function

And I’ve marked the line with the error by * below.

for (; ;) {
    int cmpres; // *
    while (b <= c and (cmpres = cmp(b, pivot)) <= 0) {
        if (cmpres == 0)
            ::std::iter_swap(a++, b);
        ++b;
    }
    while (c >= b and (cmpres = cmp(c, pivot)) >= 0) {
        if (cmpres == 0)
            ::std::iter_swap(d--, c);
        --c;
    }
    if (b > c) break;
    ::std::iter_swap(b++, c--);
}

(cmp is a functor that takes two pointers x and y and returns –1, 0 or +1 if *x < *y, *x == *y or *x > *y respectively. The other variables are pointers into the same array.)

This piece of code is part of a larger function but the variable cmpres is used nowhere else. Hence I fail to understand why this warning is generated. Furthermore, the compiler obviously understands that cmpres will never be read uninitialized (or at least, it doesn’t always warn, see above).

Now I have two questions:

  1. Why the inconsistent behaviour? Is this warning generated by a heuristic? (This is plausible since emitting this warning requires a control flow analysis which is NP hard in the general case and cannot always be performed.)

  2. Why the warning? Is my code unsafe? I have come to appreciate this particular warning because it has saved me from very hard to detect bugs in other cases – so this is a valid warning, at least sometimes. Is it valid here?

like image 515
Konrad Rudolph Avatar asked Feb 06 '11 12:02

Konrad Rudolph


People also ask

What happens when you try to use an uninitialized variable?

An uninitialized variable is a variable that has not been given a value by the program (generally through initialization or assignment). Using the value stored in an uninitialized variable will result in undefined behavior.

What does it mean when a variable is uninitialized?

In computing, an uninitialized variable is a variable that is declared but is not set to a definite known value before it is used. It will have some value, but not a predictable one. As such, it is a programming error and a common source of bugs in software.

What does uninitialized mean in C++?

In C and C++, local variables aren't initialized by default. Uninitialized variables can contain any value, and their use leads to undefined behavior. Warning C4700 almost always indicates a bug that can cause unpredictable results or crashes in your program.

What is the value of uninitialized?

An uninitialized variable has an undefined value, often corresponding to the data that was already in the particular memory location that the variable is using. This can lead to errors that are very hard to detect since the variable's value is effectively random, different values cause different errors or none at all.


3 Answers

An algorithm that diagnoses uninitialized variables with no false negatives or positives must (as a subroutine) include an algorithm that solves the Halting Problem. Which means there is no such algorithm. It is impossible for a computer to get this right 100% of the time.

I don't know how GCC's uninitialized variable analysis works exactly, but I do know it's very sensitive to what early optimization passes have done to the code. So I'm not at all surprised you get false positives only sometimes. It does distinguish cases where it's certain from cases where it can't be certain --

int foo() { int a; return a; }

produces "warning: ‘a’ is used uninitialized in this function" (emphasis mine).

EDIT: I found a case where recent versions of GCC (4.3 and later) fail to diagnose an uninitialized variable:

int foo(int x)
{
    int a;
    return x ? a : 0;
}

Early optimizations notice that if x is nonzero, the function's behavior is undefined, so they assume x must be zero and replace the entire body of the function with "return 0;" This happens well before the pass that generates the used-uninitialized warnings, so there's no diagnostic. See GCC bug 18501 for gory details.

I bring this up partially to demonstrate that production-grade compilers can get uninitialized-variable diagnostics wrong both ways, and partially because it's a nice example of the point that undefined behavior can propagate backward in execution time. There's nothing undefined about testing x, but because code control-dependent on x has undefined behavior, a compiler is allowed to assume that the control dependency is never satisfied and discard the test.

like image 105
zwol Avatar answered Nov 23 '22 22:11

zwol


There was an interesting discussion on clang dev-mailing list related to those heuristics this week.

The bottom line is: it's actually quite difficult to diagnose unitialized values without getting exponential behavior...

Apparently (from the discussion), gcc uses a predicate base approach, but given your experience it seems that it is not always sufficient.

I suspect it's got something to do with the fact that the assignment is mixed within the condition (and after a short-circuiting operator at that...). Have you tried without ?

I think both the gcc and clang folks would be very interested by this example since it's relatively common practice in C or C++ and thus could benefit from some tuning.

like image 39
Matthieu M. Avatar answered Nov 23 '22 22:11

Matthieu M.


The code is correct, but the compiler is failing to identify that the variable is never used without initialization.

like image 44
David Rodríguez - dribeas Avatar answered Nov 23 '22 21:11

David Rodríguez - dribeas