为什么“-fno-omit-frame-pointer”会干扰 ASAN?

Why does `-fno-omit-frame-pointer` interfere with ASAN?

提问人:Emanuel Schmidt 提问时间:11/28/2021 最后编辑:Emanuel Schmidt 更新时间:11/30/2021 访问量:1102

问:

在最近的一个项目中,我测试了不同编译器标志和清理程序的组合,以评估调试 C 代码的相关性。通过测试这些组合的影响,我偶然发现了一种我不理解的行为。

再生产者

我使用一个包含内存泄漏的小型 hello-world 代码示例来触发地址清理程序 (ASAN):

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

int main () {
  int * memleak = calloc(1, sizeof(int)); // no free -> leaked memory
  printf ("A memleaked memory: %d\n", *memleak);
  printf ("Hello World\n"); // Note: I found that if I comment out this function, ASAN will also report again
}

观察

我使用了编译器和链接器标志的不同组合,有时我观察到地址清理程序报告了内存泄漏,而在其他情况下它没有报告内存泄漏。我已经消除了所有潜在的编译器标志,直到我找到一组影响 ASAN 报告或忽略内存泄漏的最小标志:

使用命令编译时,ASAN 将报告内存泄漏

cc -fsanitize=address -fno-omit-frame-pointer -Og -o main.c.o -c ./main.c      && cc -o hello main.c.o -fsanitize=address,undefined && ./hello # returns 1
cc -fsanitize=address,undefined -Og -o main.c.o -c ./main.c                     && cc -o hello main.c.o -fsanitize=address,undefined && ./hello # returns 1
cc -fsanitize=address,undefined -fno-omit-frame-pointer -o main.c.o -c ./main.c && cc -o hello main.c.o -fsanitize=address,undefined && ./hello # returns 1

使用命令编译时,ASAN 不会报告内存泄漏

cc -fsanitize=address,undefined -fno-omit-frame-pointer -Og -o main.c.o -c ./main.c && cc -o hello main.c.o -fsanitize=address,undefined && ./hello # returns 0

然而

我观察到相同的行为,与使用 GCC 或 clang 无关。因此,我担心这不是一个错误,是由不同消毒剂、优化级别和标志之间的意外干扰引起的,而是我无法理解的预期行为,因为我缺乏了解的影响是什么。-fno-omit-frame-pointer-fno-omit-frame-pointer

如果有人能总结一下它的作用/作用以及在哪些情况下起作用,或者解释这个标志对给定示例的影响,或者指出我找到这些信息的地方,我将不胜感激。-fno-omit-frame-pointer-fomit-frame-pointer

为了完整性

我正在 Arch-linux 上工作,并运行了以下版本的软件:

  • GCC 11.1.0-1
  • 叮叮当当 13.0.0-2
  • 格利布克 2.33-5

但是,我刚刚测试并验证了示例和观察结果也适用于 docker-hub 的 linux/amd64 的 docker 映像。gcc:bullseye

C GCC Clang 地址-消毒剂 乌桑

评论

1赞 Nate Eldredge 11/28/2021
SO 上已经有很多问题解释了什么是帧指针以及省略它(或不省略)意味着什么。例如,参见 stackoverflow.com/questions/10057443/...stackoverflow.com/questions/14666665/...stackoverflow.com/questions/1395591/...stackoverflow.com/questions/579262/...
0赞 Nate Eldredge 11/28/2021
因此,省略帧指针是一种常见的优化,您告诉编译器不要使用该优化。目前尚不清楚为什么要避免它,但无论哪种方式,它都没有充分的理由影响消毒剂,所以这可能是编译器/消毒剂错误。它会影响两个编译器也就不足为奇了,因为 AFAIK 它们共享许多相同的清理程序代码。
0赞 Nate Eldredge 11/28/2021
我认为消毒剂最初来自叮当声。所以我要说的是,看起来 clang 有一个错误,当您同时使用消毒剂和 .(我无法猜测这个原始错误是如何产生的。但 GCC 可能继承了同样的错误,因为他们的清理程序代码是从 clang 移植而来的。-fomit-frame-pointer
0赞 yugr 11/30/2021
@NateEldredge与 [AMT]san 不同,LeakSanitizer 是一个仅库的解决方案,因此它不依赖于编译器。

答:

1赞 yugr 11/30/2021 #1

这是 LeakSanitizer 的已知问题:例如参见 #1233、#937#699。核心原因是 Lsan 是一个比 Asan 简单得多的工具,并且不能保证检测到泄漏。此外,它检测特定泄漏的能力取决于堆栈帧的布局,这些布局可能会因不相关的因素而有所不同(例如在您的情况下添加帧指针)。

不幸的是,这个问题没有可靠的解决方案 - 只需在尽可能多的编译器(gcc、clang)和/或平台(x86、ARM、Android 等)上自动测试您的应用程序,其中一些很可能会发现泄漏。

评论

0赞 Emanuel Schmidt 11/30/2021
好的,谢谢你的参考。