内存检测神器 ASan(AddressSanitizer) 原理、编译配置与工程实战 ASan全称AddressSanitizer是一种内存错误检测工具目的是帮助开发者检测和调试内存相关的问题如使用未分配的内存、使用已释放的内存、堆内存溢出等。ASan是由Google开发的广泛用于C、C等语言的代码中。ASan的工作原理是在编译时将额外的代码插入到目标程序中对内存的读写操作进行检测和记录。当程序运行时ASan会监测内存访问一旦发现内存访问错误比如越界访问、释放后再次访问等会立即输出错误信息并中断程序执行同时提供详细的报告帮助开发者定位问题的源头。通过使用ASan开发者可以在早期阶段发现和解决潜在的内存错误问题有效提高程序的稳定性和安全性。ASan在调试阶段是一个非常有用的工具但由于其会引入一些性能开销因此在生产环境中通常不建议启用ASan。ASan使用要使用ASan你需要使用支持ASan的编译器如Clang或GCC并开启ASan相关的编译选项。使用Clang编译器在终端执行以下命令clang -fsanitizeaddress -g your_program.c -o your_program使用GCC编译器在终端执行以下命令gcc -fsanitizeaddress -g your_program.c -o your_program在上述命令中-fsanitizeaddress是ASan的编译选项用于开启ASan。-g选项用于生成调试符号以支持调试和定位错误。当然我们也可以通过环境变量的方式加入ASan编译选项然后编译额时候需要加上环境变量一般是CFLAGS或者CXXFLAGS。更推荐这种方式因为通过makefile方式编译的时候这种方式经常见。export CFLAGS-fsanitizeaddress -g $CFLAGSgcc $CFLAGS your_program.c -o your_program编译完成后运行生成的可执行文件ASan会在运行时监测程序的内存访问情况并在发现错误时提供详细的错误信息包括错误的位置和类型。下面是一个使用C语言编写的示例程序可以用来使用ASan进行内存错误检测#include stdio.h #include stdlib.h void leak_memory() { int* p malloc(sizeof(int) * 10); // 没有释放内存导致内存泄漏 } int main() { int* arr malloc(sizeof(int) * 5); arr[5] 10; // 内存访问越界错误 free(arr); // 使用 free 释放内存 int* p malloc(sizeof(int)); free(p); // 使用 free 释放内存 int* q NULL; *q 5; // 使用空指针访问内存错误 leak_memory(); return 0; }编译并运行gcc $CFLAGS asan.c -o asan./asan一切顺利的话会输出下面这种异常。296710ERROR: AddressSanitizer: heap-buffer-overflow on address 0xffff7b700b64 at pc 0x000000400810 bp 0xffffd7b963b0 sp 0xffffd7b963a0WRITE of size 4 at 0xffff7b700b64 thread T0#0 0x40080c in main /home/test/asan.c:11#1 0xffff7f38df38 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#2 0xffff7f38e004 in __libc_start_main_impl ../csu/libc-start.c:409#3 0x4006ac in _start (/home/test/asan0x4006ac)0xffff7b700b64 is located 0 bytes to the right of 20-byte region [0xffff7b700b50,0xffff7b700b64)allocated by thread T0 here:#0 0xffff7f5a6080 in malloc (/usr/lib64/libasan.so.60xa9080)#1 0x4007b0 in main /home/test/asan.c:10#2 0xffff7f38df38 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58#3 0xffff7f38e004 in __libc_start_main_impl ../csu/libc-start.c:409#4 0x4006ac in _start (/home/test/asan0x4006ac)SUMMARY: AddressSanitizer: heap-buffer-overflow /home/test/asan.c:11 in mainShadow bytes around the buggy address:0x200fef6e0110: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x200fef6e0120: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x200fef6e0130: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x200fef6e0140: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x200fef6e0150: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x200fef6e0160: fa fa fa fa fa fa fa fa fa fa 00 00[04]fa fa fa0x200fef6e0170: 00 00 00 fa fa fa fa fa fa fa fa fa fa fa fa fa0x200fef6e0180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x200fef6e0190: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x200fef6e01a0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa0x200fef6e01b0: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa faShadow byte legend (one shadow byte represents 8 application bytes):Addressable: 00Partially addressable: 01 02 03 04 05 06 07Heap left redzone: faFreed heap region: fdStack left redzone: f1Stack mid redzone: f2Stack right redzone: f3Stack after return: f5Stack use after scope: f8Global redzone: f9Global init order: f6Poisoned by user: f7Container overflow: fcArray cookie: acIntra object redzone: bbASan internal: feLeft alloca redzone: caRight alloca redzone: cbShadow gap: cc296710ABORTING看堆栈调用信息可以看到#0 0x40080c in main /home/test/asan.c:11在asan.c文件中的第11行出现了异常我们看第11行可以知道只有5个元素却要访问第6个元素导致了数组溢出。 如果你修改好了第11行重新编译运行会发现还会有其他异常可以动手自己实验把这个程序修复好。需要特别特别注意的是ASan会增加程序的运行时间和内存开销因此主要用于调试和测试阶段特别不建议在生产环境中启用。ASan其他选项除了-fsanitizeaddress外还有其他 AddressSanitizer 相关的编译选项可供选择。以下是一些常用的 AddressSanitizer 编译选项及其作用Memory Sanitizer (-fsanitizememory) 用于检测对未初始化内存或使用已释放内存的操作。这个选项可以帮助发现一些难以察觉的内存错误。UndefinedBehaviorSanitizer (-fsanitizeundefined)用于检测未定义行为例如整数溢出、空指针解引用等问题。这有助于发现代码中的潜在 bug。Thread Sanitizer (-fsanitizethread)用于检测多线程程序中的数据竞争和死锁问题。这个选项可以帮助识别并修复多线程程序中的并发 bug。Address Sanitizer with Leak Detection (-fsanitizeleak)启用 AddressSanitizer 的同时也检测内存泄漏问题。这个选项有助于发现代码中的内存泄漏 bug。Coverage Sanitizer (-fsanitizecoverage)用于生成代码覆盖率报告检测程序中哪些部分被执行过。这个选项通常用于代码覆盖率测试和分析。Kernel Address Sanitizer (-fsanitizekernel-address)针对 Linux 内核模块开发用于检测内核中的内存错误。