rohanpadhye/JQF
GitHub: rohanpadhye/JQF
JQF 是一个基于覆盖率引导的 Java 模糊测试框架,通过 JUnit 参数化测试接口自动生成结构化输入来发现深层语义缺陷。
Stars: 738 | Forks: 117
# JQF + Zest:Java 的语义模糊测试
[](https://github.com/rohanpadhye/JQF/actions/workflows/ci.yml)
JQF 是一个用于 Java 的反馈导向模糊测试平台(可以想象成:AFL/LibFuzzer,但用于 JVM 字节码)。JQF 使用*属性测试*的抽象,这使得将模糊测试驱动编写为参数化的 JUnit 测试方法变得非常方便。模糊测试在 **JUnit 4** 或 **JUnit 5** 下运行,测试参数由可插拔的生成器提供者(默认为 [junit-quickcheck](https://github.com/pholser/junit-quickcheck))生成;引擎本身不依赖于两者中的任何一个。JQF 能够利用**覆盖率引导**的模糊测试算法(例如 **Zest**)的强大功能来运行这些参数化的单元测试。
[Zest][ISSTA'19 paper] 是一种算法,它将覆盖率引导的模糊测试偏向于生成*语义有效*的输入;也就是说,在最大化代码覆盖率的同时,满足结构和语义属性的输入。Zest 的目标是发现常规模糊测试工具无法发现的深层语义漏洞,常规工具通常只对错误处理逻辑施加压力。默认情况下,JQF 通过简单的命令 `mvn jqf:fuzz` 运行 Zest。
JQF 是一个模块化的框架,支持以下称为*引导*的可插拔模糊测试前端:
* 使用 [AFL](http://lcamtuf.coredump.cx/afl) 进行二进制模糊测试([教程](https://github.com/rohanpadhye/jqf/wiki/Fuzzing-with-AFL))
* 使用 **[Zest](http://arxiv.org/abs/1812.00078)** 进行语义模糊测试 [[ISSTA'19 paper]]([教程 1](https://github.com/rohanpadhye/jqf/wiki/Fuzzing-with-Zest))([教程 2](https://github.com/rohanpadhye/jqf/wiki/Fuzzing-a-Compiler))
* 使用 **[PerfFuzz](https://github.com/carolemieux/perffuzz)** 进行复杂度模糊测试 [[ISSTA'18 paper]]
* 使用 **[RLCheck](https://github.com/sameerreddy13/rlcheck)** 进行强化学习(基于 JQF 的一个分支) [[ICSE'20 paper]]
* 使用 **[Mu2](https://github.com/cmu-pasta/mu2)** 进行变异分析引导的模糊测试 [[ISSTA'23 paper]]
JQF 在 [广泛使用的开源软件中发现了一系列漏洞](#trophies),如 OpenJDK、Apache Maven 和 Google Closure Compiler。
### Zest 研究论文
如果您的研究中引用 Zest,我们请求您引用我们的 [ISSTA'19 paper]:
#### JQF 工具论文
如果您使用 JQF 框架构建新的模糊测试器,我们请求您按如下方式引用我们的 [ISSTA'19 tool paper]:
## 概述
### 什么是*结构感知模糊测试*?
像 [AFL](http://lcamtuf.coredump.cx/afl) 和 [libFuzzer](https://llvm.org/docs/LibFuzzer.html) 这样的二进制模糊测试工具将输入视为字节序列。如果测试程序期望高度结构化的输入(例如 XML 文档或 JavaScript 程序),那么改变字节数组通常会导致语法无效的输入;测试程序的核心部分仍未被测试。
**结构感知模糊测试**工具利用输入格式的特定领域知识,从构造上生成*语法有效*的输入。有一些关于使用 libFuzzer 对 [C++](https://github.com/google/fuzzing/blob/master/docs/structure-aware-fuzzing.md) 和 [Rust](https://rust-fuzz.github.io/book/cargo-fuzz/structure-aware-fuzzing.html) 程序进行结构感知模糊测试的好文章。
### 什么是*基于生成器*的模糊测试?
结构感知模糊测试工具需要一种方法来理解输入结构。其他一些工具使用输入格式的声明式规范,例如[上下文无关文法](https://embed.cs.utah.edu/csmith/)或 [Protocol Buffers](https://github.com/google/libprotobuf-mutator)。**JQF** 使用 QuickCheck 的命令式方法来指定输入空间:任意的***生成器***程序,其工作是生成单个随机输入。
`Generator` 提供了一种产生 `T` 类型随机实例的方法。例如,`Calendar` 类型的生成器返回随机生成的 `Calendar` 对象。人们可以轻松地为更复杂的类型编写生成器,例如
[XML 文档](examples/src/main/java/edu/berkeley/cs/jqf/examples/xml/XmlDocumentGenerator.java),
[JavaScript 程序](examples/src/main/java/edu/berkeley/cs/jqf/examples/js/JavaScriptCodeGenerator.java),
[JVM 类文件](examples/src/main/java/edu/berkeley/cs/jqf/examples/bcel/JavaClassGenerator.java)、SQL 查询、HTTP 请求,以及[更多](https://github.com/pholser/junit-quickcheck/tree/master/examples/src/test/java/com/pholser/junit/quickcheck/examples)——这就是**基于生成器的模糊测试**。然而,简单地随机采样类型 `T` 的输入通常不是很有效,因为生成器不知道它产生的输入是否好用。
生成器层是可插拔的:[junit-quickcheck](https://github.com/pholser/junit-quickcheck) 是默认提供者,其他提供者如 [Instancio](https://www.instancio.org/) 和 [jetCheck](https://github.com/JetBrains/jetCheck) 通过 `ArgumentsGeneratorFactory` SPI 插入,无需更改引擎。
### 什么是*语义模糊测试* (Zest)?
JQF 支持 **[*Zest 算法*][ISSTA'19 paper],该算法使用代码覆盖率和输入有效性反馈来引导 QuickCheck 风格的生成器**,以生成能够揭示深层语义漏洞的结构化输入。JQF 使用字节码插桩提取代码覆盖率,并使用 JUnit 的假设 API(JUnit 4 上的 `org.junit.Assume`,JUnit 5 上的 `org.junit.jupiter.api.Assumptions`)提取输入有效性。如果没有违反假设,则输入是有效的。
## 示例
这是一个 JUnit-Quickcheck 测试,用于检查来自 [Apache Commons Collections](https://commons.apache.org/proper/commons-collections/) 的 [PatriciaTrie](https://commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/trie/PatriciaTrie.html) 类的属性。该属性测试:如果使用输入的 JDK `Map` 初始化 `PatriciaTrie`,并且如果输入 map 已经包含一个 key,那么该 key 也应该存在于新构建的 `PatriciaTrie` 中。
```
@RunWith(JQF.class)
public class PatriciaTrieTest {
@Fuzz /* The args to this method will be generated automatically by JQF */
public void testMap2Trie(Map map, String key) {
// Key should exist in map
assumeTrue(map.containsKey(key)); // the test is invalid if this predicate is not true
// Create new trie with input `map`
Trie trie = new PatriciaTrie(map);
// The key should exist in the trie as well
assertTrue(trie.containsKey(key)); // fails when map = {"x": 1, "x\0": 2} and key = "x"
}
}
```
运行 `mvn jqf:fuzz` 会导致 JQF 使用自动生成的 `map` 和 `key` 值反复调用 `testMap2Trie()` 方法。平均大约 5 秒后(约 5,000 个输入),JQF 将报告断言冲突。它发现了一个[在 `PatriciaTrie` 实现中的漏洞](https://issues.apache.org/jira/browse/COLLECTIONS-714),该漏洞在 v4.4 版本中仍未解决。对 `map` 和 `key` 值进行随机采样不太可能找到失败的测试用例,这是一个非常特殊的极端情况(参见上面代码中断言旁边的注释)。JQF 使用一种称为 [**Zest**][ISSTA'19 paper] 的覆盖率引导轻易地发现了这种冲突。要将此示例作为独立的 Maven 项目运行,请查看 [jqf-zest-example 存储库](https://github.com/rohanpadhye/jqf-zest-example)。
在上面的示例中,`Map` 和 `String` 的生成器是由 JUnitQuickCheck 自动合成的。也可以手动指定结构化输入的生成器。请参阅下面的[教程](#tutorials)。
### 在 JUnit 5 下运行
同一个驱动程序可以作为 JUnit 5 测试运行:将 `@RunWith(JQF.class)` 和 `@Fuzz` 替换为单个 `@FuzzTest`。
```
class PatriciaTrieTest {
@FuzzTest
void testMap2Trie(Map map, String key) {
assumeTrue(map.containsKey(key));
Trie trie = new PatriciaTrie(map);
assertTrue(trie.containsKey(key));
}
}
```
使用 `-Djqf.fuzz=true`(或 `mvn jqf:fuzz`),该方法会运行一次完整的 Zest 活动;普通的 `mvn test` 会将保存的语料库和种子输入作为有界回归重放。将 `jqf-generator-quickcheck` 等生成器提供者添加到测试 classpath 中,以便使用上面提到的 junit-quickcheck 生成器。
## 文档
* [JQF Maven 插件](https://github.com/rohanpadhye/JQF/wiki/JQF-Maven-Plugin)文档展示了如何运行 `mvn jqf:fuzz` 和 `mvn jqf:repro`。
* [编写 JQF 测试](https://github.com/rohanpadhye/JQF/wiki/Writing-a-JQF-test)演示了如何为 JQF 创建基于 JUnit 的参数化测试方法。
* [Guidance 接口](https://github.com/rohanpadhye/jqf/wiki/The-Guidance-interface)文档展示了 JQF 的内部工作原理,这对于希望基于 JQF 构建自定义引导算法的研究人员很有用。
* [API 文档](https://rohanpadhye.github.io/JQF/apidocs)会在每次主要版本发布时发布,这对于希望扩展 JQF 的研究人员同样很有用。
### 教程
* [Zest 101](https://github.com/rohanpadhye/jqf/wiki/Fuzzing-with-Zest):一个使用命令行脚本对独立的小型程序进行模糊测试的基础教程。介绍了为 `Calendar` 对象编写测试驱动程序和结构化输入生成器的过程。
* [使用 Zest 对编译器进行模糊测试](https://github.com/rohanpadhye/jqf/wiki/Fuzzing-a-Compiler):使用 JavaScript 程序的生成器,对一个复杂程序——[Google Closure Compiler](https://github.com/google/closure-compiler)——进行模糊测试的教程。本教程使用了 [JQF Maven 插件](https://github.com/rohanpadhye/jqf/wiki/JQF-Maven-Plugin)。
* [使用 AFL 进行模糊测试](https://github.com/rohanpadhye/jqf/wiki/Fuzzing-with-AFL):使用 AFL 二进制模糊测试引擎,对解析二进制数据(例如 PNG 图像文件)的 Java 程序进行模糊测试的教程。
* [使用 ZestCLI 进行模糊测试](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/java-fuzzing-example):使用 ZestCLI 对 Java 程序进行模糊测试的教程。
### 持续模糊测试
[GitLab](https://docs.gitlab.com/ee/user/application_security/coverage_fuzzing/) 支持在 CI/CD 中运行 JQF([教程](https://gitlab.com/gitlab-org/security-products/demos/coverage-fuzzing/java-fuzzing-example)),尽管他们最近为此目的推出了自己的自定义 Java 模糊测试器。
## 基于 JQF 的研究和工具
* **[Zest](https://github.com/rohanpadhye/jqf-zest-example)** 🍝 [[ISSTA'19 paper]] - 语义模糊测试
* **[BigFuzz](https://github.com/UCLA-SEAL/BigFuzz)** 🍝 [[ASE'20 paper]] - Spark 模糊测试
* **[MoFuzz](https://github.com/hub-se/MoFuzz)** [[ASE'20 paper](https://doi.org/10.1145/3324884.3416668)] - 模型驱动软件
* **[RLCheck](https://github.com/sameerreddy13/rlcheck)** 🍝 [[ICSE'20 paper]] - 强化学习
* **[Bonsai](https://github.com/vasumv/bonsai-fuzzing)** 🍝 [[ICSE'21 paper]] - 简洁测试生成
* **[Confetti](https://github.com/neu-se/CONFETTI)** [[ICSE'22 paper](https://doi.org/10.1145/3510003.3510628)] - 带有全局提示的 Concolic / 污点追踪
* **[BeDivFuzz](https://github.com/hub-se/BeDivFuzz)** [[ICSE'22 paper](https://doi.org/10.1145/3510003.3510182)]- 行为多样性
* **[ODDFuzz](https://github.com/ODDFuzz/ODDFuzz)** [[IEEE S&P'23 paper](https://arxiv.org/pdf/2304.04233.pdf)] - 反序列化漏洞
* **[GCMiner](https://github.com/GCMiner/GCMiner)** [[ICSE'23 paper](https://arxiv.org/pdf/2303.07593.pdf)] - 反序列化漏洞
* **[Intender](https://github.com/purseclab/intender)** [[USENIX Security'23 paper](https://www.usenix.org/system/files/sec23fall-prepub-285_kim-jiwon.pdf)] - 基于意图的网络
* **[Mu2](https://github.com/cmu-pasta/mu2)** 🍝 [[ISSTA'23 paper]] - 变异测试作为引导
* **[TOAST](http://dx.doi.org/10.1007/s11390-021-1693-1)** [[JCST'22 paper](https://link.springer.com/article/10.1007/s11390-021-1693-1)] - 测试动态软件更新
* **[Poracle](https://github.com/PLaSE-UNIST/poracle-tool)** [[ACM TOSEM'23 paper](http://www.jooyongyi.com/papers/TOSEM23.pdf)] - 使用差分模糊测试进行补丁测试
* **[SPIDER](https://arxiv.org/abs/2209.04026)** 🍝 [[arxiv preprint](https://arxiv.org/abs/2209.04026)] - SDN 中的有状态性能问题
* **[FuzzDiff](https://github.com/akashpatil7/FuzzDiff)** [[Dissertation](https://www.scss.tcd.ie/publications/theses/diss/2022/TCD-SCSS-DISSERTATION-2022-134.pdf)] - 动态程序等价性检查
* **[JDD](https://github.com/fdu-sec/JDD)** [[IEEE S&P'24 paper](https://ieeexplore.ieee.org/document/10646692)] - 反序列化漏洞
* **[DiPri](https://github.com/QRXqrx/dipri-artifacts)** [[ACM TOSEM'24 paper](https://dl.acm.org/doi/pdf/10.1145/3654440)] - 基于距离的种子优先级排序
* **[DCAFixer](https://github.com/aprdbapp/DCAFixer)** [[IEEE TDSC'25 paper](https://ieeexplore.ieee.org/abstract/document/10525227)] - 数据库客户端应用程序测试
🍝 = 涉及至少一位 JQF 的原作者。
## 联系开发者
如果您在 JQF 中发现了一个漏洞,或者在运行 JQF 时遇到问题,请在[问题追踪器](https://github.com/rohanpadhye/jqf/issues)上创建一个 issue。您也可以使用此平台发布功能请求。
如果遇到某种紧急的模糊测试问题,您可以随时发送电子邮件给主要开发者:[Rohan Padhye](https://rohan.padhye.org)。
## 战利品
如果您使用 JQF 发现了漏洞,并且愿意分享,我们将很乐意将它们添加到此列表中。
请针对 README.md 发送一个 PR,并附上您发现的漏洞/cve 链接。
- [google/closure-compiler#2842](https://github.com/google/closure-compiler/issues/2842):VarCheck 中的 IllegalStateException:意外的变量
- [google/closure-compiler#2843](https://github.com/google/closure-compiler/issues/2843):在死代码中使用箭头函数时出现 NullPointerException
- [google/closure-compiler#3173](https://github.com/google/closure-compiler/issues/3173):在模糊输入上出现的算法复杂度/性能问题
- [google/closure-#3220](https://github.com/google/closure-compiler/issues/3220):ExpressionDecomposer 抛出 IllegalStateException:无法分解 Object 方法调用
- [JDK-8190332](https://bugs.openjdk.java.net/browse/JDK-8190332):当宽度过大时 PngReader 抛出 NegativeArraySizeException
- [JDK-8190511](https://bugs.openjdk.java.net/browse/JDK-8190511):对于非常小的畸形 PNG,PngReader 抛出 OutOfMemoryError
- [JDK-8190512](https://bugs.openjdk.java.net/browse/JDK-8190512):对于具有负维度的畸形图像,PngReader 抛出未记录的 IllegalArgumentException:“Empty Region”而不是 IOException
- [JDK-8190997](https://bugs.openjdk.java.net/browse/JDK-8190997):当缺少 PLTE 部分时 PngReader 抛出 NullPointerException
- [JDK-8191023](https://bugs.openjdk.java.net/browse/JDK-8191023):当关键字长度超过块大小时,PngReader 在 parse_tEXt_chunk 中抛出 NegativeArraySizeException
- [JDK-8191076](https://bugs.openjdk.java.net/browse/JDK-8191076):当关键字长度超过块大小时,PngReader 在 parse_zTXt_chunk 中抛出 NegativeArraySizeException
- [JDK-8191109](https://bugs.openjdk.java.net/browse/JDK-8191109):当关键字长度超过块大小时,PngReader 在 parse_iCCP_chunk 中抛出 NegativeArraySizeException
- [JDK-8191174](https://bugs.openjdk.java.net/browse/JDK-8191174):PngReader 抛出未记录的 IllegalArgumentException,消息为“Pixel stride times width must be <= scanline stride”
- [JDK-8191073](https://bugs.openjdk.java.net/browse/JDK-8191073):读取畸形文件头时 JpegImageReader 抛出 IndexOutOfBoundsException
- [JDK-8193444](https://bugs.openjdk.java.net/browse/JDK-8193444):当格式包含长序列的 unicode 字符时 SimpleDateFormat 抛出 ArrayIndexOutOfBoundsException
- [JDK-8193877](https://bugs.openjdk.java.net/browse/JDK-8193877):使用填充时 DateTimeFormatterBuilder 抛出 ClassCastException
- [mozilla/rhino#405](https://github.com/mozilla/rhino/issues/405):由于畸形的解构语法导致 FAILED ASSERTION
- [mozilla/rhino#406](https://github.com/mozilla/rhino/issues/406):编译畸形的解构表达式时出现 ClassCastException
- [mozilla/rhino#407](https://github.com/mozilla/rhino/issues/407):CodeGen 生成的字节码中出现 java.lang.VerifyError
- [mozilla/rhino#409](https://github.com/mozilla/rhino/issues/409):解析 '
标签:JS文件枚举, 代码覆盖率, 域名枚举, 测试工具