Skip to content
KitploitKITPLOIT
工具博客
提交
工具博客
提交

黑客、渗透测试和网络安全工具,武装您的安全武器库!

Kitploit 是一个黑客、网络安全和渗透测试工具的目录。发现最新的项目更新,查找漏洞、分析系统、自动化测试并加强你的安全。

··订阅源·联系·隐私·© 2026 Kitploit

工具目录

分类

查看所有分类
Loading categories
test-fuzz — Rust 宏和 Cargo 子命令,用于通过 afl.rs 自动化模糊测试,包括语料库生成和 harness 实现,并与 Rust 的测试框架集成。 | Kitploit
工具/GitHubGitHub/trailofbits/test-fuzz
动态分析 (沙盒)漏洞分析代码分析模糊测试
GitHubtrailofbits/test-fuzz

test-fuzz

Rust 宏和 Cargo 子命令,用于通过 afl.rs 自动化模糊测试,包括语料库生成和 harness 实现,并与 Rust 的测试框架集成。

查看仓库网站
2082711天前Kitploit 审核通过

最受欢迎

查看全部 →

发现我们社区最常用的工具。

探索所有工具

浏览我们的工具集合

查看所有工具 →
分享

test-fuzz

test-fuzz 是一个 Cargo 子命令,也是一组 Rust 宏,用于自动化与使用 afl.rs 进行模糊测试相关的某些任务,包括:

  • 生成模糊测试语料库
  • 实现模糊测试 harness

test-fuzz(部分地)借助 Rust 的测试机制完成这些任务。例如,为了生成模糊测试语料库,test-fuzz 会在 cargo test 调用期间每次调用目标函数时记录其参数。类似地,test-fuzz 将模糊测试 harness 实现为 cargo-test 生成的二进制文件中的附加测试。正是这种与 Rust 测试机制的紧密集成,才有了 test-fuzz 这个名字。

目录

  1. [安装]
  2. [概述]
  3. [组件]
    • [test_fuzz 宏]
    • [test_fuzz_impl 宏]
    • [cargo test-fuzz 命令]
    • [便捷函数和宏]
  4. [test-fuzz 包特性]
  5. [自动生成的语料库文件]
  6. [环境变量]
  7. [限制]
  8. [技巧与提示]
  9. [语义化版本策略]
  10. [许可协议]

安装

使用以下命令安装 cargo-test-fuzz 和 afl.rs:```sh cargo install cargo-test-fuzz cargo-afl

root@kitploit:~
## Overview

使用 `test-fuzz` 进行模糊测试本质上分为三个步骤:\*

1. **识别模糊测试目标**:
   - 在目标 crate 的 `Cargo.toml` 文件中添加以下 `dependencies`:
     ```toml
     serde = "*"
     test-fuzz = "*"
     ```
   - 在目标函数前加上 [`test_fuzz`] 宏:
     ```rust
     #[test_fuzz::test_fuzz]
     fn foo(...) {
         ...
     }
     ```
2. **通过运行 `cargo test` 生成语料库**:   ```
   cargo test
  1. 对目标进行模糊测试,通过运行 cargo test-fuzz: ``` cargo test-fuzz foo
    root@kitploit:~

* 重启后可能还需要一个额外的预备步骤:```sh cargo afl system-config

root@kitploit:~
请注意,上述命令会在内部运行 `sudo`。因此,系统可能会提示你输入密码。

## 组件

### `test_fuzz` 宏

在函数前加上 `test_fuzz` 宏,表示该函数是一个模糊测试目标。

`test_fuzz` 宏的主要作用包括:

- 为测试目标添加插桩,使其在每次被调用时序列化参数并将它们写入语料库文件。该插桩由 `#[cfg(test)]` 保护,因此仅在运行测试时才会生成语料库文件(不过,请参见下面的 [`enable_in_production`])。
- 添加一个测试,从标准输入读取并反序列化参数,然后将目标应用于这些参数。该测试会检查由 [`cargo test-fuzz`] 设置的环境变量,以便在正常执行 `cargo test` 时不会因尝试从标准输入读取而阻塞。该测试被封装在一个模块中,以降低名称冲突的可能性。目前,该模块的名称为 `target_fuzz`,其中 `target` 是目标的名称(不过,请参见下面的 [`rename`])。

#### 参数

##### `bounds = "where_predicates"`

对用于序列化/反序列化参数的结构体施加 `where_predicates`(例如 trait 约束)。这可能是必要的,例如,当目标参数类型是关联类型时。有关示例,请参见此仓库中的 [associated_type.rs]。

##### `generic_args = "parameters"`

