☰
ASan(AddressSanitizer)内存错误检测工具详讲
2026/11/3 2:51:27 网站建设 项目流程

一、ASan 是什么

  1. ASan 全称 AddressSanitizer,通常称为地址消毒器,是一种运行时内存错误检测工具。
  2. 主要用于检测 C/C++ 程序中的非法内存访问问题。
  3. 它通过编译器插桩(Instrumentation)和运行时库(Runtime Library)工作。
  4. 与 GDB 的区别:
    • GDB:用于调试程序,查看变量、调用栈、断点和崩溃现场。
    • ASan:用于检测内存错误,并报告错误类型、访问位置及相关内存分配/释放位置。

二、ASan 能检测的常见问题

1. Heap Buffer Overflow(堆内存越界)

  • 使用 malloc/new 申请的堆内存,访问超出申请范围的地址。
  • 例子:
char *p = malloc(10); p[10] = 'A'; // 越界,合法下标为 0~9

2. Stack Buffer Overflow(栈内存越界)

  • 访问局部数组或栈上对象的边界之外。
  • 例子:
int arr[5] = {0}; arr[5] = 100; // 越界,合法下标为 0~4

3. Global Buffer Overflow(全局变量越界)

  • 访问全局数组或静态存储区对象的边界之外。

4. Use After Free(释放后使用)

  • 内存已经通过 free/delete 释放,程序仍然通过旧指针访问它。
  • 例子:
int *p = malloc(sizeof(int)); free(p); printf("%d", *p); // 错误:释放后继续访问
  • 注意:free(p) 不会自动把指针 p 置为 NULL。

5. Double Free(重复释放)

  • 同一块动态内存被释放两次。
  • 例子:
int *p = malloc(sizeof(int)); free(p); free(p); // 错误
  • 常见原因:多个模块对同一资源的所有权和释放责任不清晰。

6. Use After Return(函数返回后使用栈内存)

  • 函数返回后,仍然访问该函数栈帧中的局部对象。
  • 是否检测到取决于编译器、运行时和相关配置。

7. Memory Leak(内存泄漏)

  • 动态申请的内存不再使用,却没有释放。
  • 在支持 LeakSanitizer 的平台上,ASan 通常可以配合泄漏检测功能发现此类问题。
  • 是否可用取决于平台和运行时支持。

三、ASan 的工作原理

1. 编译器插桩

  • 编译时使用-fsanitize=address。
  • 编译器会在内存访问等位置插入额外检查代码。

2. 运行时库(ASan Runtime)

  • 程序运行时配合插桩代码执行检查。
  • 发现非法访问时,输出错误报告,通常终止程序。

3. Shadow Memory(影子内存)

  • ASan 使用额外的影子内存记录应用内存区域的可访问状态。
  • 程序访问内存时,插桩代码查询相应状态,判断访问是否合法。
  • 可以把它理解为:为程序内存维护一份辅助的状态记录。

4. 性能影响

  • ASan 会增加内存占用和运行时间。
  • 常见资料中提到的运行时间开销约为 2 倍,但实际开销与程序和平台有关。
  • 通常用于开发、测试和问题定位,不应未经评估就用于正式生产版本。

四、GCC / G++ 中如何使用 ASan

1. C 程序编译

gcc -g -O1 -fsanitize=address -fno-omit-frame-pointer test.c -o test

2. C++ 程序编译

g++ -g -O1 -fsanitize=address -fno-omit-frame-pointer test.cpp -o test

3. 运行

./test

4. 常用参数说明

  • -g:生成调试信息,便于定位源代码行号。
  • -O1:使用较低级别优化,兼顾运行表现和调试定位。
  • -fsanitize=address:启用 AddressSanitizer。
  • -fno-omit-frame-pointer:保留帧指针,有助于生成更清晰的调用栈。

5. 重要

  • 编译阶段和最终链接阶段都要带上-fsanitize=address。
  • 多文件工程应确保相关目标文件按一致的方式启用 ASan。
  • 运行时需要能够找到匹配的 ASan Runtime。

五、CMake 工程中启用 ASan

可以在 CMakeLists.txt 中添加:

cmake_minimum_required(VERSION 3.10) project(MyProject) set(CMAKE_CXX_STANDARD 11) add_compile_options( -g -O1 -fsanitize=address -fno-omit-frame-pointer ) add_link_options( -fsanitize=address ) add_executable(app main.cpp network.cpp video.cpp )

构建和运行:

mkdir build cd build cmake .. cmake --build . ./app

说明:

  • add_compile_options用于添加编译选项。
  • add_link_options用于添加链接选项。
  • 如果只想对某个目标启用,可以使用target_compile_options()和target_link_options()。
  • add_link_options()需要较新的 CMake 版本;旧版本可使用目标级链接选项等方式配置。

