Reputation: 65016
Valgrind memcheck uses a bunch of heuristics to avoid false positives on "harmless" uses uninitialized values, since such uses are common both in correct and incorrect-but-otherwise functioning code.
In particular, it doesn't barf until you actually use such a value in a serious, perhaps "irreversible" way, e.g, jumping based on its value.
This means that sometimes the error occurs very far from the origin of the problem and it is not even possible to determine which value is involved. Is there some way to "check" a value at runtime, like use(x)
which will make Valgrind emit an error at that spot if x
is uninitialized?
Upvotes: 4
Views: 272
Reputation: 36412
You can make your use(x)
macro use the Valgrind VALGRIND_CHECK_VALUE_IS_DEFINED
Client Request to get an error on the spot.
For this, include valgrind/memcheck.h
and define your macro as
#define use(x) VALGRIND_CHECK_VALUE_IS_DEFINED(x)
And be sure to always pass an lvalue.
You can also run memcheck with the --track-origins=yes
for heavier tracking that should show where the uninitialized data originated.
See also the Valgrind FAQ on uninitialised value errors, which explains both, as well as why Valgrind doesn't complain on copying uninitialised values.
Upvotes: 1
Reputation: 5946
Usually, stuff like this requires instrumentation of the code (either done automatically by a tool, or inserted manually in the source).
As noted in my comment, if you can work with having to insert the use(x)
statements yourself, you could do something like this:
static FILE* dev_null = 0;
static void use_var(char* var_addr, size_t var_size)
{
if (dev_null == 0) /* make sure we only open FILE* dev_null once */
{
dev_null = fopen("/dev/null", "wb");
assert(dev_null != 0); /* opening /dev/null CAN actually fail */
}
size_t i;
for (i = 0; i < var_size; ++i)
{
fputc(var_addr[i], dev_null); /* read every byte in the variable, write to dev_null */
}
}
#define use(x) use_var((char*)&x, sizeof(x))
/* Example of usage */
int main()
{
long x = 80;
struct { double d; char c[123]; } y;
memset(&y, 0, sizeof(y) - 1); /* initialize all bytes in y, except the last */
double z[2] = {3.14, 42.0};
use(x);
use(y);
use(z);
return 0;
}
However there is a problem with using struct
s that contain alignment-padding.
The padding is never used for anything, so this can be a legal reason for passing around uninitialized data. Valgrind can cause spurious errors regarding uninitialized reads in this case exactly.
These two posts discuss this issue specifically: Is there anyway a valgrind message "Conditional jump or move depends on uninitialized value" can be a so called 'false positive'
Should I worry about "Conditional jump or move depends on uninitialised value(s)"?
Upvotes: 0