Valgrind 在 "X bytes below stack pointer" 报告 "invalid write"

Valgrind reports "invalid write" at "X bytes below stack pointer"

我是 运行 Valgrind 下的一些代码,使用 gcc 7.5 编译,目标是 aarch64(ARM 64 位)架构,启用了优化。

我收到以下错误:

==3580== Invalid write of size 8
==3580==    at 0x38865C: ??? (in ...)
==3580==  Address 0x1ffeffdb70 is on thread 1's stack
==3580==  16 bytes below stack pointer

这是违规代码附近的程序集转储:

  388640:       a9bd7bfd        stp     x29, x30, [sp, #-48]!
  388644:       f9000bfc        str     x28, [sp, #16]
  388648:       a9024ff4        stp     x20, x19, [sp, #32]
  38864c:       910003fd        mov     x29, sp
  388650:       d1400bff        sub     sp, sp, #0x2, lsl #12
  388654:       90fff3f4        adrp    x20, 204000 <_IO_stdin_used-0x4f0>
  388658:       3dc2a280        ldr     q0, [x20, #2688]
  38865c:       3c9f0fe0        str     q0, [sp, #-16]!

我正在尝试确定这是否是我的代码中可能存在的错误(请注意,我已经彻底检查了我的代码并且我相当有信心它是正确的),或者 Valgrind 是否会盲目地报告任何低于堆栈指针错误。

假设是后者,它看起来像是一个 Valgrind 错误,因为 0x38865c 处的违规指令使用预递减寻址模式,因此它实际上并没有写在堆栈指针下方。

此外,在地址 0x388640 执行了类似的访问(并再次使用预递减寻址模式),但 Valgrind 并未报告;主要区别在于在地址 0x388640 处使用 x 寄存器与在地址 38865c.

处使用 q 寄存器

我还想提请注意 0x388650 处的大堆栈指针减法,这可能与此问题有任何关系,也可能没有任何关系(注意这种减法是有道理的,因为有问题的 C代码在堆栈上声明了一个大数组)。

所以,有人能帮助我理解这一点吗?我是否应该担心我的代码?

code declares a large array on the stack

should [I] worry about my code?

我认为这取决于您希望自己的代码更具可移植性。

使用这段代码,我认为它至少代表了您在 post 中提到的一件重要事情:

#include <stdio.h>
#include <stdlib.h>

long long foo (long long sz, long long v) {
    long long arr[sz]; // allocating a variable on the stack
    arr[sz-1] = v;
    return arr[sz-1];
}

int main (int argc, char *argv[]) {
    long long n = atoll(argv[1]);
    long long v = foo(n, n);
    printf("v = %lld\n", v);
}

$ uname -mprsv
Darwin 20.5.0 Darwin Kernel Version 20.5.0: Sat May  8 05:10:33 PDT 2021; root:xnu-7195.121.3~9/RELEASE_X86_64 x86_64 i386
$ gcc test.c
$ a.out 1047934
v = 1047934
$ a.out 1047935
Segmentation fault: 11

$ uname -snrvmp
Linux localhost.localdomain 3.19.8-100.fc20.x86_64 #1 SMP Tue May 12 17:08:50 UTC 2015 x86_64 x86_64
$ gcc test.c
$ ./a.out 2147483647
v = 2147483647
$ ./a.out 2147483648
v = 2147483648

此代码至少存在一些小的可移植性问题。这两种环境的可分配堆栈内存量差异很大。这仅适用于两个平台。还没有在我的 Windows 10 虚拟机上尝试过,但我认为我不需要这样做,因为我很久以前就被它咬过。

代码看起来没问题,写的肯定不在栈指针下面。该消息似乎是一个 valgrind 错误,可能 #432552,已标记为已修复。 OP 确认将 valgrind 升级到 3.17.0 后不会产生该消息。

除了 Valgrind 错误导致的 OP 问题之外,这个问题的标题肯定会吸引更多人(比如我)将“在堆栈指针下方的 X 字节处进行无效写入”视为合法错误。

我的建议:检查您要写入的地址是否不是另一个函数的局部变量(不存在于调用堆栈中)!

我在函数 yyparse 之外尝试写入 yyget_lloc(yyscanner) 返回的地址时偶然发现了这个问题(前者 returns 后者的局部变量地址).