为啥`-fno-omit-frame-pointer` 会干扰 ASAN?

Posted

技术标签:

【中文标题】为啥`-fno-omit-frame-pointer` 会干扰 ASAN?【英文标题】:Why does `-fno-omit-frame-pointer` interfere with ASAN?为什么`-fno-omit-frame-pointer` 会干扰 ASAN? 【发布时间】:2022-01-05 15:13:40 【问题描述】:

在最近的一个项目中,我测试了不同编译器标志和消毒剂的组合,以评估调试我的 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

观察

我使用了编译器和链接器标志的不同组合,有时我观察到地址清理程序报告了 memleak,而在其他情况下它没有报告 memleak。我已经消除了所有潜在的编译器标志,直到找到影响 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 clang 13.0.0-2 glibc 2.33-5

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

【问题讨论】:

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

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

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

【讨论】:

好的,谢谢你的参考。

以上是关于为啥`-fno-omit-frame-pointer` 会干扰 ASAN?的主要内容,如果未能解决你的问题,请参考以下文章

是否有任何分析器可以在 x86_64 上与 -fomit-frame-pointer 一起使用?

你应该同步运行方法吗?为啥或者为啥不?

为啥使用 glTranslatef?为啥不直接更改渲染坐标?

为啥 DataGridView 上的 DoubleBuffered 属性默认为 false,为啥它受到保护?

为啥需要softmax函数?为啥不简单归一化?

为啥 g++ 需要 libstdc++.a?为啥不是默认值?