Memcheck 在访问堆栈中的局部变量时报告单元化值
Memcheck reports unitialised values when accessing local variables down the stack
我遇到了 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)
如果我使用 --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 知道这一点)。
我遇到了 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)
如果我使用 --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 知道这一点)。