2.2 教程

使用 AFL 模糊测试 rust-url

译文 · 基于 Rust Fuzz Book

教程

原文链接: https://rust-fuzz.github.io/book/afl/tutorial.html

在本教程中,我们将对 URL 解析器 rust-url 进行模糊测试。目标是找到模糊器生成的某种输入,在传给 Url::parse 时会导致 panic 或崩溃。

创建模糊测试目标

首先要创建一个模糊测试目标,形式为 Rust 二进制 crate。AFL 会调用生成的二进制文件,向标准输入提供生成的字节,我们再将其传给 Url::parse。

1
2
cargo new --bin url-fuzz-target
cd url-fuzz-target

此 crate 需要两个依赖:

  • url:我们要模糊测试的 crate
  • afl:非必需,但包含一些辅助创建模糊测试目标的实用函数

将它们添加到 Cargo.toml:

1
2
3
[dependencies]
afl = "*"
url = { git = "https://github.com/servo/rust-url.git", rev = "bfa167b4e0253642b6766a7aa74a99df60a94048" }

我们选择了已知存在 bug 的 url 特定修订版本。你的目标是找到它!

接下来在 src/main.rs 中编写模糊测试目标的源代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
#[macro_use]
extern crate afl;
extern crate url;

fn main() {
    fuzz!(|data: &[u8]| {
        if let Ok(s) = std::str::from_utf8(data) {
            let _ = url::Url::parse(&s);
        }
    });
}

fuzz! 是 afl crate 提供的实用宏,从标准输入读取字节并传给所提供的闭包。

在闭包体内,我们用 AFL 生成的字节调用 Url::parse。若一切正常,url::Url::parse 会返回包含有效 Url 的 Ok,或返回 Err 表示无法从 String 构造 Url。若 Url::parse 在解析 String 时 panic,AFL 会将其视为崩溃,AFL 界面也会相应显示。

关于 fuzz! 宏的一个重要细节:若闭包体内发生 panic,panic 会被panic::catch_unwind捕获,随后会调用 process::abort。若不调用 process::abort,AFL 不会将展开中的 panic 视为崩溃。

构建模糊测试目标

通常使用 cargo build 编译基于 Cargo 的 Rust 项目。要让 AFL 与 Rust 配合工作,构建过程中需要向 rustc 传递一些额外的编译器标志。为简化此过程,afl crate 提供了 AFL cargo 子命令,会自动为我们传递这些 rustc 标志。用法示例:

1
cargo afl <cargo command>

我们要构建此 crate,因此运行:

1
cargo afl build

与 cargo fuzz 类似,cargo afl build 会向依赖图中的每个 crate 提供 --cfg fuzzing 参数,从而启用任何标注了 #[cfg(fuzzing)] 的代码路径。

提供起始输入

AFL 严格要求起始输入;若不提供至少包含一个示例输入的输入目录,它将完全无法运行。高质量的输入语料库包含许多不同的有效输入示例,可练习被模糊测试解析过程的不同特性。关于构建有效输入语料库的更多说明见 AFL README,包括对 HTML 等高度冗长数据格式的字典方法的讨论。

本教程不会生成高质量语料库,只提供足以演示基本机制的示例。为此,我们创建一个名为 in 的目录,其中包含几个含有效 URL 的文件:

1
2
3
4
mkdir in
echo "tcp://example.com/" > in/url
echo "ssh://192.168.1.1" > in/url2
echo "http://www.example.com:80/foo?hi=bar" > in/url3

开始模糊测试

开始模糊测试,运行:

1
cargo afl fuzz -i in -o out target/debug/url-fuzz-target

cargo-afl 的 fuzz 子命令是使用 AFL 对 Rust 代码进行模糊测试的主要接口。熟悉 AFL 的用户而言,cargo-afl 的 fuzz 子命令与运行 afl-fuzz 相同。

-i 标志指定 AFL 用作种子的输入文件所在目录。

可使用 -e ext 标志按文件扩展名过滤输入。

-o 标志指定 AFL 写入所有状态与结果的目录。

最后一个参数 target/debug/url-fuzz-target 指定 AFL 将调用的模糊测试目标二进制文件,并向其标准输入提供随机字节。

运行此命令后,应立即看到 AFL 界面启动:

有关此界面及各部分含义的更多信息,请参阅 AFL 网站上的此资源。

AFL 会无限期运行,若要退出,请按 CTRL-C。

复现

从模糊器运行中收集到若干崩溃后,可手动将它们传入测试用例进行复现。通常通过 stdin 完成。例如,对 url-fuzz-target,命令为:

1
cargo afl run url-fuzz-target < out/default/crashes/crash_file

其中 out 是模糊测试命令中的 -o 参数,crash_file 是 crashes 目录中的任意文件。

最后修改 August 23, 2026: 更新 (499855b16)