Bug 419230

Summary: Memcheck does not report conditional jump on uninitialized value
Product: [Developer tools] valgrind Reporter: Thomas Fannes <thomasfannes>
Component: memcheckAssignee: Julian Seward <jseward>
Status: RESOLVED NOT A BUG    
Severity: normal CC: tom
Priority: NOR    
Version: 3.15 SVN   
Target Milestone: ---   
Platform: Manjaro   
OS: Linux   
Latest Commit: Version Fixed In:
Attachments: Minimal example

Description Thomas Fannes 2020-03-25 14:00:31 UTC
Created attachment 127003 [details]
Minimal example

SUMMARY

Memcheck does not always report a conditional jump based on an uninitialized value on the stack, but is seems to depend on previous stack allocations.

In attachment a minimal example to reproduce the error. Compiling with macros
INIT_BUFFER=1, TEST_A1=0 and TEST_A2=1, and executing with valgrind will not report the conditional jump based on uninitialized value.

STEPS TO REPRODUCE
1. Allocate a buffer of 1kB on the stack, clear it and let it go out of scope
2. Allocate a struct on the stack (without initialization), but do not use it
3. Allocate a second struct on the stack (without initialization), and jump based on a member.

OBSERVED RESULT
No error is reported

EXPECTED RESULT
Valgrind should report an conditional jump based on unitialized value

SOFTWARE/OS VERSIONS
Linux/KDE Plasma: Manjaro
(available in About System)
KDE Plasma Version: 5.18.3
KDE Frameworks Version:  5.68.0
Qt Version: 4.14.1

ADDITIONAL INFORMATION

compiled with gcc version 9.3.0 (Arch Linux 9.3.0-1)
Comment 1 Tom Hughes 2020-03-25 14:12:56 UTC
That's clearly not a minimal test case because it has a whole pile of ifdefs only one combination of which is required and it is unnecessarily split over multiple files...
Comment 2 Tom Hughes 2020-03-25 14:18:43 UTC
I'm pretty sure that there's no bug here anyway, it's just you're expecting more of valgrind than it is able to provide.

When stack is reused for different variables inside the same function there is no guarantee that the compiler will actually move the stack pointer up at the end of one scope and then back down at the start of the next one - more likely it will just move it down once at the start of the function to reserve the maximum amount of memory required and then move it back at the end.

Within the function it will just use whatever regions it wants within that chunk of stack it has reserved and reuse things as and when it wants.

In fact you don't even need scopes - if you just stop using one variable and start using another then the compiler might decide to overlay them in the same way.

What all this means is that there is no way for valgrind to know when the compiler has switched from using that piece of stack for one variable to using it for another variable and hence no way for it to mark it as uninitialised again. Only at the end of the function when the stack is released by moving the stack pointer will valgrind mark it as uninitialised again.

In general you will find that something like address sanitiser can do a better job finding stack related issues because it is able to instrument at compile time - valgrind is better than nothing for sure but it can never do everything that compile time instrumentation can do.