前言
我们在进行编程时,有时不免会无意中写出一些容易导致内存问题(可能一时表象上正常)的代码,导致的后果肯定是不好的,就像一颗颗“哑弹”,令人心慌。网上推荐的辅助工具很多,此篇文章只简单介绍其中的一种---经典的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_program
CMake
# 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.c
Qt(.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 00
0x10003c38bb60: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10003c38bb70: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10003c38bb80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10003c38bb90: 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 f4
0x10003c38bbb0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10003c38bbc0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10003c38bbd0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10003c38bbe0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x10003c38bbf0: 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: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Heap righ redzone: fb
Freed Heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack partial redzone: f4
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
ASan internal: fe
==11068== ABORTING
从上面可以看出程序内存有溢出,溢出的地址也标明。
如果将上述测试代码循环条件修改正确(i<6),重新编译再执行,结果如下,没有报错误。