【内存】内存检测⼯具sanitizer[内存泄漏、内存越
界]VSvalgrind
简介
是⾕歌发起的开源⼯具集,包括了AddressSanitizer, MemorySanitizer, ThreadSanitizer, LeakSanitizer,Sanitizers项⽬本是LLVM 项⽬的⼀部分,但GNU也将该系列⼯具加⼊到了⾃家的GCC编译器中。GCC从开始⽀持Address和Thread Sanitizer,开始⽀持Leak Sanitizer和UB Sanitizer,这些都是查隐藏Bug的利器。
valgrind特点:
gnu编译器这个⼯具相对简单,对于⼀些复杂的内存泄露可能⽆法解析出来⽽且有⼀个问题就是只有检测的进程退出后才会输出内存泄露信息。
sanitizer特点:
Sanitizer可以在检测到内存泄露第⼀时间⽴刻终⽌进程,并且它可以深⼊检测(随应⽤进程⼀起编译)。
(AddressSanitizer的缩写)是⼀款⾯向C/C++语⾔的内存错误问题检查⼯具,可以检测如下内存问题:
使⽤已释放内存(野指针)
堆内存越界(读写)
栈内存越界(读写)
全局变量越界(读写)
函数返回局部变量
内存泄漏
⼯具⽐ Valgrind 更⾼效,主要由两部分组成:
编译器插桩模块(编译器instrumentation 模块)
运⾏时库(提供malloc()/free()替代项的运⾏时库)
gcc 4.8 开始,AddressSanitizer 成为 gcc 的⼀部分,但不⽀持符号信息,⽆法显⽰出问题的函数和⾏数。从 4.9 开始,gcc ⽀持AddressSanitizer 的所有功能。
安装
Ubuntu ⼀般不⽤安装,CentOS ⼀般需要安装。
如果使⽤ AddressSanitizer 时报错:
/usr/bin/ld: cannot find /usr/lib64/libasan.so.0.0.0
则需要先安装。Ubuntu 安装命令:
sudo apt-get install libasan0
CentOS 安装命令:
sudo yum install libasan
使⽤⽅法
内存越界检测
1,内存访问错误是模糊测试通常能够发现的⼀类错误,其主要是由于程序对不该进⾏读写操作的内存进⾏了操作,从⽽导致了应⽤程序的
崩溃。
2,实际上并不是所有的内存访问错误都能发⽣崩溃。
test.c来作为⼀个例⼦:
#include <stdio.h>
int main()
{
int a[2] = {1, 0};
int b = a[2];
return 0;
}
3,我们可以看出这是⼀个经典的 off-by-one-error错误((英语:Off-by-one error,缩写OBOE)是在计数时由于边界条件判断失误导致结果多了⼀或少了⼀的错误):
定义⼀个有两个元素的数组,访问该数组的元素时需从0开始,因此该数组的两个元素分别为a[0]、a[1]。然后将a[2]赋值给b,但实际上
a[2]不是数组的⼀部分,是个⽆效的数值。所以b最终就成了⼀个任意的数值,它只是从堆栈和内存中未被定义的部分中读取了“⼀些”内存。
4,然⽽和其他的内存访问错误所不同的是,它并不会造成崩溃,即使是强⼤的 valgrind 也⽆法告诉我们具体问题在哪⾥。⽽在最新版本的llvm和gcc中有⼀款⼯具却可以发现这种错误,它就是Address Sanitizer (ASan) ,它可以直接在编译的时候进⾏使⽤。
不过要注意的是,在使⽤前我们需要在编译器标志中增加⼀个参数-fsanitize=address。另外,为了使调试更为轻松,我们还需要增加 -ggdb.
zhangsan@PC:~$ gcc -fsanitize=address -ggdb -o test test.c
现在,我们再去运⾏之前的那个例⼦,我们会得到⼀个彩⾊的错误信息:
==7402==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fff2971ab88 at pc 0x400904 bp 0x7fff2971ab40 sp 0x7fff2971ab30READ of size #0 0x400903 in main /tmp/test.c:5
#1 0x7fd7e2601f9f in __libc_start_main (/lib64/libc.so.6+0x1ff9f)
#2 0x400778 (/tmp/a.out+0x400778)
Address 0x7fff2971ab88 is located in stack of thread T0 at offset 40 in frame
#0 0x400855 in main /tmp/test.c:1
This frame has 1 object(s):
[32, 40) 'a' <== Memory access at offset 40 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)
SUMMARY: AddressSanitizer: stack-buffer-overflow /tmp/test.c:3 main
5,我选择了其中最为有趣的⼀部分,相信这部分已经完全⾜够说明发⽣了什么——测试代码的第三⾏中由于⼀个⼤⼩为4的⽆效读取导致
出现了栈溢出(⼀个整数的⼤⼩)。
使⽤Address Sanitizer进⾏软件开发
1,我们在进⾏模糊测试时,通常对象不会是⼀个简单的C⽂件,因此我们必须要把address sanitizer添加到编译器的标志寄存器中。⽽软
件所使⽤的正常配置脚本则如下所⽰:
zhangsan@PC:~$ ./configure --disable-shared CFLAGS="-fsanitize=address -ggdb" CXXFLAGS="-fsanitize=address -ggdb"
zhangsan@PC:~$ make
2,为了获得更多的调试信息,我们需要再⼀次的添加-ggdb。如果可能的话我们还可以禁⽤共享库并设置C和C++编译器的标志。
3,然后,我们就可以像第⼀部分所说的那样对运⾏中的软件进⾏畸形输⼊了。⽽当我们将输出重定向到⽇志⽂件时,我们就不得不考虑到我们不能够对分段错误进⾏grep,⽽是需要我们去grep Address Sanitizer的消息:
grep AddressSanitizer fuzzing.log
在使⽤Address Sanitizer时,我们还必须要考虑到⼀些事:
1、ASan发现内存访问违规的时,应⽤程序并不会⾃动崩溃。这是由于在使⽤模糊测试⼯具时,它们通常都是通过检查返回码来检测这种错误。当然,我们也可以在模糊测试进⾏之前通过将环境变量 ASAN_OPTIONS修改成如下形式来迫使软件崩溃:
export ASAN_OPTIONS='abort_on_error=1'/
2、 ASan需要相当⼤的虚拟内存(⼤约20TB),不⽤担⼼,这个只是虚拟内存,你仍可以使⽤你的应⽤程序。但像 american fuzzy lop这样的模糊测试⼯具就会对模糊化的软件使⽤内存进⾏限制,不过你仍可以通过禁⽤内存限制来解决该问题。唯⼀需要注意的就是,这会带来⼀些风险:测试样本可能会导致应⽤程序分配⼤量的内存进⽽导致系统不稳定或者其他应⽤程序崩溃。因此在进⾏⼀些重要的模糊测试时,不要去尝试在同⼀个系统上禁⽤内存限制。
3、 Zzuf⽬前并不能和 ASan协同⼯作, 但你仍可以使⽤zzuf⼿动去创建⼀些测试样本将他们输⼊ASan的编译软件中。
4、还有就是,ASan会显著的延缓执⾏,其所发现的bug往往没有那么严重(当然也有例外),不过它能发现更多的bug是⽏庸置疑的。Address Sanitizer ⽤法
Address Sanitizer(ASan)是⼀个快速的内存错误检测⼯具。这⾥说明它的⽤法。
参考资料
1. 简介
Address Sanitizer(ASan)是⼀个快速的内存错误检测⼯具。它⾮常快,只拖慢程序两倍左右(⽐起Valgrind快多了)。它包括⼀个编译器instrumentation模块和⼀个提供malloc()/free()替代项的运⾏时库。
从gcc 4.8开始,AddressSanitizer成为gcc的⼀部分。当然,要获得更好的体验,最好使⽤4.9及以上版本,因为gcc 4.8的AddressSanitizer还不完善,最⼤的缺点是没有符号信息。
2. 使⽤步骤
⽤-fsanitize=address选项编译和链接你的程序。
⽤-fno-omit-frame-pointer编译,以得到更容易理解stack trace。
可选择-O1或者更⾼的优化级别编译
gcc -fsanitize=address -fno-omit-frame-pointer -O1 -g use-after-free.c -o use-after-free
运⾏use-after-fee。如果发现了错误,就会打印出类似下⾯的信息:
==9901==ERROR: AddressSanitizer: heap-use-after-free on address 0x60700000dfb5
at pc 0x45917b bp 0x7fff4490c700 sp 0x7fff4490c6f8
READ of size 1 at 0x60700000dfb5 thread T0
#0 0x45917a in main use-after-free.c:5
#1 0x7fce9f25e76c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226
#2 0x459074 in _start (a.out+0x459074)
0x60700000dfb5 is located 5 bytes inside of 80-byte region [0x60700000dfb0,0x60700000e000)
freed by thread T0 here:
#0 0x4441ee in __interceptor_free projects/compiler-rt/lib/asan/asan_:64
#1 0x45914a in main use-after-free.c:4
#2 0x7fce9f25e76c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226
previously allocated by thread T0 here:
#0 0x44436e in __interceptor_malloc projects/compiler-rt/lib/asan/asan_:74
#1 0x45913f in main use-after-free.c:3
#2 0x7fce9f25e76c in __libc_start_main /build/buildd/eglibc-2.15/csu/libc-start.c:226
SUMMARY: AddressSanitizer: heap-use-after-free use-after-free.c:5 main
第⼀部分(ERROR)指出错误类型是heap-use-after-free;
第⼆部分(READ), 指出线程名thread T0,操作为READ,发⽣的位置是use-after-free.c:5。
该heapk块之前已经在use-after-free.c:4被释放了;
该heap块是在use-fater-free.c:3分配
第三部分 (SUMMARY) 前⾯输出的概要说明。
3. 错误类型
3.1 (heap) use after free 释放后使⽤
下⾯的代码中,分配array数组并释放,然后返回它的⼀个元素。
5 int main (int argc, char** argv)
6 {
7 int* array = new int[100];
8 delete []array;
9 return array[1];
10 }
下⾯的错误信息与前⾯的“使⽤步骤”⼀节中的类似。
==3189==ERROR: AddressSanitizer: heap-use-after-free on address 0x61400000fe44
at pc 0x0000004008f1 bp 0x7ffc9b6e2630 sp 0x7ffc9b6e2620
READ of size 4 at 0x61400000fe44 thread T0
#0 0x4008f0 in main /home/ron/dev/as/use_after_free.cpp:9
#1 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#2 0x4007b8 in _start (/home/ron/dev/as/build/use_after_free+0x4007b8)
0x61400000fe44 is located 4 bytes inside of 400-byte region [0x61400000fe40,0x61400000ffd0)
freed by thread T0 here:
#0 0x7f3763ef1caa in operator delete[](void*) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x99caa)
#1 0x4008b5 in main /home/ron/dev/as/use_after_free.cpp:8
#2 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
previously allocated by thread T0 here:
#0 0x7f3763ef16b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2) #1 0x40089e in main /home/ron/dev/as/use_after_free.cpp:7
#2 0x7f3763aa882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
SUMMARY: AddressSanitizer: heap-use-after-free /home/ron/dev/as/use_after_free.cpp:9 main
3.2 heap buffer overflow 堆缓存访问溢出
如下代码中,访问的位置超出堆上数组array的边界。
2 int main (int argc, char** argv)
3 {
4 int* array = new int[100];
5 int res = array[100];
6 delete [] array;
7 return res;
8 }
下⾯的错误信息指出:
错误类型是heap-buffer-overflow
不合法操作READ发⽣在线程T0, heap_buf_overflow.cpp:5
heap块分配发⽣在heap_buf_overflow.cpp
==3322==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x61400000ffd0
at pc 0x0000004008e0 bp 0x7ffeddce53a0 sp 0x7ffeddce5390
READ of size 4 at 0x61400000ffd0 thread T0
#0 0x4008df in main /home/ron/dev/as/heap_buf_overflow.cpp:5
#1 0x7f3b83d0882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#2 0x4007b8 in _start (/home/ron/dev/as/build/heap_buf_overflow+0x4007b8)
0x61400000ffd0 is located 0 bytes to the right of 400-byte region [0x61400000fe40,0x61400000ffd0) allocated by thread T0 here:
#0 0x7f3b841516b2 in operator new[](unsigned long) (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x996b2) #1 0x40089e in main /home/ron/dev/as/heap_buf_overflow.cpp:4
#2 0x7f3b83d0882f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
SUMMARY: AddressSanitizer: heap-buffer-overflow /home/ron/dev/as/heap_buf_overflow.cpp:5 main 3.2 stack buffer overflow 栈缓存访问溢出
如下代码中,访问的位置超出栈上数组array的边界。
2 int main (int argc, char** argv)
3 {
4 int array[100];
5 return array[100];
6 }
下⾯的错误信息指出:
错误类型是stack-buffer-overflow
不合法操作READ发⽣在线程T0, stack_buf_overflow.cpp:5
栈块在线程T0的栈上432偏移位置上。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论