六、如何阅读 ASan 错误报告

常见报告关键字:

  1. heap-buffer-overflow:堆内存越界。
  2. stack-buffer-overflow:栈内存越界。
  3. global-buffer-overflow:全局变量内存越界。
  4. heap-use-after-free:访问已经释放的堆内存。
  5. attempting double-free:重复释放同一块内存。

阅读报告时重点关注:

  • ERROR:错误类型。
  • READ / WRITE:非法读取还是非法写入。
  • at ... file.c:行号:错误访问发生的位置。
  • allocated by:内存最初申请的位置。
  • freed by:内存释放的位置。
  • thread T0 / T1:发生问题的线程信息。

排查思路:

  1. 先确认错误类型。
  2. 找到报告中的源代码行号。
  3. 检查出错地址对应的内存对象及其边界。
  4. 如果涉及动态内存,继续查看分配和释放位置。
  5. 沿调用栈检查资源所有权、生命周期和并发访问逻辑。
  6. 修复后重新编译并运行,确认报告消失,并补充回归测试。

七、ASan 在嵌入式 Linux 项目中的用途

1. TCP 网络通信

  • 检查接收缓冲区长度计算错误。
  • 检查数据拷贝时是否超出缓冲区边界。

2. 视频流与图像处理

  • 检查视频帧缓存越界。
  • 检查帧缓冲区释放后仍被其他模块访问。
  • 检查图像尺寸、步长和内存长度计算错误。

3. 多线程程序

  • 检查线程访问已经释放的对象。
  • 检查队列节点、任务对象等资源的生命周期问题。
  • 注意:ASan 不是专门的数据竞争检测工具,数据竞争通常使用 TSan。

4. 链表、队列和缓存管理

  • 检查删除节点后继续访问。
  • 检查重复释放节点。
  • 检查缓冲区读写越界。

5. 动态内存管理

  • 检查申请后未释放的内存(需要平台支持泄漏检测)。
  • 帮助定位内存对象的分配和释放位置。

八、交叉编译和开发板上的注意事项

在 i.MX6ULL、RK3568 等嵌入式 Linux 平台上使用 ASan,需要同时满足:

  1. 交叉编译器支持-fsanitize=address。
  2. 目标架构有匹配的 ASan 运行时库。
  3. 目标根文件系统中包含运行时所需的库及依赖。
  4. 程序能够在开发板上正确加载这些库。
  5. 编译器、运行时库和目标系统的架构及 ABI 必须匹配。

注意:

  • 编译器接受-fsanitize=address,不代表目标板一定能够运行程序。
  • 交叉编译时要检查工具链 sysroot 和开发板的根文件系统中有没有 ASan 的运行时库。
  • 建议先在 Ubuntu x86 Linux 上练习,再研究目标板上的支持情况。
  • 嵌入式设备资源有限,ASan 的额外内存和性能开销需要评估。

九、相关工具对比

1. GDB

  • 用途:断点调试、单步执行、查看变量、调用栈和崩溃现场。

2. ASan(AddressSanitizer)

  • 用途:检测堆/栈/全局内存越界、释放后使用、重复释放等问题。

3. TSan(ThreadSanitizer)

  • 用途:检测多线程数据竞争等线程同步问题。

4. UBSan(UndefinedBehaviorSanitizer)

  • 用途:检测部分未定义行为,例如有符号整数溢出、非法移位、某些类型对齐问题等。

5. Valgrind

  • 用途:动态分析内存访问错误、内存泄漏等;运行开销通常较大,且需要考虑目标平台支持。

十、建议掌握程度

对于嵌入式 Linux C/C++ 应用开发,建议掌握:

  1. 理解 ASan 的作用和基本工作原理。
  2. 能区分堆越界、栈越界、Use After Free、Double Free。
  3. 能使用 GCC / G++ 的-fsanitize=address编译程序。
  4. 能在 CMake 工程中启用 ASan。
  5. 能读懂 ASan 报告,定位错误访问、内存申请和释放位置。
  6. 了解 ASan 与 GDB、TSan、UBSan、Valgrind 的区别。
  7. 了解交叉编译时 ASan Runtime 的依赖。

建议动手练习:

  • 编写一个堆内存越界程序。
  • 编写一个栈数组越界程序。
  • 编写一个 Use After Free 程序。
  • 编写一个 Double Free 程序。
  • 分别使用 ASan 编译运行,观察报告并修复问题。

一句话总结:ASan 是一种通过编译器插桩和运行时检查来发现内存错误的工具。它能帮助开发者定位非法内存访问及相关的申请、释放位置,是 C/C++ 程序开发和测试阶段非常实用的内存排错工具。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询