在模糊测试时使用 `parameters` 作为目标的类型参数。示例:```rust
#[test_fuzz(generic_args = "String")]
fn foo<T: Clone + Debug + Serialize>(x: &T) {
    ...
}

注意:目标的参数必须对其类型参数的每一次实例化都是可序列化的。但只有当目标以 parameters 实例化时,目标的参数才需要是可反序列化的。

impl_generic_args = "parameters"

在模糊测试时,使用 parameters 作为目标的 Self 类型参数。示例:```rust #[test_fuzz_impl] impl<T: Clone + Debug + Serialize> for Foo { #[test_fuzz(impl_generic_args = "String")] fn bar(&self, x: &T) { ... } }

root@kitploit:~
注意:目标的参数必须在其 `Self` 类型参数的 **每一次** 实例化时都是可序列化的。但仅当目标的 `Self` 使用 `parameters` 实例化时,目标的参数才需要是可反序列化的。

##### `convert = "X, Y"`

在序列化目标的参数时,使用 `Y` 对 `From<X>` 的实现将类型 `X` 的值转换为类型 `Y`,或者使用 `Y` 对非标准 trait `test_fuzz::FromRef<X>` 的实现将类型 `&X` 的值转换为类型 `Y`。在反序列化时,使用 `Y` 对非标准 trait `test_fuzz::Into<X>` 的实现将这些值转换回类型 `X`。

也就是说,使用 `convert = "X, Y"` 必须伴随特定的实现。如果 `X` 实现了 [`Clone`],那么 `Y` 可以实现以下内容:```rust
impl From<X> for Y {
    fn from(x: X) -> Self {
        ...
    }
}

如果 X 没有实现 Clone,那么 Y 必须实现以下内容:```rust impl test_fuzz::FromRef for Y { fn from_ref(x: &X) -> Self { ... } }

root@kitploit:~
此外,`Y` 必须实现以下内容(无论 `X` 是否实现了 [`Clone`]):```rust
impl test_fuzz::Into<X> for Y {
    fn into(self) -> X {
        ...
    }
}

test_fuzz::Into 的定义与 std::convert::Into 相同。使用非标准 trait 的原因是为了避免标准 trait 的覆盖实现可能引发的冲突。

enable_in_production

当不在运行测试时,如果设置了环境变量 TEST_FUZZ_WRITE,则生成 corpus 文件。默认行为是仅在运行测试时生成 corpus 文件,无论是否设置了 TEST_FUZZ_WRITE。当从包目录之外运行目标时,请将 TEST_FUZZ_MANIFEST_PATH 设置为该包的 Cargo.toml 文件的路径。

警告:设置 enable_in_production 可能会引入拒绝服务攻击向量。例如,为某个使用不同参数被多次调用的函数设置此选项,可能会占满磁盘空间。对 TEST_FUZZ_WRITE 的检查旨在对此提供一定防御。尽管如此,请在使用前仔细考虑此选项。

execute_with = "function"

不直接调用目标,而是:

  • 构造一个类型为 FnOnce() -> R 的闭包,其中 R 是目标的返回类型,这样调用该闭包就等于调用目标;
  • 使用该闭包调用 function。

以这种方式调用目标,可以让 function 为调用设置环境。例如,这对于对 Substrate externalities 进行模糊测试很有用。

no_auto_generate

不要尝试为目标 auto-generate corpus files。

only_generic_args

在运行测试时记录目标的泛型实参,但不生成 corpus 文件,也不实现模糊测试 harness。当目标是泛型函数但还不清楚应使用哪些类型参数进行模糊测试时,这很有用。

预期的工作流程是:启用 only_generic_args,然后依次运行 cargo test 和 cargo test-fuzz --display generic-args。得到的某个泛型实参可能可以作为 generic_args 的 parameters 使用。类似地,从 cargo test-fuzz --display impl-generic-args 得到的泛型实参可能可以用作 impl_generic_args 的 parameters。

但请注意,仅仅因为目标在测试期间使用某些参数被调用,并不意味着当使用这些参数时目标的参数是可序列化/可反序列化的。--display generic-args/--display impl-generic-args 的结果仅供参考。

rename = "name"

在向封闭作用域添加模块时,将目标视为其名称为 name。test_fuzz 宏的展开会在封闭作用域中添加一个模块定义。默认情况下,该模块的命名如下:

  • 如果目标不位于 impl 块中,则模块命名为 target_fuzz__,其中 target 是目标的名称。
  • 如果目标位于 impl 块中,则模块命名为 path_target_fuzz__,其中 path 是 impl 的 Self 类型路径的最后一段。

然而,使用此选项会使模块改名为 name_fuzz__。示例:```rust #[test_fuzz(rename = "bar")] fn foo() {}

// Without the use of rename, a name collision and compile error would result. mod foo_fuzz__ {}

root@kitploit:~
#### 函数参数上的 Serde 字段属性

`test_fuzz` 宏允许将 [Serde 字段属性] 应用于函数参数。这为处理困难类型提供了另一种工具。

下面是一个示例。由于 `Context` 包含一个 `Mutex`,因此无法为 `Context` 派生 `serde::Serialize` 和 `serde::Deserialize` trait。但 `Context` 实现了 `Default`。因此,对 `Context` 参数应用 `#[serde(skip)]` 会导致它在序列化时被跳过,并在反序列化时采用其默认值。```rust
use std::sync::Mutex;

// Traits `serde::Serialize` and `serde::Deserialize` cannot be derived for `Context` because it
// contains a `Mutex`.
#[derive(Default)]
struct Context {
    lock: Mutex<()>,
}

impl Clone for Context {
    fn clone(&self) -> Self {
        Self {
            lock: Mutex::new(()),
        }
    }
}

#[test_fuzz::test_fuzz]
fn target(#[serde(skip)] context: Context, x: i32) {
    assert!(x >= 0);
}

Note that when Serde field attributes are applied to an argument, the test_fuzz macro performs no other [转换] on the argument.

test_fuzz_impl 宏

无论何时在 impl 块中使用 test_fuzz 宏, 都必须用 test_fuzz_impl 宏置于该 impl 之前。示例:```rust #[test_fuzz_impl] impl Foo { #[test_fuzz] fn bar(&self, x: &str) { ... } }

root@kitploit:~
这一要求的原因如下。[`test_fuzz`] 宏的展开会在所在作用域中添加一个模块定义。但是,模块定义不能出现在 `impl` 块内部。在 `impl` 之前使用 `test_fuzz_impl` 宏,会使模块被添加到 `impl` 块之外。

如果你看到类似下面的错误,很可能意味着缺少对 `test_fuzz_impl` 宏的使用:```
error: module is not supported in `trait`s or `impl`s

test_fuzz_impl 目前没有选项。

cargo test-fuzz 命令

cargo test-fuzz 命令用于与模糊测试目标进行交互,以及操作其语料库、崩溃、挂起和工作队列。示例调用包括:

  1. 列出模糊测试目标 ``` cargo test-fuzz --list
    root@kitploit:~
  2. 显示目标 foo 的语料库 ``` cargo test-fuzz foo --display corpus
    root@kitploit:~
  3. 模糊测试目标 foo ``` cargo test-fuzz foo
    root@kitploit:~
  4. 为目标 foo 重放发现的崩溃 ``` cargo test-fuzz foo --replay crashes
    root@kitploit:~

用法```

Usage: cargo test-fuzz [OPTIONS] [TARGETNAME] [-- ...]

Arguments: [TARGETNAME] String that fuzz target's name must contain [ARGS]... Arguments for the fuzzer

Options: --backtrace Display backtraces --consolidate Move one target's crashes, hangs, and work queue to its corpus; to consolidate all targets, use --consolidate-all --coverage Generate coverage for corpus, crashes, hangs, or work queue. Note that generating coverage for instrumented fuzz targets is not supported. --cpus Fuzz using at most cpus; default is all but one --display Display corpus, crashes, generic args, impl generic args, hangs, or work queue. By default, an uninstrumented fuzz target is used. To display with instrumentation, append -instrumented to , e.g., --display corpus-instrumented. --exact Target name is an exact name rather than a substring --exit-code Exit with 0 if the time limit was reached, 1 for other programmatic aborts, and 2 if an error occurred; implies --no-ui, does not imply --run-until-crash or --max-total-time --features Space or comma separated list of features to activate --list List fuzz targets --manifest-path Path to Cargo.toml --max-total-time Fuzz at most of time (equivalent to -- -V ) --no-default-features Do not activate the default feature --no-run Compile, but don't fuzz --no-ui Disable user interface -p, --package Package containing fuzz target --persistent Enable persistent mode fuzzing --pretty Pretty-print debug output when generating coverage, displaying, or replaying --release Build in release mode --replay Replay corpus, crashes, hangs, or work queue. By default, an uninstrumented fuzz target is used. To replay with instrumentation, append -instrumented to , e.g., --replay corpus-instrumented. --reset Clear fuzzing data for one target, but leave corpus intact; to reset all targets, use --reset-all --resume Resume target's last fuzzing session --run-until-crash Stop fuzzing once a crash is found --slice If there are not sufficiently many cpus to fuzz all targets simultaneously, fuzz them in intervals of [default: 1200] --test Integration test containing fuzz target --timeout Number of seconds to consider a hang when fuzzing or replaying (equivalent to -- -t <TIMEOUT * 1000> when fuzzing) --verbose Show build output when generating coverage, displaying, or replaying -h, --help Print help -V, --version Print version

