I encountered a problem that Memcheck reports uninitialized values and I think these are perfectly legal. I managed to create a small example program that exhibits this behavior. I would like to know if Memcheck is really wrong and what can be done about it. (Is there any solution besides adding the errors into a suppression file?)
To reproduce this, I made the program below. It runs function go
that puts 0x42
on the stack, calls og
(this pushes the address of the next instruction leave
on the stack), then in og
it stores esp+4
into global variable a
.
The stack looks like this:
| address of `leave` instruction | pc = a[-1]
| 0x42 | a points here, answer = a[0]
If I build it and run Valgrind,
gcc -g -m32 main.c go.S -o main
valgrind --track-origins=yes ./main
Valgrind thinks that the value in variable pc
(and answer
, if you put it in the if
instead) is undefined. I checked with debugger that the values there actually are what I wanted.
==14160== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==14160== Command: ./main
==14160==
==14160== Conditional jump or move depends on uninitialised value(s)
==14160== at 0x804847D: print (main.c:18)
==14160== by 0x80484B0: ??? (go.S:19)
==14160== by 0x8048440: main (main.c:8)
==14160== Uninitialised value was created by a stack allocation
==14160== at 0x80484AC: ??? (go.S:19)
==14160==
==14160== Use of uninitialised value of size 4
==14160== at 0x80484B1: ??? (go.S:20)
==14160== by 0x8048440: main (main.c:8)
==14160== Uninitialised value was created by a stack allocation
==14160== at 0x80484AC: ??? (go.S:19)
If I debug from Valgrind with --vgdb-error=0
and print the definedness, it says that all the bits are undefined.
(gdb) p &pc
$1 = (int *) 0xfea5e4a8
(gdb) mo xb 0xfea5e4a8 4
ff ff ff ff
0xFEA5E4A8: 0x9e 0x84 0x04 0x08
The value at 0xfea5e4a8 is
(gdb) x/x 0xfea5e4a8
0xfea5e4a8: 0x0804849e
and
(gdb) x/i 0x0804849e
0x804849e <go+10>: leave
(gdb)
#include<stdio.h>
int *a;
extern void go();
int main() {
go();
printf("finito\n");
return 0;
}
int print() {
int answer = a[0];
int pc = a[-1];
// use the vars
if (pc == 0x42) {
printf("%d\n", 0);
}
}
.text
.globl go
go:
pushl %ebp
movl %esp, %ebp
pushl $0x42
call og
leave
ret
og:
addl $4, %esp
movl %esp, a
sub $4, %esp
call print
ret
The problem is this sequence of code:
addl $4, %esp
movl %esp, a
sub $4, %esp
When you move %esp
up valgrind will mark everything below the new stack pointer position as undefined, and it will stay that way even after you move it back.
It's not safe anyway, because if a signal hit between the add and sub then that stack really might get overwritten (in 32 bit code - in 64 bit code there is a "red zone" below the pointer that is safe but valgrind knows about that).