Abstract
fuzzing技术被广泛运用于漏洞挖掘中,研究者也开发了不同的方法,能够让 fuzzing 并行运行,从而提高挖掘的效率。但是先前出现的项目都存在一些问题,降低了多线程挖掘的效率:
- 对于状态的同步,需要暂停 IO,浪费 CPU 算力
- 同步太慢时,fuzzing 线程无法及时得到更新,不能达到最优效果
- 同步太快时,同步的线程占用过多 CPU 资源,开销太大。
本论文提出了一种新的方法μfuzz,并且给出了实现代码,通过微服务的方式减少上述问题对多余资源的开销,从而加快 fuzzing 的速度。
1 Introduction
总结了现有技术中分布式 fuzzing的解决方案,列举了上述提到的几个问题,引出了本文项目的微架构方案,写出了这个项目在测试项目中和其他项目的分数差别。
2 Problem
2.1 How Existing Parallel Fuzzing Works
主要讲解了现有主流的分布式 fuzzing 技术的架构和方案,如图 Fig.1,在一个实例中维护状态,这会导致状态同步的不及时性,和在进行同步时对 CPU 的浪费。
2.2 Limitation of Existing Approaches
讲解了现有分布式解决方案的问题:
- 因为 IO 瓶颈导致的 CPU 浪费。(Table 1,2)
- 测试程序带来的 IO 浪费(系统调用,压缩,etc.)
- 状态同步带来的 IO 浪费
- 测试状态没有被实时更新。 (Fig.2)
- 一个实例可能使用过时的策略和方法进行 fuzz,这个策略可能是当前实例最优,但不是全局最优,因为状态更新是定时完成的。
- 但是同样,也不能对一个实例过于频繁地进行状态更新。
2.3 Microservice Architecture
微服务架构的好处:
- 多个松散的但是可组织的服务,并行运行。我们可以将一个 instance 的多个状态分装为不同的服务。
- 每个服务都不依赖于其他的服务,独立运行。
- 每个服务的能力都可以通过调整并行线程的数量来调整。
2.4 Our Approach
笔者设计了一种比较不错的微服务架构来完成 fuzzing 的过程。
- 重新设计的微服务架构:把传统 fuzzing 中的 state 拆分得到几个不同的微服务
- 重新设计的状态:拆分后,我们将状态也进行拆分:
我们要求:
- 状态是功能完整的
- 本地最优->接近全局最优的聚合。
3 Design
Fig.3是μfuzz 的整体框架。
接下来我们会在3.1中介绍从 instance 到几个不同的 Microservice 的方法,3.2中讲解用来使不同 service 之间协作的输出缓存机制,3.3讲解负载均衡的方法,3.4中讲解状态设计,3.5中讲解zero-copy communication 方法。
3.1 From Monolith to Microservice
每个 service 的基础架构如图 Fig.4:一个 service 中有 Input Dispatcher,几个不同 Worker 和剩余的 Output Caching Queue,同时它维护一个 state。我们按照如下方法对传统的 fuzzing 进行拆分:
- 每个服务只关注 fuzzing 的一个部分
- 每个服务不需要其他服务的 state 来完成。
- Corpus Management Service
语料管理服务用来维护一组有趣的测试用例和它们的元数据,如性能分散。它通过一个优先级算法来选择最优的语料进入下一步生成。
- Text Case Generation Service
测试样例生成服务使用从上一步输入的“有趣”的语料,通过变异等策略来生成新的测试样例。
- Execution Service
运行服务将上一步生成的测试样例输入程序运行。
- Feedback Collection Service
从运行服务中收集运行结果,对这些结果进行分类,挑选出“有趣”的结果发送回语料管理服务中。
这就是这个系统的工作流程。每个设备都是单独运行,不受影响的。
3.2 Concurrency by Output Caching
每个 service 都是有输入和输出的,我们通过输出缓存来解耦输出,从而实现程序的连续运行。
如 Fig.4 所示,一个 worker 完成了运行之后将结果发送到 Output Caching Queue,而不是下一个 service 的输入。当下一个 service 准备好了之后,这个 queue 才会将内容发送。
拥塞控制:但是如果 output queue 中存入了太多的内容,它会占用大量 CPU 资源,让下一个进程无法清理输入。所以需要对 OutputCache 进行限制,从而可以使内容生成和输入消耗达到平衡。
3.3 Parallelism by Load Balancing
每个 service 的 worker 数量都设置为CPU 核心数量,然后维护一个空闲核心的队列,当一个空闲核心进入队列且任务队列中有任务时,给空闲核心分配任务。
3.4 Avoid Synchronziation by State Partition
接下来设计状态来避免同步。
μfuzz 进行两级状态分级:第一级中,μfuzz 将状态分配给它的服务,以便服务能独立工作。第二级中,每个服务将状态分配给不同的 workers。
State Partition:我们对状态进行分区,固定大小的状态进行静态分区,可变大小的进行动态分区。算法如Alg.1 所示。每个 worker 负责一部分状态的内容。
Result Accumulation:对每个 worker,我们需要把结果整合。(在本文中是 Output Cache Queue 做的这一步)
State Updates:状态由 service 中的worker更新。
3.5 Zero-Copy Communication
复制的参数会造成很多不必要的 IO 操作,可以通过共享的指针来传递参数。
但是这样操作可能出现一些安全隐患,对指针添加所有权限制来阻止不安全的内存访问。
4 Implementation
μfuzz 由 rust 实现,共用了 9534 行代码,见 Table 5
- 使用 tokio 来实现多线程
- 将所有的 service 运行在一个进程中来共用内存。
5 Evaluation
用了一些测试方法对比了其他的几个现有的 Fuzzer,且测试中发现了新的漏洞。
6 Discussion
本章讲解了 μfuzz 可以改进的点。
6.1 Distributed Fuzzing
可以实现 RPC 调用来完成远程调用,从而实现分布式的 fuzz。给出了网络成为 IO 瓶颈的解决方法:Prefetching。
6.2 Support More Mutation Strategy
应用更多不同的进化方法。
6.3 Support Collaborative Fuzzing
实现多种 fuzzing 服务的结合。
7 Related Work
讲解了一些相关的工作,比如 fuzzing strategy 的改进,加速 fuzzing ,和多线程 fuzzing。