AddressSanitizer(ASan)工具是用于用户空间程序的内存错误检测工具,无法直接用于内核空间。
ASAN早先是LLVM中的特性,后被加入gcc4.8,成为 gcc 的一部分,但不支持符号信息,无法显示出问题的函数和行数。从 4.9 开始,gcc 支持 AddressSanitizer 的所有功能。因此gcc 4.8以上版本使用ASAN时不需要安装第三方库,通过在编译时指定编译CFLAGS即可打开开关。
ASan利用编译器插桩和运行时库来检测内存错误,例如缓冲区溢出、使用未初始化的内存等。但是,内核和用户空间程序的执行环境和内存管理方式不同,因此ASan无法直接应用于内核空间。
对于内核空间的内存错误检测,可以考虑使用其他专门针对内核开发的工具,如KASAN(Kernel AddressSanitizer)。KASAN是Linux内核中的一个内存错误检测工具,可以用于检测内核空间的内存问题。后续我们介绍学习。
优点:
缺点:
综上所述,ASan作为一种内存错误检测工具,具有很多优点,但也存在一些缺点。在使用ASan之前,需要权衡其带来的利弊,根据具体情况选择适合的工具和方法来确保代码的质量和可靠性。
which aarch64-none-linux-gnu-gcc
/usr/local/gcc-arm-9.2-2019.12-x86_64-aarch64-none-linux-gnu/bin/aarch64-none-linux-gnu-gcc
./aarch64-none-linux-gnu/lib64/libasan.so
./aarch64-none-linux-gnu/libc/usr/lib64/libasan.so
gcc编译选项:
#-fsanitize=address:开启内存越界检测
#-fsanitize-recover=address:一般后台程序为保证稳定性,不能遇到错误就简单退出,而是继续运行,采用该选项支持内存出错之后程序继续运行,需要叠加设置ASAN_OPTIONS=halt_on_error=0才会生效;若未设置此选项,则内存出错即报错退出
#-fno-stack-protector:去使能栈溢出保护
#-fno-omit-frame-pointer:去使能栈溢出保护
//命名test.c
#include
#include
char* getMemory()
{
char *p = (char *)malloc(30);
return p;
}
int main()
{
char *p = getMemory();
p = NULL;
return 0;
}
//这段代码存在内存泄漏的问题。在函数getMemory中,它使用malloc函数动态分配了内存,并返回指向该内存的指针。
//然而,在main函数中,获取到指向动态分配内存的指针后,立即将指针赋值为NULL,导致之前分配的内存地址丢失,
//无法再被释放。
ASAN_OPTIONS设置:
ASAN_OPTIONS是Address-Sanitizier的运行选项环境变量。
#halt_on_error=0:检测内存错误后继续运行
#detect_leaks=1:使能内存泄露检测
#malloc_context_size=15:内存错误发生时,显示的调用栈层数为15
#log_path=/home/asan.log:内存检查问题日志存放文件路径
#env |grep ASAN_OPTIONS
设置环境变量,
# export ASAN_OPTIONS=halt_on_error=0:use_sigaltstack=0:detect_leaks=1:malloc_context_size=15:log_path=/home/asan.log
# env |grep ASAN_OPTIONS
# gcc test.c -fsanitize=address -fsanitize-recover=address -fno-stack-protector -fno-omit-frame-pointer
运行a.out会生成acan检出的log, /home/asan.log.60920,如下:
=================================================================
==114141==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 30 byte(s) in 1 object(s) allocated from:
#0 0x7fb16294bb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
#1 0x55ca068137fb in getMemory (/home/wuqianlong/a.out+0x7fb)
#2 0x55ca06813817 in main (/home/wuqianlong/a.out+0x817)
#3 0x7fb16249dc86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
SUMMARY: AddressSanitizer: 30 byte(s) leaked in 1 allocation(s).
现在创建两个文件,test.c和test1.c, test.c的内容如上述内容。
//test1.c
#include
#include
extern char* getMemory();
int main()
{
char *p = getMemory();
p = NULL;
return 0;
}
将test.c编译成共享库libtest.so
gcc -shared -o libtest.so test.c -fsanitize=address -fsanitize-recover=address -fno-stack-protector -fPIC
链接共享库,编译test1.c成a.out文件
gcc -o a.out test1.c /home/wuqianlong/libtest.so -fsanitize=address -fsanitize-recover=address -fno-stack-protector
运行a.out文件生成asan log如下:
=================================================================
==89713==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 30 byte(s) in 1 object(s) allocated from:
#0 0x7f0efbcefb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.4+0xdeb40)
#1 0x7f0efba0f6eb in getMemory (/home/wuqianlong/libtest.so+0x6eb)
#2 0x5570669f283b in main (/home/wuqianlong/a.out+0x83b)
#3 0x7f0efb63fc86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x21c86)
SUMMARY: AddressSanitizer: 30 byte(s) leaked in 1 allocation(s).
#include
#include
int main(int argc, char **argv)
{
int *array = malloc(sizeof (int) * 100);
array[0] = 0;
int res = array[1 + 100]; //array访问越界
free(array);
pause();//程序等待,不退出
return 0;
}
编译命令:
gcc heapOOB.c -o heapOOB -g -fsanitize=address -fsanitize=leak
执行情况:
==3653==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61400000ffd4 at pc 0x000000400871 bp 0x7ffe50cde9c0 sp 0x7ffe50cde9b0
READ of size 4 at 0x61400000ffd4 thread T0
#0 0x400870 in main /home/jetpack/work/4G/test/asan/heapOOB.c:7
#1 0x7f30b337a83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapOOB+0x400708)
0x61400000ffd4 is located 4 bytes to the right of 400-byte region [0x61400000fe40,0x61400000ffd0)
allocated by thread T0 here:
#0 0x7f30b37bc602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x4007ee in main /home/jetpack/work/4G/test/asan/heapOOB.c:5
#2 0x7f30b337a83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#include
#include
int main(int argc, char **argv)
{
int stack_array[100];
stack_array[100] = 0;//栈访问越界
pause();
return 0;
}
编译命令:
gcc stackOOB.c -o stackOOB -g -fsanitize=address -fsanitize=leak
执行结果:
==3952==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffdb72e830 at pc 0x0000004008ef bp 0x7fffdb72e660 sp 0x7fffdb72e650
WRITE of size 4 at 0x7fffdb72e830 thread T0
#0 0x4008ee in main /home/jetpack/work/4G/test/asan/stackOOB.c:6
#1 0x7f0c47a8e83f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#2 0x400748 in _start (/home/jetpack/work/4G/test/asan/stackOOB+0x400748)
Address 0x7fffdb72e830 is located in stack of thread T0 at offset 432 in frame
#0 0x400825 in main /home/jetpack/work/4G/test/asan/stackOOB.c:4
This frame has 1 object(s):
[32, 432) 'stack_array' <== Memory access at offset 432 overflows this variable
HINT: this may be a false positive if your program uses some custom stack unwind mechanism or swapcontext
(longjmp and C++ exceptions *are* supported)
#include
#include
int main(int argc, char **argv)
{
int *array = malloc(sizeof (int) * 100);
array[0] = 0;
free(array);
int res = array[0]; //使用已经释放的内存
pause();
return 0;
}
编译命令:
gcc heapUAF.c -o heapUAF -g -fsanitize=address -fsanitize=leak
执行结果:
==4385==ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe40 at pc 0x000000400877 bp 0x7ffdc9019c20 sp 0x7ffdc9019c10
READ of size 4 at 0x61400000fe40 thread T0
#0 0x400876 in main /home/jetpack/work/4G/test/asan/heapUAF.c:8
#1 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapUAF+0x400708)
0x61400000fe40 is located 0 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0)
freed by thread T0 here:
#0 0x7f93f91982ca in __interceptor_free (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x982ca)
#1 0x40083f in main /home/jetpack/work/4G/test/asan/heapUAF.c:7
#2 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
previously allocated by thread T0 here:
#0 0x7f93f9198602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x4007ee in main /home/jetpack/work/4G/test/asan/heapUAF.c:5
#2 0x7f93f8d5683f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
#include
#include
#include
int main(int argc, char **argv)
{
int *array = malloc(sizeof (int) * 100);
memset(array, 0, 100 * 4);
return 0;
}
//内存泄漏:在 main 函数中通过调用 malloc 分配了内存空间给 array,但在函数结束时并未调用 free 来释放这块内存。这将导致在每次程序运行时都会分配一块新的内存空间,而不会释放之前分配的内存,最终可能导致内存泄漏。
编译命令:
gcc heapLeak.c -o heapLeak -g -fsanitize=address -fsanitize=leak
执行结果:
==3120==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 400 byte(s) in 1 object(s) allocated from:
#0 0x7f412d5b7602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
#1 0x40073e in main /home/jetpack/work/4G/test/asan/heapLeak.c:7
#2 0x7f412d17583f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2083f)
SUMMARY: AddressSanitizer: 400 byte(s) leaked in 1 allocation(s).
int *ptr;
void usr_func(void)
{
//申请栈内存
int local[100] = {0};
ptr = local;
}
int main(void)
{
//使用函数栈返回的内存
*ptr = 0;
return 0;
}
int global_array[100] = {0};
int main(int argc, char **argv)
{
//全局变量global_array访问越界
global_array[101];
return 0;
}
感谢一起学习,有疑问评论区讨论。