Reading uninitialized variable that happens to have a good value

103 Views Asked by At

I came across this bug in some code running on a Blackfin 533 processor.

The first time Func() runs, fooStruct will contain garbage, but in the next iteration, the old value that was returned by getFoo() will by chance still be in fooStruct.foo.

FooStruct
{
    double foo;
    double bar;
};

void Func()
{
    FooStruct fooStruct;

    double bar = 123.4 / fooStruct.foo;

    fooStruct.foo = getFoo();
    fooStruct.bar = bar;
}

That means that the first time this runs, we are reading from an uninitialized variable, which is undefined behavior. What about the following iterations? Is that still undefined behavior? What sort of behavior can we expect to see when reading uninitialized variables on embedded processors?

2

There are 2 best solutions below

0
On

One undefined behaviour has been encountered, the behaviour of that and all subsequent statements is undefined too.

Paradoxically, the behaviour of any statements prior to the undefined one are undefined too.

As for the sort of behaviour, asking to categorise undefined behaviour is not logical.

0
On

Yes it is undefined, but the behaviour you observe is not necessarily surprising; it is just that the stack is reused and the reused space is not initialised, and you happened to have reused exactly the same stack location as the previous call. All memory has to contain something and if you call this function and it happens to re-use the same stack frame as a previous call, it will contain whatever was last left there.

For example if you call:

Func() ;
Func() :

It is not defined, but not unreasonable for the second call fooStruct.foo to contain the value left by the first call, because that is what would happen when the compiler takes no action to initialise the variable.

However if instead you had:

void Func2()
{
    int x = 0 ;
    int y = 0 ;
    Func() ;
}

Then called:

Func() ;
Func2() ;

The second call to Func() via Func2() would almost certainly place the local fooStruct.foo at a different address within the stack because of the stack frame for Func2, so would not then have the same value other then by coincidence. Moreover if the sequence were:

Func() ;
Func2() ;
Func() ;

The third call to Func() might use the same stack location as the first, but that space will probably have been modified by Func2() (because of the initialised variables), so likely you will no longer observe the same value in fooStruct.foo.

That is what uninitialised means; you get whatever happens to be there. And because when a variable goes out of scope, it is not generally modified, such values can "reappear" (and not necessarily in the same variable) - just because that is the simplest and most efficient implementation (i.e. to do nothing).