每日论文合辑
(2025.06.12)【ASPLOS’25】 ClosureX: Compiler Support for Correct Persistent Fuzzing
因为对于该领域不熟悉,我还是召唤了翻译来理解每个词的本意:Fuzzing 是一种被广泛采用且务实的漏洞挖掘方法,用于加强软件的健壮性。研究表明,提高 fuzzing 的吞吐量能直接提升漏洞发现率。性能最高的 fuzzing 策略是「持久化 fuzzing」(persistent fuzzing),它通过在完成一次测试后循环回到入口,而不是退出进程,从而复用同一进程处理所有测试用例。这样就消除了与执行成本相当的进程创建、初始化和销毁开销。不幸的是,持久化 fuzzing 会导致语义上不一致的程序状态:一个测试用例对进程状态的修改会保留到后续测试中。这种语义不一致会带来漏报崩溃、误报崩溃以及整体错误,进而削弱模糊测试器的有效性。我们观察到,现有的 fuzzing 执行机制在“每次测试用例之间丢弃和恢复状态量”上形成一条连续谱。我们提出了 ClosureX,一种在这条状态恢复谱上位于新位置的执行机制:它仅重置与当前测试用例执行相关的状态。这样就实现了接近持久化的性能,同时保留了重量级状态恢复的正确性。我们将 ClosureX 实现为一组 LLVM Pass,并与 AFL++ 集成。在对十个流行的开源 fuzzing 目标的评估中,ClosureX 在保持语义正确性的同时,平均将测试用例执行速度提升了 3.5 倍以上(相比于 AFL++)。ClosureX 也比 AFL++ 更稳定、更快地发现漏洞,速度快 1.9 倍,且发现了 15 个 0-day 漏洞(4 个已记为 CVE)。
本文中 Fuzz 的定义:
Fuzzing is the process of generating test cases based on a set of seed test cases and running the test case against a program to find bugs.
背景、动机、贡献
- Fuzzing 着重需要关注的是生成未见过的用例,称为
coverage-guided fuzzing
- 这篇论文关注的是如何减小 Fuzz 的开销(复用进程),而同行有两个极端
- ①每个 Fuzz 都单开一个进程
- ②所有 Fuzz 都使用一个进程(循环到开始)
- 本文核心是识别程序运行时的关键信息(例如变量修改),然后当可以复用时恢复他们然后执行下个例子
- 作者对 Persistent Fuzzing 的效率问题起到了贡献
补充信息:==在 fuzzing 领域,“怎么在性能和正确性中间平衡”一直是个难题。==很多工作要么牺牲正确性,要么牺牲性能,能在二者之间找到新切入点,就很有价值。
- Fuzz 的四个步骤:
- 程序插桩
- 测试用例生成
- 程序运行与监控
- 反馈分析与修复
- Fuzz 的两种层次:
- 高级语言:因为有源码,所以插桩效率和效果都好
- 二进制:基本块级别
- Fuzz 程序生成有两种:
- generational
- mutational
作者在背景结束时说了:**ClosureX is a coverage-guided, mutational, source-available fuzzer.**这个地方的定义下的不错。
- 作者在进程管理上做到了跨架构
作者的动机在于(首先对比了几种 Fuzz 策略)对比了其他 Fuzz 的不足,已经讲明白了人家的缺点,比如依赖 os 导致无法跨架构,以及持续 fuzz 条件下进程的问题。主要导致:Semantically inconsistent
所以作者在实现时又回到了那个核心问题:让程序自己记录状态,并在相邻测例之间保持正确性。
- 要在 main 函数里着重对异常退出的程序恢复其栈信息、同时清空堆 (alloc) 。