Memcheck 在访问堆栈中的局部变量时报告单元化值

Memcheck reports unitialised values when accessing local variables down the stack

我遇到了 Memcheck 报告未初始化值的问题,我认为这些是完全合法的。我设法创建了一个展示这种行为的小示例程序。我想知道 Memcheck 是否真的错了以及可以做些什么。 (除了将错误添加到抑制文件中,还有什么解决方法吗?)

为了重现这一点,我制作了下面的程序。它 运行 的函数 go0x42 放入堆栈,调用 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)

如果我使用 --vgdb-error=0 从 Valgrind 调试并打印定义,它说所有位都未定义。

(gdb) p &pc
 = (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 [=17=]x42
    call og

    leave
    ret

og:
    addl , %esp
    movl %esp, a
    sub , %esp
    call print
    ret

问题是这个代码序列:

addl , %esp
movl %esp, a
sub , %esp

当您向上移动%esp时,valgrind 会将新堆栈指针位置下方的所有内容标记为未定义,即使您将其移回后,它也会保持这种状态。

无论如何都不安全,因为如果在 add 和 sub 之间有信号命中,那么该堆栈确实可能会被覆盖(在 32 位代码中 - 在 64 位代码中,指针下方有一个 "red zone"安全,但 valgrind 知道这一点)。