简介
Mozilla 一直在 模糊测试 Firefox 及其底层组件。事实证明,模糊测试是识别质量和安全问题最有效的方法之一。通常,我们在 不同级别 上进行模糊测试:对整个浏览器进行模糊测试,但同时也会花费大量时间对 隔离代码 (例如使用 libFuzzer)或使用单独的 Shell 对整个组件(如 JS 引擎)进行模糊测试。在这篇博文中,我们将专门讨论浏览器模糊测试,并详细介绍我们开发的管道。这个管道是模糊测试团队多年来努力将我们的浏览器模糊测试工作整合在一起的结果,旨在为开发人员提供始终可行的操作问题,并简化内部和外部模糊测试工具的集成(随着这些工具的可用性)。
构建检测
为了尽可能有效,我们使用不同的方法来检测错误。这些方法包括诸如 AddressSanitizer(使用 LeakSanitizer)、ThreadSanitizer 和 UndefinedBehaviorSanitizer 等清理器,以及使用启用断言和其他运行时检查的调试构建。我们还使用诸如 rr 和 Valgrind 等调试器。这些工具中的每一个都提供了不同的视角来帮助发现特定的错误类型,但许多工具彼此不兼容,或者需要它们自己的自定义构建才能正常运行或提供最佳结果。除了提供调试和错误检测之外,某些工具在没有构建检测的情况下无法工作,例如代码覆盖率和 libFuzzer。每个操作系统和体系结构组合都需要一个唯一的构建,并且可能只支持这些工具的一个子集。
最后,每个变体都有多个活动分支,包括发行版、测试版、夜间版和扩展支持发行版 (ESR)。Firefox CI Taskcluster 实例会定期构建这些分支中的每一个。
下载构建
Taskcluster 使得找到和下载最新构建以进行测试变得轻而易举。我们在上面讨论了不同检测类型创建的变体数量,我们需要在自动化中对它们进行模糊测试。由于构建、工件、体系结构、操作系统和解压缩每个文件的组合数量庞大,因此下载是一项非凡的任务。
为了帮助降低构建管理的复杂性,我们开发了一个名为 fuzzfetch 的工具。Fuzzfetch 使得指定所需的构建参数变得轻而易举,它会下载并解压缩构建。它还支持下载指定版本,使其与二分工具一起使用时非常有用。
我们如何生成测试用例
由于这篇博文的目的是解释整个管道,因此我们不会花费太多时间来解释模糊器。如果您有兴趣,请阅读 “使用 WebIDL 模糊测试 Firefox” 和 树内文档。我们使用公开可用的模糊器和定制模糊器的组合来生成测试用例。
我们如何执行、报告和扩展
对于针对浏览器的模糊器,Grizzly 会管理和运行测试用例,并监控结果。创建适配器 使得我们在 Grizzly 中轻松运行现有模糊器。
为了充分利用任何给定机器上的可用资源,我们并行运行多个 Grizzly 实例。
对于每个模糊器,我们都会创建容器来封装运行它所需的配置。这些容器位于 Orion 单一代码库 中。每个模糊器都有一个配置,其中包含部署细节和资源分配,具体取决于模糊器的优先级。Taskcluster 会持续部署这些配置以分配工作并管理模糊测试节点。
Grizzly Target 会处理诸如挂起、崩溃和其他缺陷等问题的检测。Target 是 Grizzly 和浏览器之间的接口。检测到的问题会自动打包并报告给 FuzzManager 服务器。FuzzManager 服务器提供了自动化和用于对结果进行分类的 UI。
其他更具针对性的模糊器会使用 JS Shell,而基于 libFuzzer 的目标会使用 模糊测试接口。许多第三方库也会在 OSS-Fuzz 中进行模糊测试。这些库值得一提,但不在本文的讨论范围之内。
管理结果
对各种目标进行大规模的多个模糊器运行会生成大量数据。这些崩溃不适合直接输入到诸如 Bugzilla 等错误跟踪系统。我们拥有管理这些数据并使其准备报告的工具。
FuzzManager 客户端库会过滤掉崩溃变化和重复结果,然后再将它们从模糊测试节点中删除。唯一的结果会报告给 FuzzManager 服务器。FuzzManager Web 界面允许创建签名,这些签名有助于将报告分组到桶中,以帮助客户端检测重复结果。
模糊器通常会生成数百行甚至数千行的测试用例。FuzzManager 会自动扫描桶,以在 Taskcluster 中排队减少任务。这些减少任务会使用 Grizzly Reduce 和 Lithium 来应用不同的减少策略,通常会删除大部分不必要的数据。每个桶都会持续处理,直到成功减少完成为止。然后,工程师可以对最小化的测试用例进行最终检查,并将其附加到错误报告。最终结果通常用作 Firefox 测试套件中的崩溃测试。
模糊器的代码覆盖率也会定期进行衡量。FuzzManager 再次用于收集代码覆盖率数据并生成覆盖率报告。
创建最佳错误报告
我们的目标是创建可操作的错误报告,以便尽快解决问题,同时最大程度地减少开发人员的工作量。
我们通过提供以下内容来实现这一点:
Grizzly Replay 是一个工具,它构成了 Bugmon 和 Grizzly Reduce 的基本执行引擎,并且可以轻松收集 rr 跟踪以提交给 Pernosco。它使浏览器测试用例的重新运行变得容易,无论是在自动化中还是在手动使用中。它简化了使用顽固测试用例和触发多个结果的测试用例的操作。
如前所述,我们也一直在使用 Pernosco。Pernosco 是一个工具,它为 rr 跟踪提供 Web 界面,并使开发人员无需直接访问执行环境即可使用它们。它是一个由同名公司开发的出色工具,可以显著帮助调试大规模并行应用程序。当测试用例不可靠到无法减少或附加到错误报告时,它也很有用。创建 rr 跟踪并将其上传可以使停滞的错误报告变得可行。
Grizzly 和 Pernosco 的结合带来了额外的优势,即使不频繁、难以重现的问题变得可行。非常不一致问题的测试用例可以在 rr 下运行数百次甚至数千次,直到发生所需的崩溃。跟踪会自动收集并准备好提交给 Pernosco,以便开发人员修复,而不是由于不可行而被忽略。
我们如何与开发人员互动
为了请求新功能,并获得适当的评估,模糊测试团队可以通过 fuzzing@mozilla.com 或 Matrix 与他们联系。这对于任何原因来说都是一个很好的联系方式。我们很乐意帮助您解决任何与模糊测试相关的疑问或想法。当我们收到有关新计划和功能的信息时,我们也会联系您,这些计划和功能我们认为需要关注。一旦某个组件的模糊测试开始,我们主要通过 Bugzilla 进行沟通。如前所述,我们努力打开可操作的问题,或者增强其他人记录的现有问题。
Bugmon 用于自动二分查找回归范围。这会尽快通知相关人员,并在标记为 FIXED 后验证错误。自动关闭错误会将其从 FuzzManager 中删除,因此,如果类似的错误再次进入代码库,它可以再次被识别。
在模糊测试期间发现的一些问题会阻止我们有效地模糊测试某个功能或构建变体。这些问题被称为 模糊测试阻碍因素,并且它们有几种不同的形式。从产品角度来看,这些问题可能看起来是良性的,但它们可能会阻止模糊器针对重要的代码路径,甚至完全阻止模糊测试目标。适当优先处理这些问题并尽快修复它们对于模糊测试团队来说非常有帮助,并且非常感谢。
PrefPicker 管理用于模糊测试的 Firefox 首选项集。在添加具有首选项支持的功能时,请考虑将其添加到 PrefPicker 模糊测试模板中,使其在模糊测试期间启用。定期审计 PrefPicker 的 模糊测试模板 可以帮助确保不会遗漏任何区域,并最大程度地有效利用资源。
衡量成功
与其他领域一样,衡量是评估成功的重要部分。我们利用 Bugzilla 的元错误功能来帮助我们跟踪模糊器识别的问题。我们努力为每个模糊器和每个新模糊测试组件创建元错误。
例如,Domino 的元错误 列出了此工具识别出的所有问题(超过 1100 个!)。使用此 Bugzilla 数据,我们能够展示多年来各种模糊器的影响。
Domino 随着时间的推移报告的错误数量
这些仪表板有助于评估模糊器的投资回报率。
结论
模糊测试管道中包含许多组件。这些组件不断发展以适应调试工具、执行环境和浏览器内部的变化。开发人员一直在添加、删除和更新浏览器功能。正在检测、分类和记录错误。要确保一切持续运行并尽可能多地针对代码,需要持续不断的努力。
如果您参与 Firefox 开发,可以通过以下方式提供帮助:告知我们可能影响或需要模糊测试的新功能和计划,优先处理 模糊测试阻滞器,并在 PrefPicker 中整理 模糊测试偏好设置。如果您对模糊测试感兴趣,请参与 漏洞赏金计划。我们的 工具可公开获取,我们鼓励您进行漏洞挖掘。