0

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

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

4

1 回答 1

1

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

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

于 2021-11-29T17:02:34.923 回答