模糊测试

模糊测试或模糊测试是一种测试方式,可针对目标界面生成随机输入,以自动发现缺陷和/或漏洞。

换言之,模糊测试只是一种使用生成的数据测试 API 的自动化方法。

模糊测试工具是一种用于对接口进行模糊测试的程序。它通常重复执行三个步骤:

  1. 生成新的输入。模糊测试工具的这一部分称为“模糊引擎”。
  2. 使用输入来运行目标接口或要测试的代码。这称为“模糊测试目标函数”。
  3. 监控要测试的代码是否存在任何异常情况。这称为“插桩”。

本文档从整体上探讨了模糊测试。如需详细了解如何对 Fuchsia 软件进行模糊测试,请参阅 Fuchsia 中的模糊测试

历史记录

虽然在测试中使用随机数据可以追溯到打孔卡时代,但“模糊测试”这一术语大约在 1990 年出现,他在雷暴期间发送远程命令时有很多 Unix 程序会崩溃,这让他感到惊讶。在随后的努力中,他和他的学生发现了他们检查的超过四分之一的常用 Unix 实用程序的崩溃输入。

随着时间的推移,模糊测试演变为几种不同的形式,包括:

  • Dumb 模糊测试:这是最广为人知的方式,简单来说就是提交随机数据和监控崩溃情况。它往往会在浅层错误路径上卡住。
  • 基于生成的模糊测试:模型用于描述生成的数据应该是什么样的。这种方法通常可以更深入地处理代码,因为模型可确保输入通过更简单的验证。但是,创建和维护数据模型通常需要投入大量的时间和精力。
  • 基于突变的模糊测试:这种形式不是使用模型来描述数据,而是从实际的有效输入开始,然后向其中添加随机突变。这组“种子”输入统称为“语料库”。它可以更轻松地设置,但有大量变更无法通过简单验证。

覆盖率引导的模糊测试

一种基于突变的模糊测试变体,称为“覆盖率引导的模糊测试”已被证实可产生特别大量的 bug。这是 Google 首选的模糊测试形式,也是 Fuchsia 通过 LLVM 的 libFuzzer 支持的主要形式。

代码覆盖率用于衡量处理特定输入后执行哪些指令。在覆盖率引导型模糊测试中,系统会对被测代码进行插桩测试,以便收集代码覆盖率数据以及监控异常情况。模糊引擎会使用此数据来确定哪些输入可以增加整体代码覆盖率,并将这些输入用作生成进一步输入的基础。

覆盖率引导模糊测试引擎的示例包括 libFuzzersyzkallerAFL。每种类型都有自己的策略,用于根据其语料库中的输入以及从插桩获得的反馈来改变输入。

覆盖率引导的插桩示例包括 LLVM 的排错程序,例如 ASan。这些操作可以检查特定条件(例如内存损坏),并提供回调来注册要执行的代码块

在下图中,模糊测试工具作者会提供模糊测试目标函数和可选的语料库。该工具与模糊引擎、插桩的排错程序以及要测试以创建模糊测试工具的库代码结合使用。模糊测试工具会返回证明软件缺陷的输入,以及日志和/或堆栈轨迹等其他工件。 覆盖率引导式模糊测试

模糊测试基础架构

使用模糊引擎和插桩测试可以轻松创建新的模糊测试工具。借助模糊测试基础架构,您可以像运行模糊测试工具一样轻松。模糊测试基础架构可以自动执行许多与模糊测试相关的流程,例如:

  • 检索并部署受测代码的最新版本。
  • 持续运行模糊测试工具。
  • 在并行运行模糊测试工具时同步语料库。
  • 重现由特定输入引起的软件缺陷。
  • 最大限度地减少触发特定软件缺陷所需的输入。
  • 隔离首次发现软件缺陷的版本。
  • 只要输入的内容发现了可重现的新软件缺陷,系统就会打开 bug 报告。
  • 当不再观察到以前可重现的软件缺陷时,关闭 bug 报告。

将项目与模糊测试基础架构集成需要投入大量精力,但可以带来诸多优势。有了完整的模糊测试基础架构,模糊测试工具作者只需提供模糊测试目标函数以及如何构建该函数,即可收到切实可行的 bug 报告。

模糊测试基础架构的示例包括 ClusterFuzzsyz-ciOSS Fuzz

模糊测试的效果

进行模糊测试的动机相当明确。作为一种方法,这种方法在查找 bug 方面效果出众。通过对引擎和基础架构进行模糊测试,发现了数万个 bug,例如:

模糊测试(尤其是覆盖率引导模糊测试)对于测试和查找代码中的 bug 非常有用:

  • 接收来自不可信来源的输入,且必须是安全的。
  • 具有复杂的算法,存在一些等效性(例如压缩和解压缩),并且必须正确
  • 需要处理大量输入和/或不可靠的依赖项,并且必须保持稳定

单元测试和模糊测试

另一个关键的一点是,开发者编写和维护模糊测试工具的总体成本较低。如果代码经过充分的单元测试,则只需极少的额外工作量即可对代码进行模糊测试。单元测试描述了如何调用 API,并且可以为模糊测试目标函数奠定基础。

如果代码未经过充分的单元测试,开发模糊测试工具难度更大,但仍然非常有益。编写模糊测试工具可以使代码更易于进行单元测试:

  • 该操作可能会促使重构代码以公开更容易模糊测试的 API,例如将二进制文件转换为封装库的瘦封装容器。这些更改还使代码更易于进行单元测试。
  • 覆盖率引导的模糊测试工具将生成一个语料库,即一组“有趣”的输入。语料库通常包括边缘情况和在添加单元测试时有用的其他意外输入。

Fuchsia 中的模糊测试

Fuchsia 提供了一些指南,旨在帮助开发者对 Fuchsia 软件进行模糊测试: