模糊测试 LLVM 库和工具¶
简介¶
LLVM 代码树包含许多针对各种组件的模糊测试器。这些模糊测试器构建在 LibFuzzer 之上。要构建和运行这些模糊测试器,请参阅 配置 LLVM 以构建模糊测试器。
可用的模糊测试器¶
clang-fuzzer¶
一个 通用模糊测试器,尝试将文本输入编译为 C++ 代码。此模糊测试器报告的一些错误位于 bugzilla 上 和 OSS Fuzz 的跟踪器上。
clang-proto-fuzzer¶
一个 基于 libprotobuf-mutator 的模糊测试器,它编译从描述 C++ 语言子集的 protobuf 类生成的有效 C++ 程序。
此模糊测试器在 ignore_remaining_args=1 之后接受 clang 命令行选项。例如,以下命令将使用更高的优化级别模糊测试 clang
% bin/clang-proto-fuzzer <corpus-dir> -ignore_remaining_args=1 -O3
clang-format-fuzzer¶
一个 通用模糊测试器,它在 C++ 文本片段上运行 clang-format。此模糊测试器报告的一些错误位于 bugzilla 上 和 OSS Fuzz 的跟踪器上。
llvm-as-fuzzer¶
一个 通用模糊测试器,尝试将文本解析为 LLVM 汇编语言。此模糊测试器报告的一些错误位于 bugzilla 上。
llvm-dwarfdump-fuzzer¶
一个 通用模糊测试器,它将输入解释为目标文件并在其上运行 llvm-dwarfdump。此模糊测试器报告的一些错误位于 OSS Fuzz 的跟踪器上
llvm-demangle-fuzzer¶
一个 通用模糊测试器,用于各种 LLVM 工具中使用的 Itanium 解码器。我们已经对 __cxa_demangle 进行过彻底的模糊测试,为什么不模糊测试 LLVM 对同一函数的实现呢!
llvm-isel-fuzzer¶
一个 结构化的 LLVM IR 模糊测试器,旨在查找指令选择中的错误。
此模糊测试器在 ignore_remaining_args=1 之后接受标志。这些标志与 llc 的标志匹配,并且需要三元组。例如,以下命令将使用 全局指令选择 模糊测试 AArch64
% bin/llvm-isel-fuzzer <corpus-dir> -ignore_remaining_args=1 -mtriple aarch64 -global-isel -O0
还可以将一些标志直接指定在二进制名称本身中,以支持 OSS Fuzz,OSS Fuzz 在处理必需参数时存在问题。为此,您可以将 llvm-isel-fuzzer
复制或移动到 llvm-isel-fuzzer--x-y-z
,使用“–”将选项与二进制名称分隔。有效选项是架构名称(aarch64
、x86_64
)、优化级别(O0
、O2
)或特定关键字,例如 gisel
以启用全局指令选择。在这种模式下,相同的示例可以这样运行
% bin/llvm-isel-fuzzer--aarch64-O0-gisel <corpus-dir>
llvm-opt-fuzzer¶
一个 结构化的 LLVM IR 模糊测试器,旨在查找优化传递中的错误。
它接收优化管道并为每个模糊测试器输入运行它。
此模糊测试器的接口几乎直接反映了 llvm-isel-fuzzer
。需要 mtriple
和 passes
参数。传递以适合新传递管理器的格式指定。您可以在 PassBuilder::parsePassPipeline
的 doxygen 中找到有关此格式的一些文档。
% bin/llvm-opt-fuzzer <corpus-dir> -ignore_remaining_args=1 -mtriple x86_64 -passes instcombine
与 llvm-isel-fuzzer
类似,某些预定义配置中的参数可能会直接嵌入到二进制文件名中
% bin/llvm-opt-fuzzer--x86_64-instcombine <corpus-dir>
llvm-mc-assemble-fuzzer¶
一个 通用模糊测试器,通过将输入视为特定于目标的汇编来模糊测试 MC 层的汇编程序。
请注意,此模糊测试器具有不寻常的命令行界面,它与 libFuzzer 的所有功能不完全兼容。模糊测试器参数必须在 --fuzzer-args
之后传递,任何 llc
标志必须使用两个连字符。例如,要模糊测试 AArch64 汇编程序,您可以使用以下命令
llvm-mc-fuzzer --triple=aarch64-linux-gnu --fuzzer-args -max_len=4
此方案将来可能会发生变化。
llvm-mc-disassemble-fuzzer¶
一个 通用模糊测试器,通过将输入视为已汇编的二进制数据来模糊测试 MC 层的反汇编程序。
请注意,此模糊测试器具有不寻常的命令行界面,它与 libFuzzer 的所有功能不完全兼容。有关详细信息,请参阅上面关于 llvm-mc-assemble-fuzzer
的说明。
lldb-target-fuzzer¶
一个 通用模糊测试器,它将输入解释为目标文件并使用它们在 lldb 中创建目标。
变异器和输入生成器¶
模糊测试目标的输入是通过对 语料库 进行随机变异生成的。LLVM 中的模糊测试器可能需要几种变异类型。
通用随机模糊测试¶
输入变异的最基本形式是使用 LibFuzzer 的内置变异器。它们只是将输入语料库视为一组位并进行随机变异。这种类型的模糊测试器非常适合对程序的表面层进行压力测试,并且非常适合测试词法分析器、解析器或二进制协议等内容。
使用这种变异器类型的一些树内模糊测试器是 clang-fuzzer、clang-format-fuzzer、llvm-as-fuzzer、llvm-dwarfdump-fuzzer、llvm-mc-assemble-fuzzer 和 llvm-mc-disassemble-fuzzer。
使用 libprotobuf-mutator
进行结构化模糊测试¶
我们可以使用 libprotobuf-mutator 来执行结构化模糊测试并对程序的更深层进行压力测试。这是通过定义一个 protobuf 类来实现的,该类将任意数据转换为结构化有趣的输入。具体来说,我们使用它来处理 C++ 语言的一个子集并执行产生有效 C++ 程序的变异,以便练习 clang 中比解析错误处理更有趣的部件。
要构建这种类型的模糊测试器,您需要安装 protobuf 及其依赖项,并且您需要在使用 CMake 配置构建时指定一些额外的标志。例如,可以通过将 -DCLANG_ENABLE_PROTO_FUZZER=ON
添加到 配置 LLVM 以构建模糊测试器 中描述的标志中来启用 clang-proto-fuzzer。
目前唯一使用 libprotobuf-mutator
的树内模糊测试器是 clang-proto-fuzzer。
LLVM IR 的结构化模糊测试¶
我们还对以 LLVM IR 作为输入的模糊测试器使用更直接的结构化模糊测试形式。这是通过 FuzzMutate
库实现的,该库曾在 2017 年的 EuroLLVM 上讨论过。
FuzzMutate
库用于在 llvm-isel-fuzzer 中结构化模糊测试后端。
构建和运行¶
配置 LLVM 以构建模糊测试器¶
只要您在构建 LLVM 时启用了 sanitizer 覆盖率,fuzzer 就会默认构建并链接到 libFuzzer。您通常还会启用至少一个 sanitizer 以更快地查找错误。构建 fuzzer 最常见的方法是在 CMake 调用中添加以下两个标志:-DLLVM_USE_SANITIZER=Address -DLLVM_USE_SANITIZE_COVERAGE=On
。
注意
如果您在使用 sanitizer 构建时在 LLVM 树中检出了compiler-rt
,则需要指定-DLLVM_BUILD_RUNTIME=Off
以避免使用启用的 sanitizer 构建 sanitizer 本身。
注意
如果您使用 BFD ld 构建,可能会遇到问题,BFD ld 是许多 Unix 系统上的默认链接器。这些问题正在 https://llvm.net.cn/PR34636 中跟踪。
持续运行和查找错误¶
过去有一个公共的 buildbot 持续运行 LLVM fuzzer,虽然这确实发现了问题,但它没有一个很好的方法以可操作的方式报告问题。因此,我们正转向更多地使用 OSS Fuzz。
您可以浏览 LLVM 项目问题列表,以查看由 LLVM 在 OSS Fuzz 上 发现的错误。这些错误也会发送到 llvm-bugs 邮件列表。
编写 Fuzzers 的实用程序¶
LLVM 提供了一些用于编写 fuzzer 的实用程序。
一些用于处理命令行界面的帮助程序位于 include/llvm/FuzzMutate/FuzzerCLI.h
中,包括以一致的方式解析命令行选项以及实现独立主函数的功能,以便在未针对 libFuzzer 构建时可以构建和测试您的 fuzzer。
还有一些用于处理 fuzzer 的 CMake 配置,您应该使用 add_llvm_fuzzer
来设置 fuzzer 目标。此函数的工作方式类似于 add_llvm_tool
等函数,但它们会在适当的时候负责链接到 LibFuzzer,并且可以传递 DUMMY_MAIN
参数以启用独立测试。