Search code examples
false-positivememcheck

Memcheck reports unitialised values when accessing local variables down the stack


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)

main.c

#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);
    }
}

go.S

    .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

Solution

  • 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).