关于 cargo-fuzz
cargo-fuzz 是面向使用 Cargo 的 Rust 项目的模糊测试命令行工具,基于 libFuzzer,并自动设置相关编译选项及 AddressSanitizer 支持。
主要功能
- 01基于 libFuzzer 进行模糊测试
- 02Cargo 子命令快速初始化目标
- 03默认集成 AddressSanitizer
- 04支持 arbitrary 结构化输入
- 05生成覆盖率数据与报告
适用场景
- 01测试 Rust 库处理任意输入的能力
- 02对解析器等输入处理代码进行模糊测试
- 03重放崩溃用例并检查覆盖率
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ trailofbits/ skills --skill cargo-fuzz安装后在 Claude Code 中输入/cargo-fuzz确认已加载。
附带文件agents/openai.yamlassets/trail-of-bits-mark.svg
详细介绍
cargo-fuzz
在使用 Cargo 对 Rust 项目进行模糊测试时,cargo-fuzz 是事实上的首选。它使用 libFuzzer 作为后端,并提供便捷的 Cargo 子命令,自动为 Rust 项目启用相关编译标志,包括支持 AddressSanitizer 等消毒器。
适用场景
对于使用 Cargo 的 Rust 项目,cargo-fuzz 目前是主要且最成熟的模糊测试解决方案。
| 模糊测试器 | 最适用场景 | 复杂度 |
|---|---|---|
| cargo-fuzz | 基于 Cargo 的 Rust 项目、快速设置 | 低 |
| AFL++ | 多核模糊测试、非 Cargo 项目 | 中 |
| LibAFL | 自定义模糊测试器、研究、高级用例 | 高 |
在以下情况下选择 cargo-fuzz:
- 项目使用 Cargo(必需)
- 希望轻松快速地完成设置,尽量减少配置
- 需要集成的消毒器支持
- 要对带有或不带有 unsafe 代码块的 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
# Edit fuzz/fuzz_targets/fuzz_target_1.rs with your harness
cargo +nightly fuzz run fuzz_target_1
安装
cargo-fuzz 需要 nightly Rust 工具链,因为它使用了仅在 nightly 中提供的功能。
先决条件
- 通过 rustup 安装 Rust 和 Cargo
- Nightly 工具链
Linux/macOS
# Install nightly toolchain
rustup install nightly
# Install cargo-fuzz
cargo install cargo-fuzz
验证
cargo +nightly --version
cargo fuzz --version
编写 Harness
项目结构
将代码组织为库 crate 时,cargo-fuzz 的效果最佳。如果你的项目是二进制项目,请将 main.rs 拆分为:
src/main.rs # Entry point (main function)
src/lib.rs # Code to fuzz (public functions)
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. Validate input size if needed
if data.is_empty() {
return;
}
// 2. Call target function with fuzz data
your_project::target_function(data);
}
fuzz_target!(|data: &[u8]| {
harness(data);
});
Harness 规则
| 应做 | 不应做 |
|---|---|
| 将代码组织为库 crate | 将所有内容放在 main.rs 中 |
使用 fuzz_target! 宏 | 编写自定义 main 函数 |
妥善处理 Result::Err | 对预期错误触发 panic |
| 保持 harness 确定性 | 使用随机数生成器 |
另请参阅: 有关详细的 harness 编写技巧和使用
arbitrarycrate 进行的结构感知模糊测试,请参阅 fuzz-harness-writing 技巧技能。
结构感知模糊测试
cargo-fuzz 集成了 arbitrary crate,可进行结构感知模糊测试:
// In your library crate
use arbitrary::Arbitrary;
#[derive(Debug, Arbitrary)]
pub struct Name {
data: String
}
// In your fuzz target
#![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)
如果项目不使用 unsafe Rust,请禁用消毒器以提升 2 倍性能:
cargo +nightly fuzz run --sanitizer none fuzz_target_1
检查项目是否使用 unsafe 代码:
cargo install cargo-geiger
cargo geiger
重新执行测试用例
# Run a specific test case (e.g., a crash)
cargo +nightly fuzz run fuzz_target_1 fuzz/artifacts/fuzz_target_1/crash-<hash>
# Run all corpus entries without fuzzing
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 | 模糊测试器已成功初始化 |
| 带堆栈跟踪的崩溃 | 发现 Bug,已保存到 fuzz/artifacts/ |
语料库位置:fuzz/corpus/fuzz_target_1/
崩溃文件位置:fuzz/artifacts/fuzz_target_1/
消毒器集成
AddressSanitizer (ASan)
ASan 默认启用,可检测内存错误:
cargo +nightly fuzz run fuzz_target_1
禁用消毒器
对于纯安全 Rust(你的代码或依赖项中没有 unsafe 代码块):
cargo +nightly fuzz run --sanitizer none fuzz_target_1
性能影响: ASan 会带来约 2 倍的开销。对于安全 Rust,禁用它可以提高模糊测试速度。
检查 unsafe 代码
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
生成覆盖率报告
# Generate coverage data from corpus
cargo +nightly fuzz coverage fuzz_target_1
创建覆盖率生成脚本:
cat <<'EOF' > ./generate_html
#!/bin/sh
if [ $# -lt 1 ]; then
echo "Error: Name of fuzz target is required."
echo "Usage: $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 倍 |
| 定期检查覆盖率 | 找出测试框架或种子语料库中的缺口 |
| 为解析器使用字典 | 帮助绕过魔数检查 |
| 将代码组织为库 | 与 cargo-fuzz 集成的必要条件 |
libFuzzer 选项
在 -- 后向 libFuzzer 传递选项:
# See all options
cargo +nightly fuzz run fuzz_target_1 -- -help=1
# Set timeout per run
cargo +nightly fuzz run fuzz_target_1 -- -timeout=10
# Use dictionary
cargo +nightly fuzz run fuzz_target_1 -- -dict=dict.dict
# Limit maximum input size
cargo +nightly fuzz run fuzz_target_1 -- -max_len=1024
多核模糊测试
# Experimental forking support (not recommended)
cargo +nightly fuzz run --jobs 1 fuzz_target_1
注意:多核模糊测试功能仍处于实验阶段,不建议使用。如需并行模糊测试,可考虑手动运行多个实例,或使用 AFL++。
真实示例
示例:ogg Crate
ogg crate 用于解析 Ogg 媒体容器文件。解析器是很好的模糊测试目标,因为它们会处理不受信任的数据。
# Clone and initialize
git clone https://github.com/RustAudio/ogg.git
cd ogg/
cargo fuzz init
测试框架位于 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
故障排除
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 出现 "requires nightly" 错误 | 使用了 stable 工具链 | 使用 cargo +nightly fuzz |
| 模糊测试性能较慢 | 对安全的 Rust 启用了 ASan | 添加 --sanitizer none 标志 |
| 出现 "cannot find binary" | 没有库 crate | 将代码从 main.rs 移至 lib.rs |
| Sanitizer 编译问题 | nightly 版本不正确 | 尝试其他 nightly 版本: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 文档 介绍结构感知模糊测试,以及如何为 Rust 类型自动派生实现。
cargo-fuzz GitHub 仓库 cargo-fuzz 的源代码、问题跟踪器和示例。