2016-01-09 44 views
0

我遇到了Memcheck报告未初始化值的问题,我认为这些都是完全合法的。我设法创建了一个展示此行为的小示例程序。我想知道Memcheck是否真的错了,可以做些什么。 (除了将错误添加到抑制文件以外是否还有其他解决方案?)Memcheck在向堆栈中访问局部变量时报告单位值

要重现此问题,我制作了以下程序。它运行函数go,将0x42放入堆栈,调用og(这会将下一条指令leave的地址推入堆栈),然后在og中将esp+4存储到全局变量a中。

堆栈看起来是这样的:

| address of `leave` instruction |      pc = a[-1] 
| 0x42       | a points here, answer = a[0] 

如果我建立并运行Valgrind的,

gcc -g -m32 main.c go.S -o main 
valgrind --track-origins=yes ./main 

Valgrind的认为,在可变pc(和answer的值,如果你把它放在相反,if)未定义。我用调试器检查了那里的值实际上是我想要的。

==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) 

如果我从Valgrind的调试与--vgdb-error=0和打印definedness,它说,所有的位是不确定的。

(gdb) p &pc 
$1 = (int *) 0xfea5e4a8 
(gdb) mo xb 0xfea5e4a8 4 
       ff  ff  ff  ff 
0xFEA5E4A8:  0x9e 0x84 0x04 0x08 

在0xfea5e4a8值是

(gdb) x/x 0xfea5e4a8 
0xfea5e4a8:  0x0804849e 

(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 
+1

我认为这个问题可能是,只要你做'addl $ 4,%esp',你不再拥有超出esp的堆栈部分。情况总是如此。虽然它很少发生,但O/S可以自由地覆盖堆栈的那部分,比如在中断处理程序中。 –

回答

1

问题是这样的序列代码:

addl $4, %esp 
movl %esp, a 
sub $4, %esp 

当您移动%esp了Valgrind的将新的堆栈指​​针位置下的所有标记为不确定的,它会留你将其移回后也如此。

反正它不安全,因为如果在add和sub之间发生信号,那么堆栈确实可能会被覆盖(在32位代码中 - 在64位代码中,指针下面有一个“红色区域”,这是安全的,但valgrind知道这一点)。