c-为什么"-fno省略帧指针"会干扰ASAN



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

再现器

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

#include<stdlib.h>
#include<stdio.h>
int main () {
int * memleak = calloc(1, sizeof(int)); // no free -> leaked memory
printf ("A memleaked memory: %dn", *memleak);
printf ("Hello Worldn"); // 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
  • glibc 2.33-5

然而,我刚刚测试并验证了该示例和观察结果也将适用于dockerhub的linux/aamd64的docker映像gcc:bullseye

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

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

最新更新