下载工具

Try cargo afl fuzz --help to see additional fuzzer options.

root@kitploit:~
使用 `--display` 选项时,目标程序写入 stderr 的任何输出都会显示出来。这包括来自 `eprintln!` 语句的输出,以及来自 `dbg!` 等调试宏的输出。这在处理特定输入时,有助于理解代码中发生的情况。

`--display` 和 `--replay` 选项可以同时传递,使您能够在单个命令中同时查看和重放语料库条目,例如:```
cargo test-fuzz foo --display corpus --replay corpus

便捷函数与宏

警告: 这些实用工具不受语义化版本控制约束,并可能在 test-fuzz 的未来版本中被移除。

dont_care!

dont_care! 宏可用于为那些易于构造且你不关心记录其值的类型实现 serde::Serialize/serde::Deserialize。直观地说,dont_care!($ty, $expr) 的含义是:

  • 序列化时跳过类型为 $ty 的值。
  • 反序列化时使用 $expr 初始化类型为 $ty 的值。

更具体地说,dont_care!($ty, $expr) 会展开为以下内容:```rust impl serde::Serialize for $ty { fn serialize(&self, serializer: S) -> std::result::Result<S::Ok, S::Error> where S: serde::Serializer, { ().serialize(serializer) } }

impl<'de> serde::Deserialize<'de> for $ty { fn deserialize(deserializer: D) -> std::result::Result<Self, D::Error> where D: serde::Deserializer<'de>, { <()>::deserialize(deserializer).map(|_| $expr) } }

root@kitploit:~
如果 `$ty` 是单元结构体,则可以省略 `$expr`。也就是说,`dont_care!($ty)` 等价于 `dont_care!($ty, $ty)`。

#### `leak!`

`leak!` 宏可以帮助序列化那些是引用且其类型实现了 [`ToOwned`] trait 的目标参数。它旨在与 [`convert`] 选项一起使用。

具体来说,以下形式的调用会声明一个类型 `LeakedX`,并为其实现 `From` 和 `test_fuzz::Into` trait:```rust
leak!(X, LeakedX);

然后可以使用 LeakedX 的 convert 选项,如下所示:```rust #[test_fuzz::test_fuzz(convert = "&X, LeakedX")

root@kitploit:~
一个 `X` 为 [`Path`] 的示例位于本仓库的 [conversion.rs] 中。

更一般地,形如 `leak!($ty, $ident)` 的调用会展开为以下内容:```rust
#[derive(Clone, std::fmt::Debug, serde::Deserialize, serde::Serialize)]
struct $ident(<$ty as ToOwned>::Owned);

impl From<&$ty> for $ident {
    fn from(ty: &$ty) -> Self {
        Self(ty.to_owned())
    }
}

impl test_fuzz::Into<&$ty> for $ident {
    fn into(self) -> &'static $ty {
        Box::leak(Box::new(self.0))
    }
}

serialize_ref / deserialize_ref

serialize_ref 和 deserialize_ref 的功能类似于 leak!,但它们旨在分别与 Serde 的 serialize_with 和 deserialize_with 字段属性一起使用。```rust fn serialize_ref<S, T>(x: &&T, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer, T: serde::Serialize, { ::serialize(*x, serializer) }

fn deserialize_ref<'de, D, T>(deserializer: D) -> Result<&'static T, D::Error> where D: serde::Deserializer<'de>, T: serde:🇩🇪:DeserializeOwned + std::fmt::Debug, { let x = ::deserialize(deserializer)?; Ok(Box::leak(Box::new(x))) }

root@kitploit:~
#### `serialize_ref_mut` / `deserialize_ref_mut`

`serialize_ref_mut` 和 `deserialize_ref_mut` 分别与 `serialize_ref` 和 `deserialize_ref` 类似,区别在于它们操作的是可变引用,而非不可变引用。

## `test-fuzz` 包特性

本节中的特性适用于整个 `test-fuzz` 包。按照 [The Cargo Book] 中的描述,在 `test-fuzz` 的依赖规格中启用它们。例如,要启用 `cast_checks` 特性,请使用:```toml
test-fuzz = { version = "*", features = ["cast_checks"] }

The test-fuzz package currently supports the following features:

cast_checks

Use cast_checks to automatically check target functions for invalid casts.

Note that this feature enables cast_checks only for functions annotated with the test_fuzz macro, not for the functions they call.

Serde 格式

test-fuzz 可以使用多种 Serde 格式序列化目标参数。以下特性用于选择格式。

  • serde_postcard - Postcard(默认)

  • serde_bincode - Bincode

自动生成的语料库文件

cargo-test-fuzz 可以为实现了某些 trait 的类型自动生成值。如果目标的所有参数类型都实现了这些 trait,cargo-test-fuzz 就可以为该目标自动生成语料库文件。

cargo-test-fuzz 当前支持的 trait 以及为它们生成的值如下:

图例

  • Add - core::ops::Add
  • Bounded - num_traits::bounds::Bounded
  • Default - std::default::Default
  • Div - core::ops::Div
  • One - num_traits::One
  • Sub - core::ops::Sub

环境变量

TEST_FUZZ_LOG

在宏展开期间:

  • 如果 TEST_FUZZ_LOG 被设置为 1,则将所有已插桩的模糊测试目标和模块定义写入标准输出。
  • 如果 TEST_FUZZ_LOG 被设置为某个 crate 名称,则将该 crate 中已插桩的模糊测试目标和模块定义写入标准输出。

这在调试时可能很有用。

TEST_FUZZ_MANIFEST_PATH

当从包目录之外运行目标时,在此位置查找该包的 Cargo.toml 文件。使用 enable_in_production 时,可能需要设置此环境变量。

TEST_FUZZ_WRITE

对于设置了 enable_in_production 的目标,在不运行测试时为其生成语料库文件。

限制

可克隆的参数

目标的参数必须实现 Clone trait。这一要求的原因是,参数在两个地方都需要使用:在 test-fuzz 内部一个写入语料库文件的函数中,以及在目标函数的函数体中。为了解决这一冲突,参数会在传递给前者之前被克隆。

可序列化 / 可反序列化的参数

通常情况下,目标的参数必须实现 serde::Serialize 和 serde::Deserialize trait,例如通过派生它们。我们说“通常情况下”,是因为 test-fuzz 知道如何处理某些通常无法序列化/反序列化的特殊情况。例如,类型为 &str 的参数在序列化时会转换为 String,在反序列化时再转换回 &str。另请参阅上文中的 generic_args 和 impl_generic_args。

全局变量

test-fuzz 实现的模糊测试框架(harness)不会初始化全局变量。虽然 execute_with 提供了一些补救措施,但它并不是完整的解决方案。一般来说,对依赖全局变量的函数进行模糊测试需要采用临时(ad-hoc)方法。

convert 与 generic_args / impl_generic_args

这些选项在以下意义上互不兼容:如果模糊测试目标的参数类型是类型参数,convert 会尝试匹配类型参数本身,而不是该参数所设置的具体类型。要支持后者,似乎需要模拟编译器所执行的那种类型替换。然而,目前尚未实现这一点。

提示与技巧

  • #[cfg(test)] 在集成测试中不会被启用。如果您的目标仅由集成测试测试,请考虑使用 enable_in_production 和 TEST_FUZZ_WRITE 生成语料库。(不过,请注意 enable_in_production 附带的警告。)

  • 如果您知道目标所在的包,将 -p <package> 传给 cargo test/cargo test-fuzz 可以显著减少构建时间。同样,如果您知道目标只由一个集成测试调用,传入 --test <name> 也可以减少构建时间。

  • Rust 不允许您为其他仓库中的类型实现 serde::Serialize。但您也许可以对其他仓库应用补丁,使其类型可序列化。此外, 对于获取依赖项的仓库也很有用。

语义化版本策略

我们保留更改语料库、崩溃、挂起和工作队列格式的权利,并有权将这些更改视为非破坏性更改。

许可证

test-fuzz 依据 AGPLv3 许可证获得授权并进行分发,且附带宏与内联函数例外。简而言之,在您的软件中使用 test_fuzz 宏、test_fuzz_impl 宏 或 test-fuzz 的便捷函数和宏,并不要求其受 AGPLv3 许可证约束。

Trait(s)值
BoundedT::min_value(), T::max_value()
Bounded + Add + OneT::min_value() + T::one()
Bounded + Add + Div + TwoT::min_value() / T::two() + T::max_value() / T::two()
Bounded + Add + Div + Two + OneT::min_value() / T::two() + T::max_value() / T::two() + T::one()
Bounded + Sub + OneT::max_value() - T::one()
DefaultT::default()
  • Two - test_fuzz::runtime::traits::Two(本质上是 Add + One)
  • cargo-clone
  • Serde 属性 有助于为复杂的类型实现 serde::Serialize/serde::Deserialize。