Fuchsia 的工具链支持使用 LLVM 的 libFuzzer 进行模糊测试。为了针对特定的集群 接口,您需要实现一个模糊测试目标函数,该函数使用 提供的字节序列来练习该接口。字节序列称为 模糊测试工具“input”。libFuzzer 使用模糊测试目标函数来搜索会导致 panic 的输入 或其他错误。
用于模糊测试的示例代码
对于下面的每个示例,假设您想要测试如下代码:
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
trait。
也可以创建“手动”模糊测试目标函数,它是针对简单模糊测试的
定位其他语言的函数。此函数必须将对字节片的引用作为其
单个参数,不返回任何内容,并且具有 #[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
trait。如果可能的话 推荐的方法是自动派生 trait。否则, “手动”完成按照 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 构建新创建的模糊测试工具。