前言
我们在进行编程时,有时不免会无意中写出一些容易导致内存问题(可能一时表象上正常)的代码,导致的后果肯定是不好的,就像一颗颗“哑弹”,令人心慌。网上推荐的辅助工具很多,此篇文章只简单介绍其中的一种---经典的asan。
编译选项
一般gcc和交叉编译工具链里面应该都集成了(如果没有,自行添加或换个工具,本文免读),只需在编译代码之前加上支持asan的编译选项即可。
具体情况见下方代码块,根据自己的需求添加对应的编译选项。
Makefile
# 假设您的源文件是 main.c # 原来的编译命令可能是这样的
main.o: main.c gcc -c main.c # 修改后的编译命令,添加 -fsanitize=address 选项
main.o: main.c gcc -c -fsanitize=address main.c # 在链接时,您也需要添加相同的选项
my_program: main.o gcc -fsanitize=address main.o -o my_programCMake
# CMakeLists.txt # 添加编译器选项
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") # 或者使用 add_compile_options(对于较新的CMake版本)
add_compile_options(-fsanitize=address) # 其余的CMake指令...GCC命令行
gcc -fsanitize=address -o my_program my_program.cQt(.pro)
C++工程
QMAKE_CXXFLAGS += -fsanitize=address
C工程
QMAKE_CFLAGS += -fsanitize=address
示例
下面只以gcc为例,演示asan的用法。
代码
首先编写一份简单有内存泄漏的C代码(test.c),如下:
#include<stdio.h>int main(int argc, char *argv[])
{char hello[6] = "hello";for(int i = 0; i <= 6; i++){printf("hello[%d] = %c\n", i, hello[i]);}return 0;
}
编译
gcc -fsanitize=address -o test_asan test.c
运行
root@ubuntu:/tmp# ./test_asan
hello[0] = h
hello[1] = e
hello[2] = l
hello[3] = l
hello[4] = o
hello[5] =
=================================================================
==11068== ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7ffde1c9dd66 at pc 0x400919 bp 0x7ffde1c9dd10 sp 0x7ffde1c9dd08
READ of size 1 at 0x7ffde1c9dd66 thread T0#0 0x400918 (/tmp/test_asan+0x400918)#1 0x7f62939f6f44 (/lib/x86_64-linux-gnu/libc-2.19.so+0x21f44)#2 0x4007a8 (/tmp/test_asan+0x4007a8)
Address 0x7ffde1c9dd66 is located at offset 38 in frame <main> of T0's stack:This frame has 1 object(s):[32, 38) 'hello'
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext(longjmp and C++ exceptions *are* supported)
Shadow bytes around the buggy address:0x10003c38bb50: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10003c38bb60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10003c38bb70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10003c38bb80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10003c38bb90: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x10003c38bba0: 00 00 00 00 00 00 00 00 f1 f1 f1 f1[06]f4 f4 f40x10003c38bbb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10003c38bbc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10003c38bbd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10003c38bbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 000x10003c38bbf0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):Addressable: 00Partially addressable: 01 02 03 04 05 06 07 Heap left redzone: faHeap righ redzone: fbFreed Heap region: fdStack left redzone: f1Stack mid redzone: f2Stack right redzone: f3Stack partial redzone: f4Stack after return: f5Stack use after scope: f8Global redzone: f9Global init order: f6Poisoned by user: f7ASan internal: fe
==11068== ABORTING
从上面可以看出程序内存有溢出,溢出的地址也标明。
如果将上述测试代码循环条件修改正确(i<6),重新编译再执行,结果如下,没有报错误。