Fuchsia 的工具链支持使用 LLVM 的 libFuzzer 进行模糊测试。如需为特定 接口创建模糊器,您需要实现一个 模糊测试目标函数,该函数使用 提供的字节序列来执行接口。字节序列称为模糊器“输入”。libFuzzer 使用模糊测试目标函数来搜索导致崩溃或其他错误的输入。
用于模糊测试的示例代码
对于以下每个示例,假设您要测试如下代码:
C/C++
class Parser {
Parser(const std::string &name, uint32_t flags);
virtual ~Parser();
int Parse(const uint8 *buf, size_t len);
};
Rust
struct ToyStruct {
n: u8,
s: String,
}
fn toy_example(input: ToyStruct) -> Result<u8, &'static str>;
简单模糊测试目标函数
对于每种语言,模糊测试目标函数都会使用提供的字节来调用您要模糊测试的代码。如果被模糊测试的接口对其参数有明确的限制,您可以拒绝不符合这些限制的输入。您还可以忽略返回的错误,因为在无效参数上正常失败是正确的行为。
C/C++
对于 C 和 C++,模糊测试目标函数必须具有签名
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) 并返回 0:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
Parser parser("", 0);
if (size < 5) {
return 0;
}
parser.Parse(data, size);
return 0;
}
将此代码放在与被模糊测试的代码相邻的源文件中,就像使用单元测试一样。例如,上面的代码可能位于 parser-fuzztest.cc 中。
Rust
对于 Rust,建议的方法是使用下一部分讨论的 Arbitrary 特征。
您还可以创建一个“手动”模糊测试目标函数,该函数与其他语言中的简单模糊测试目标函数类似。此函数必须将对字节切片的引用作为其
单个参数,不返回任何内容,并且具有 #[fuzz] 属性:
use fuzz::fuzz;
#[fuzz]
fn toy_example_u8(input: &[u8]) {
if input.len() == 0 {
return
}
let n = input[0];
if let Ok(s) = std::str::from_utf8(input[1:]) {
let _ = toy_example(ToyStruct{n, s: s.to_string(),});
}
}
与单元测试一样,此代码可以放在与被测试代码相同的文件中。例如,上面的代码可能位于 toy_example/src/lib.rs 中。
支持模糊测试更复杂的类型
每种语言都有实用程序,可帮助您创建更复杂的模糊测试目标函数:
C/C++
LLVM 提供的 FuzzedDataProvider 类可以帮助您
将提供的 data 的各个部分映射到更复杂的类型。
例如:
#include <fuzzer/FuzzedDataProvider.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider provider(data, size);
auto flags = provider.ConsumeIntegral<uint32_t>();
auto name = provider.ConsumeRandomLengthString();
Parser parser(name, flags);
auto buf = provider.ConsumeRemainingBytes<uint8_t>();
parser.Parse(buf.data(), buf.size());
return 0;
}
使用此库有两大显著优势:
- 首先,它可以更轻松地快速编写模糊器。
- 其次,它旨在以这样一种方式动态拆分输入,以便模糊器可以从覆盖率数据中高效地创建新输入。
有一个显著的缺点:
- 由于输入是动态拆分的,因此更难提供预先存在的 语料库。提供字典仍然是可行的 。
Rust
您可以使用 Arbitrary 特征
从 arbitrary crate 中创建一个模糊测试目标函数,该函数接受一个或多个输入。这是推荐的方法。
如需编写可自动转换任意输入的模糊测试目标函数,请执行以下操作:
如果需要,请为测试代码使用的类型实现
Arbitrary特征。如果可能,建议的方法是自动派生特征。否则,可以按照 crate 的说明 “手动”完成此操作。例如,在
src/lib.rs中:use arbitrary:Arbitrary; #[derive(Arbitrary)] struct ToyStruct { ... }创建一个具有
#[fuzz]属性的函数,该函数将必要的 参数传递给您要测试的代码。例如,在
src/lib.rs中:use fuzz::fuzz; #[fuzz] fn toy_example_arbitrary(input: ToyStruct) { let _ = toy_example(input); }
接下来,您可以使用 GN 和 Ninja 构建新创建的模糊器。