name: cargo-fuzz type: 模糊测试器 description: > cargo-fuzz 是使用Cargo的Rust项目事实上的模糊测试工具。 用于通过libFuzzer后端对Rust代码进行模糊测试。
cargo-fuzz
cargo-fuzz 是当使用Cargo时,模糊测试Rust项目的首选工具。它使用libFuzzer作为后端,并提供一个方便的Cargo子命令,自动为您的Rust项目启用相关编译标志,包括支持如AddressSanitizer等消毒器。
何时使用
cargo-fuzz 是目前使用Cargo的Rust项目的主要和最成熟的模糊测试解决方案。
| 模糊测试器 | 最适合 | 复杂性 |
|---|---|---|
| cargo-fuzz | 基于Cargo的Rust项目,快速设置 | 低 |
| AFL++ | 多核心模糊测试,非Cargo项目 | 中等 |
| LibAFL | 自定义模糊测试器,研究,高级用例 | 高 |
选择 cargo-fuzz 当:
- 您的项目使用Cargo(必需)
- 您希望简单、快速设置,最小配置
- 您需要集成消毒器支持
- 您正在模糊测试带或不带不安全块的Rust代码
快速开始
#![no_main]
use libfuzzer_sys::fuzz_target;
fn harness(data: &[u8]) {
your_project::check_buf(data);
}
fuzz_target!(|data: &[u8]| {
harness(data);
});
初始化和运行:
cargo fuzz init
# 在 fuzz/fuzz_targets/fuzz_target_1.rs 中编辑您的harness
cargo +nightly fuzz run fuzz_target_1
安装
cargo-fuzz 需要夜间Rust工具链,因为它使用仅在夜间可用的功能。
先决条件
- 通过 rustup 安装的Rust和Cargo
- 夜间工具链
Linux/macOS
# 安装夜间工具链
rustup install nightly
# 安装 cargo-fuzz
cargo install cargo-fuzz
验证
cargo +nightly --version
cargo fuzz --version
编写Harness
项目结构
cargo-fuzz 在您的代码结构为库crate时效果最佳。如果您有二进制项目,拆分您的 main.rs 为:
src/main.rs # 入口点(主函数)
src/lib.rs # 要模糊测试的代码(公共函数)
Cargo.toml
初始化模糊测试:
cargo fuzz init
这会创建:
fuzz/
├── Cargo.toml
└── fuzz_targets/
└── fuzz_target_1.rs
Harness结构
#![no_main]
use libfuzzer_sys::fuzz_target;
fn harness(data: &[u8]) {
// 1. 如果需要,验证输入大小
if data.is_empty() {
return;
}
// 2. 使用模糊数据调用目标函数
your_project::target_function(data);
}
fuzz_target!(|data: &[u8]| {
harness(data);
});
Harness规则
| 做 | 不要 |
|---|---|
| 将代码结构为库crate | 将所有内容保留在main.rs中 |
使用 fuzz_target! 宏 |
编写自定义主函数 |
优雅处理 Result::Err |
对预期错误恐慌 |
| 保持harness确定性 | 使用随机数生成器 |
另请参阅: 关于详细harness编写技术和使用
arbitrarycrate进行结构感知模糊测试,参见 fuzz-harness-writing 技术技能。
结构感知模糊测试
cargo-fuzz 与 arbitrary crate集成以进行结构感知模糊测试:
// 在您的库crate中
use arbitrary::Arbitrary;
#[derive(Debug, Arbitrary)]
pub struct Name {
data: String
}
// 在您的模糊测试目标中
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: your_project::Name| {
data.check_buf();
});
添加到您的库的 Cargo.toml:
[dependencies]
arbitrary = { version = "1", features = ["derive"] }
运行活动
基本运行
cargo +nightly fuzz run fuzz_target_1
不使用消毒器(安全Rust)
如果您的项目不使用不安全Rust,禁用消毒器以获得2倍性能提升:
cargo +nightly fuzz run --sanitizer none fuzz_target_1
检查您的项目是否使用不安全代码:
cargo install cargo-geiger
cargo geiger
重新执行测试用例
# 运行特定测试用例(例如,崩溃)
cargo +nightly fuzz run fuzz_target_1 fuzz/artifacts/fuzz_target_1/crash-<hash>
# 运行所有语料库条目而不进行模糊测试
cargo +nightly fuzz run fuzz_target_1 fuzz/corpus/fuzz_target_1 -- -runs=0
使用字典
cargo +nightly fuzz run fuzz_target_1 -- -dict=./dict.dict
解释输出
| 输出 | 含义 |
|---|---|
NEW |
发现新的增加覆盖率的输入 |
pulse |
定期状态更新 |
INITED |
模糊测试器初始化成功 |
| 带堆栈跟踪的崩溃 | 发现错误,保存到 fuzz/artifacts/ |
语料库位置: fuzz/corpus/fuzz_target_1/
崩溃位置: fuzz/artifacts/fuzz_target_1/
消毒器集成
AddressSanitizer (ASan)
ASan默认启用并检测内存错误:
cargo +nightly fuzz run fuzz_target_1
禁用消毒器
对于纯安全Rust(您的代码或依赖中无不安全块):
cargo +nightly fuzz run --sanitizer none fuzz_target_1
性能影响: ASan增加约2倍开销。对安全Rust禁用以提高模糊测试速度。
检查不安全代码
cargo install cargo-geiger
cargo geiger
另请参阅: 关于详细消毒器配置、标志和故障排除,参见 address-sanitizer 技术技能。
覆盖率分析
cargo-fuzz 与Rust的覆盖工具集成以分析模糊测试有效性。
先决条件
rustup toolchain install nightly --component llvm-tools-preview
cargo install cargo-binutils
cargo install rustfilt
生成覆盖率报告
# 从语料库生成覆盖率数据
cargo +nightly fuzz coverage fuzz_target_1
创建覆盖率生成脚本:
cat <<'EOF' > ./generate_html
#!/bin/sh
if [ $# -lt 1 ]; then
echo "错误:需要模糊测试目标名称。"
echo "用法:$0 fuzz_target [sources...]"
exit 1
fi
FUZZ_TARGET="$1"
shift
SRC_FILTER="$@"
TARGET=$(rustc -vV | sed -n 's|host: ||p')
cargo +nightly cov -- show -Xdemangler=rustfilt \
"target/$TARGET/coverage/$TARGET/release/$FUZZ_TARGET" \
-instr-profile="fuzz/coverage/$FUZZ_TARGET/coverage.profdata" \
-show-line-counts-or-regions -show-instantiations \
-format=html -o fuzz_html/ $SRC_FILTER
EOF
chmod +x ./generate_html
生成HTML报告:
./generate_html fuzz_target_1 src/lib.rs
HTML报告保存到: fuzz_html/
另请参阅: 关于详细覆盖率分析技术和系统覆盖改进,参见 coverage-analysis 技术技能。
高级用法
技巧和诀窍
| 技巧 | 为什么有帮助 |
|---|---|
| 从种子语料库开始 | 显著加快初始覆盖率发现速度 |
对安全Rust使用 --sanitizer none |
2倍性能提升 |
| 定期检查覆盖率 | 识别harness或种子语料库中的空白 |
| 对解析器使用字典 | 帮助克服魔术值检查 |
| 将代码结构为库 | cargo-fuzz集成所必需 |
libFuzzer选项
在 -- 后传递选项给libFuzzer:
# 查看所有选项
cargo +nightly fuzz run fuzz_target_1 -- -help=1
# 设置每运行超时
cargo +nightly fuzz run fuzz_target_1 -- -timeout=10
# 使用字典
cargo +nightly fuzz run fuzz_target_1 -- -dict=dict.dict
# 限制最大输入大小
cargo +nightly fuzz run fuzz_target_1 -- -max_len=1024
多核心模糊测试
# 实验性的分叉支持(不推荐)
cargo +nightly fuzz run --jobs 1 fuzz_target_1
注意:多核心模糊测试功能是实验性的,不推荐。对于并行模糊测试,考虑手动运行多个实例或使用AFL++。
现实世界示例
示例:ogg Crate
ogg crate 解析Ogg媒体容器文件。解析器是优秀的模糊测试目标,因为它们处理不受信任的数据。
# 克隆和初始化
git clone https://github.com/RustAudio/ogg.git
cd ogg/
cargo fuzz init
Harness在 fuzz/fuzz_targets/fuzz_target_1.rs:
#![no_main]
use ogg::{PacketReader, PacketWriter};
use ogg::writing::PacketWriteEndInfo;
use std::io::Cursor;
use libfuzzer_sys::fuzz_target;
fn harness(data: &[u8]) {
let mut pck_rdr = PacketReader::new(Cursor::new(data.to_vec()));
pck_rdr.delete_unread_packets();
let output = Vec::new();
let mut pck_wtr = PacketWriter::new(Cursor::new(output));
if let Ok(_) = pck_rdr.read_packet() {
if let Ok(r) = pck_rdr.read_packet() {
match r {
Some(pck) => {
let inf = if pck.last_in_stream() {
PacketWriteEndInfo::EndStream
} else if pck.last_in_page() {
PacketWriteEndInfo::EndPage
} else {
PacketWriteEndInfo::NormalPacket
};
let stream_serial = pck.stream_serial();
let absgp_page = pck.absgp_page();
let _ = pck_wtr.write_packet(
pck.data, stream_serial, inf, absgp_page
);
}
None => return,
}
}
}
}
fuzz_target!(|data: &[u8]| {
harness(data);
});
种子语料库:
mkdir fuzz/corpus/fuzz_target_1/
curl -o fuzz/corpus/fuzz_target_1/320x240.ogg \
https://commons.wikimedia.org/wiki/File:320x240.ogg
运行:
cargo +nightly fuzz run fuzz_target_1
分析覆盖率:
cargo +nightly fuzz coverage fuzz_target_1
./generate_html fuzz_target_1 src/lib.rs
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
| “需要夜间” 错误 | 使用稳定工具链 | 使用 cargo +nightly fuzz |
| 模糊测试性能慢 | 对安全Rust启用ASan | 添加 --sanitizer none 标志 |
| “找不到二进制” | 无库crate | 将代码从 main.rs 移动到 lib.rs |
| 消毒器编译问题 | 错误的夜间版本 | 尝试不同夜间: rustup install nightly-2024-01-01 |
| 覆盖率低 | 缺失种子语料库 | 添加样本输入到 fuzz/corpus/fuzz_target_1/ |
| 未找到魔术值 | 无字典 | 创建包含魔术值的字典文件 |
相关技能
技术技能
| 技能 | 用例 |
|---|---|
| fuzz-harness-writing | 使用 arbitrary crate进行结构感知模糊测试 |
| address-sanitizer | 理解ASan输出和配置 |
| coverage-analysis | 测量和改进模糊测试有效性 |
| fuzzing-corpus | 构建和管理种子语料库 |
| fuzzing-dictionaries | 为格式感知模糊测试创建字典 |
相关模糊测试器
| 技能 | 何时考虑 |
|---|---|
| libfuzzer | 使用类似工作流模糊测试C/C++代码 |
| aflpp | 多核心模糊测试或非Cargo Rust项目 |
| libafl | 高级模糊测试研究或自定义模糊测试器开发 |
资源
Rust Fuzz Book - cargo-fuzz cargo-fuzz的官方文档,涵盖安装、用法和高级功能。
arbitrary crate documentation 使用Rust类型的自动派生进行结构感知模糊测试的指南。
cargo-fuzz GitHub Repository cargo-fuzz的源代码、问题跟踪器和示例。