网站建设营销技巧,ie浏览器官方网址入口,厦门做商城网站,建材营销型的网站ASAN功能介绍
AddressSanitizer#xff08;ASan#xff09;工具是用于用户空间程序的内存错误检测工具#xff0c;无法直接用于内核空间。
ASAN早先是LLVM中的特性#xff0c;后被加入gcc4.8#xff0c;成为 gcc 的一部分#xff0c;但不支持符号信息#xff0c;无法显…ASAN功能介绍
AddressSanitizerASan工具是用于用户空间程序的内存错误检测工具无法直接用于内核空间。
ASAN早先是LLVM中的特性后被加入gcc4.8成为 gcc 的一部分但不支持符号信息无法显示出问题的函数和行数。从 4.9 开始gcc 支持 AddressSanitizer 的所有功能。因此gcc 4.8以上版本使用ASAN时不需要安装第三方库通过在编译时指定编译CFLAGS即可打开开关。
ASan利用编译器插桩和运行时库来检测内存错误例如缓冲区溢出、使用未初始化的内存等。但是内核和用户空间程序的执行环境和内存管理方式不同因此ASan无法直接应用于内核空间。
对于内核空间的内存错误检测可以考虑使用其他专门针对内核开发的工具如KASANKernel AddressSanitizer。KASAN是Linux内核中的一个内存错误检测工具可以用于检测内核空间的内存问题。后续我们介绍学习。
优点
检测能力强大ASan可以检测到许多内存相关的错误如缓冲区溢出、使用未初始化的内存、使用已释放的内存等。它通过在运行时对程序进行插桩来捕获并报告这些错误帮助开发人员及早发现和修复问题。低侵入性ASan工具通过编译器插桩和运行时库来实现内存错误检测对代码的更改较少对现有代码的侵入性较小。这使得它相对容易集成到现有项目中而无需进行大规模的代码修改。容易定位问题当ASan检测到内存错误时它会提供详细的错误报告包括错误类型、触发位置和调用栈信息。这有助于开发人员快速定位和修复问题减少调试时间。平台支持广泛ASan工具在多个平台和编译器中都有支持包括Linux、macOS和Windows等。这使得开发人员可以在不同的环境中使用相同的工具提高了可移植性和开发效率。
缺点
内存开销较大ASan会在运行时为每个分配的内存块添加额外的元数据并在每次内存访问时进行检查。这会导致内存使用量增加有时可能会显著影响程序的性能和内存占用。编译时间延长由于ASan需要对程序进行插桩编译时间可能会延长。特别是对于大型项目或包含大量代码的程序这可能会导致较长的编译时间。不支持所有错误类型尽管ASan可以检测到许多内存相关的错误但它并不能覆盖所有可能的问题。某些类型的错误如数据竞争和内存泄漏ASan无法直接检测到。可能引入假阳性由于ASan是在运行时进行检测的它可能会产生一些误报假阳性即报告了实际上并非错误的情况。这可能会增加调试和排除问题的复杂性。
综上所述ASan作为一种内存错误检测工具具有很多优点但也存在一些缺点。在使用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.sogcc编译选项
#-fsanitizeaddress开启内存越界检测 #-fsanitize-recoveraddress一般后台程序为保证稳定性不能遇到错误就简单退出而是继续运行采用该选项支持内存出错之后程序继续运行需要叠加设置ASAN_OPTIONShalt_on_error0才会生效若未设置此选项则内存出错即报错退出
#-fno-stack-protector去使能栈溢出保护 #-fno-omit-frame-pointer去使能栈溢出保护
test.c测试程序
//命名test.c
#include stdio.h
#include stdlib.hchar* 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_error0检测内存错误后继续运行 #detect_leaks1:使能内存泄露检测 #malloc_context_size15内存错误发生时显示的调用栈层数为15 #log_path/home/asan.log:内存检查问题日志存放文件路径
#env |grep ASAN_OPTIONS 设置环境变量
# export ASAN_OPTIONShalt_on_error0:use_sigaltstack0:detect_leaks1:malloc_context_size15:log_path/home/asan.log
# env |grep ASAN_OPTIONS
# gcc test.c -fsanitizeaddress -fsanitize-recoveraddress -fno-stack-protector -fno-omit-frame-pointer运行a.out会生成acan检出的log, /home/asan.log.60920,如下 114141ERROR: LeakSanitizer: detected memory leaksDirect leak of 30 byte(s) in 1 object(s) allocated from:#0 0x7fb16294bb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.40xdeb40)#1 0x55ca068137fb in getMemory (/home/wuqianlong/a.out0x7fb)#2 0x55ca06813817 in main (/home/wuqianlong/a.out0x817)#3 0x7fb16249dc86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.60x21c86)SUMMARY: AddressSanitizer: 30 byte(s) leaked in 1 allocation(s).动态库的asan日志定位
现在创建两个文件test.c和test1.c, test.c的内容如上述内容。
//test1.c
#include stdio.h
#include stdlib.hextern char* getMemory();int main()
{char *p getMemory();p NULL;return 0;
}将test.c编译成共享库libtest.so
gcc -shared -o libtest.so test.c -fsanitizeaddress -fsanitize-recoveraddress -fno-stack-protector -fPIC链接共享库编译test1.c成a.out文件
gcc -o a.out test1.c /home/wuqianlong/libtest.so -fsanitizeaddress -fsanitize-recoveraddress -fno-stack-protector运行a.out文件生成asan log如下 89713ERROR: LeakSanitizer: detected memory leaksDirect leak of 30 byte(s) in 1 object(s) allocated from:#0 0x7f0efbcefb40 in __interceptor_malloc (/usr/lib/x86_64-linux-gnu/libasan.so.40xdeb40)#1 0x7f0efba0f6eb in getMemory (/home/wuqianlong/libtest.so0x6eb)#2 0x5570669f283b in main (/home/wuqianlong/a.out0x83b)#3 0x7f0efb63fc86 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.60x21c86)SUMMARY: AddressSanitizer: 30 byte(s) leaked in 1 allocation(s).案例
1.堆访问越界(heap-buffer-overflow)
#include stdlib.h
#include unistd.h
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 -fsanitizeaddress -fsanitizeleak
执行情况
3653ERROR: 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.60x2083f)
#2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapOOB0x400708)
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.20x98602)
#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.60x2083f)
2.栈访问越界(stack-buffer-overflow)
#include stdlib.h
#include unistd.h
int main(int argc, char **argv)
{int stack_array[100];stack_array[100] 0;//栈访问越界pause();return 0;
}
编译命令
gcc stackOOB.c -o stackOOB -g -fsanitizeaddress -fsanitizeleak执行结果
3952ERROR: 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.60x2083f)
#2 0x400748 in _start (/home/jetpack/work/4G/test/asan/stackOOB0x400748)
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)
3.使用已经释放的内存UseAfterFree
#include stdlib.h
#include unistd.h
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 -fsanitizeaddress -fsanitizeleak执行结果:
4385ERROR: 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.60x2083f)
#2 0x400708 in _start (/home/jetpack/work/4G/test/asan/heapUAF0x400708)
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.20x982ca)
#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.60x2083f)
previously allocated by thread T0 here:
#0 0x7f93f9198602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.20x98602)
#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.60x2083f)
4.内存泄漏 (Memory Leak)
#include string.h
#include stdlib.h
#include unistd.hint 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 -fsanitizeaddress -fsanitizeleak执行结果
3120ERROR: LeakSanitizer: detected memory leaksDirect leak of 400 byte(s) in 1 object(s) allocated from:
#0 0x7f412d5b7602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.20x98602)
#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.60x2083f)SUMMARY: AddressSanitizer: 400 byte(s) leaked in 1 allocation(s).5.函数返回的局部变量访问
int *ptr;
void usr_func(void)
{ //申请栈内存int local[100] {0};ptr local;
} int main(void)
{ //使用函数栈返回的内存*ptr 0;return 0;
}
6.全局变量访问越界
int global_array[100] {0}; int main(int argc, char **argv)
{//全局变量global_array访问越界 global_array[101];return 0;
}
感谢一起学习有疑问评论区讨论。