1 前言
在使用Vuzzer重现其论文中的实验时,对mpg321的fuzzing发现三个crash,其中一个为segementation fault.下文为对这个segmentation fault的分析过程。
2 环境准备
mpg321编译:
mpg321_0.3.2.orig.tar.gz (-g -O0编译)
依赖库编译安装:
- libao-1.2.0(-g -O0编译)
- libmad-0.15.1b(-g -O0编译)
- libid3tag-0.15.1b (-g -O0编译)
- libasound2-dev (apt-get install)
ubuntu 14.04 X64
3 调试过程
A coredump调试
首先配置core dump 记录 crash的 coredump 信息。
$: ulimit -c unlimited (不限制core文件大小)
使用crash input重新运行程序,得到coredump 信息:
使用gdb调试 coredump,得到崩溃时的调用栈:
从崩溃的调用栈处,看到的信息是调用calloc分配84字节内存的时候导致了segmentataion fault。从调用栈追踪,看不出什么问题。
在网上搜索了关于calloc导致segmentaion fault,大部分都是由于在程序的其他地方堆被破坏,然后在后续分配内存的时候导致出错。推荐的heap check 工具为 gperftools http://https://github.com/gperftools/gperftools。
gperftools配置安装后使用没有什么发现(安装使用可能还有问题,没有继续研究),改用 LLVM的 AddressSanitizer(安装LLVM...)
LLVM 的安装源:http://apt.llvm.org/
B 堆溢出调试
在使用LLVM ASAN编译mpg321时,发现了一件奇怪的事情,使用-O0 -O1优化,clang都无法识别libmad库中的一个函数,而使用-O2却可以。。。
编译好之后,运行crash input结果如下:
这里显示的结果是:
在 0x530602处 向内存 0x602...398 开始处写入8字节
但是 0x602....398处于分配的内存区域[...390 ,...398)偏移0个字节的地方
意思就是分配了8字节的内存,但是在写的时候,写到第9字节去了。
这个输出并不是很友好,看不出在哪里分配,哪里写入的,继续查看AddressSanitizer的使用说明,发现通过导出 ASAN_SYMBOLIZER_PATH环境变量后,可以看到更具体的符号信息。
导出环境变量后得到的结果如下:
这样我们得到分配内存的代码和写入内存代码的位置:
分配内存:
写入内存:
通过查看源码上下文,猜测在分配时,分配了8个字节的内存,在使用时,访问到第九个字节。
使用gdb调试下断点,在mad.c:289行处,变量current_frame为1,而playbuf->frames存储的元素为指针,在X64系统中,指针大小为8字节,current_frame为1,访问到的是第二个指针,即playbuf->frames开始处第九个字节。如果分配了8字节内存,这里的访问就越界了。
再使用gdb调试,在分配内存处下断点,得到malloc的参数为8,即分配了8字节内存。验证了上面的猜想。
从源码看,如果这里分配了8字节内存,那是不是playbuf.num_frames值为0,然后malloc((playbuf.num_frames + 1) * sizeof(void*))分配8字节内存? 但是 在写入内存时,判断了 current_frame
继续调试,看看playbuf.num_frames值到底是是什么。
这里,使用gdb 指令单步模式,并打开汇编代码显示:
发现 playbuf.num_frames 的值为0x8000000000000000
lea 0x8(,%rax,8) %rdi 这里 %rax即为 playbuf.num_frames
也就是(playbuf.num_frames + 1)*8 在汇编层面,是(playbuf.num_frames*8 + 8) 而playbuf.num_frames为 unsigned long 类型,playbuf.num_frames*8 之后溢出为0,最后导致malloc分配了8字节内存。
但是在写入内存处,current_frame也是 unsigned long类型,current_frame