CSI-Fuzz: Full-speed Edge Tracing Using Coverage Sensitive Instrumentation
文章来自 TDSC2020
作者来自斯威本科技大学
一、论文阅读
1. Introduction
首先UnTracer指出AFL中的问题:AFL通过当前覆盖位图与全局位图决定当前测试用例是否有新的覆盖。然而绝大部分测试用例是不会产生新覆盖的,所以如果能跳过对这些测试用例的追踪将会节省大量开销。但是UnTracer是基于块插桩的,会带来边冲突,从而降低fuzz的有效性。此外,UnTracer不追踪无新覆盖的测试用例,所以fuzzer也就缺失了路径频率信息。
一种解决方案是基于边覆盖,但是静态分析检测不到间接控制流,即间接调用中的很多边检测不到。
提出了CSI-Fuzz,用Dyninst首先插桩确定边(包括条件满足转移和条件不满足转移),其次插桩间接边的源块(包括switch、call)在运行时区分这些边。
CSI-Fuzz还通过计算路径标记的哈希值设置路径标识符。
贡献:
- 分析了现有全速Fuzzer的问题:存在边冲突,不能计算路径频率,影响fuzz有效性。
- 设计了CSI-Fuzz,在确定边和间接边插桩,只追踪有新覆盖的测试用例。对于间接边的难题,在源块插桩。
- 基于AFL实现了CSI-Fuzz,实验结果表明在发现边、执行速度和发现bug方面高效。
- 开源 https://github.com/ Vul4Vendetta/csi-afl
2. Overview of CSI-Fuzz
2.1 Background of Fuzzing
UnTracer的处理方式
在每个基本块的开头都插入断点,覆盖后移除。但是UnTracer忽略了一些新的边,比如走过 A->B->D 和 A->C->E 这两条路径后,基本块中的断点就会被移除,当执行 A->B->E 时,就检查不到新的边BE。
2.2 Edge Instrumentation
CSI-Fuzz插桩边,并且只有在覆盖到新的边时才追踪路径。
2.3 Full-speed edge tracing
oracle:中断新的边
tracer:更新边覆盖信息
2.3.1 oracle
在每条边插入了一个 exit()
的调用,执行到新的边时就会退出,这也导致了无法知道测试用例的完整执行路径,所以会用tracer追踪完整的执行路径,并且移除oracle中对应边的桩代码(确定边),或是记录在新边上的块(间接边)
2.3.2 tracer
跟踪完整路径并更新覆盖信息。当一些bug是在发现新边后才能触发的则只有tracer能检测到,oracle检测不到。
2.3.3 crasher
检测崩溃。类似oracle,当触发崩溃并且执行新边时会调用exit()
。在tracer或是oracle中触发的崩溃会在crasher中检验,如果被crasher终止执行则是新的崩溃,如果触发崩溃则是已经识别过的崩溃。
2.4 path Identifier
如图,当执行 A->B->C->D 时,由于AB是新边,会在其上标记m1。当执行A->B->F->H 时,由于BF是新边,会在其上标记m2。当再次执行 A->B->F->H 时则利用 m1和m2计算路径标识符。
3. Implementation of CSI-Fuzz
插入 exit(N)
,以一个特殊的状态码(N=66 表示确定边,N=67表示间接边)结束,以区别系统状态码。
- AFL初始化
- 获取所有的边
- 插桩Oracle和Tracer
- 进入循环
- 生成测试用例
- 如果覆盖新边
- 将覆盖到的第一条新边标记
- 测试用例添加到队列中
- 调用Tracer跟踪所有的边
- 如果执行了循环,则为种子分配更多能量
- 利用Tracer的信息移除Oracle中的桩代码
- 计算路径标识符
- 更新间接边
- 重启 fork server
3.1 Edge coverage
0:程序正常退出状态码为
1:异常退出状态码为
66,67:自定义退出状态码
- 为每条确定边分配标识符,Tracer并将覆盖的边的标识符存在共享内存中,Oracle能够从共享内存中快速读取标识符并移除桩代码。
- 对于间接边,在 call 和 jump 指令处插桩,桩代码内容为:获取所有的历史目标快集合,如果当前目标块不在集合中,则是新的边。将当前目标快加入到集合中。间接边的源块和目标块的地址都记录在文件中并在启用forks erver之前读到内存中,所以可以根据源块查找到目标块的记录。
3.2 Removal scheme
移除操作的实现:将桩代码的第一条指令修改为无条件跳转指令,跳转至桩代码的最后,所以桩代码不会执行。其实是伪删除,还是占空间的,只是不执行了。如下图所示(为什么不让第一条指令直接为 jmp B 呢,这样不是少一次跳转?有无大佬解答一下!)。
作者最开始的解决方案是重插桩,但是开销太大。
3.3 Path identifier
(一直没看懂这是个啥东西,但感觉就是个很简单东西,作者有意说得很复杂很高大上)
4. Evaluation of binaries
与AFL、AFLFast、UnTracer 从边发现,执行速度,bug发现三个方面对比,
4.1 Evaluation on QEMU mode
CSI-FUZZ发现了更多的边,但是边的统计方式是作者自己实现的。还发现条件满足转移的边少于条件不满足转移的边,这也说明了路径约束问题。
4.2 Evaluation on Dyninst mode
比较执行速度
4.3 Bug discovery
这部分的实验效果一般
6. Discussion and future work
可以使用其他插桩工具,当前的路径标识符方案导致可能存在路径碰撞。
二、源码分析
有时间读读源码后再更新。