Skip to content
KitploitKITPLOIT
ИнструментыБлог
Отправить
ИнструментыБлог
Отправить

Инструменты для хакинга, пентеста и кибербезопасности — ваш арсенал защиты!

Kitploit — это каталог инструментов для хакинга, кибербезопасности и пентестинга. Находите последние обновления проектов для поиска уязвимостей, анализа систем, автоматизации тестирования и усиления вашей безопасности.

··Ленты·Контакты·Конфиденциальность·© 2026 Kitploit

Каталог инструментов

Категории

Все категории
Loading categories
test-fuzz — Rust-макросы и подкоманда Cargo для автоматизации фаззинга с помощью afl.rs, включая генерацию корпуса и реализацию харнеса, интегрированные с тестовым фреймворком Rust. | Kitploit
Инструменты/GitHubGitHub/trailofbits/test-fuzz
Динамический анализ (песочница)Анализ уязвимостейАнализ КодаФаззинг
GitHubtrailofbits/test-fuzz

test-fuzz

Rust-макросы и подкоманда Cargo для автоматизации фаззинга с помощью afl.rs, включая генерацию корпуса и реализацию харнеса, интегрированные с тестовым фреймворком Rust.

Репозиторий
2082711 дней назадПроверено Kitploit

Популярное

Смотреть все →

Откройте для себя самые используемые инструменты нашего сообщества.

Изучить все инструменты

Просмотрите нашу коллекцию инструментов

Смотреть все инструменты →
Сайт
Поделиться

test-fuzz

test-fuzz — это подкоманда Cargo и набор макросов Rust для автоматизации задач, связанных с фаззингом с помощью afl.rs, включая:

  • генерацию фаззинг-корпуса
  • реализацию фаззинг-харнеса

test-fuzz выполняет эти задачи (частично) с помощью средств тестирования Rust. Например, для генерации фаззинг-корпуса test-fuzz записывает аргументы целевой функции при каждом её вызове во время выполнения cargo test. Аналогично, test-fuzz реализует фаззинг-харнес как дополнительный тест в бинарном файле, созданном 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:~
## Обзор

Фаззинг с помощью `test-fuzz` — это, по сути, три шага:\*

1. **Определите цель фаззинга**:
   - Добавьте следующие `dependencies` в файл `Cargo.toml` целевого крейта:
     ```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` (например, ограничения трейтов) на структуру, используемую для сериализации/десериализации аргументов. Это может быть необходимо, например, если тип аргумента цели является ассоциированным типом. Пример см. в [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"`

При сериализации аргументов цели преобразуйте значения типа `X` в тип `Y`, используя реализацию `From<X>` в `Y`, или значения типа `&X` в тип `Y` с использованием реализации нестандартного трейта `test_fuzz::FromRef<X>` в `Y`. При десериализации преобразуйте эти значения обратно в тип `X`, используя реализацию нестандартного трейта `test_fuzz::Into<X>` в `Y`.

То есть использование `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. Причина использования нестандартного трейта — избежать конфликтов, которые могут возникнуть из-за бланкетных реализаций стандартных трейтов.

enable_in_production

Генерируйте файлы корпуса при запуске вне тестов, если установлена переменная окружения TEST_FUZZ_WRITE. По умолчанию файлы корпуса генерируются только при запуске тестов, независимо от того, установлена ли TEST_FUZZ_WRITE. При запуске целевой функции за пределами каталога её пакета установите TEST_FUZZ_MANIFEST_PATH в путь к файлу Cargo.toml пакета.

ПРЕДУПРЕЖДЕНИЕ: Установка enable_in_production может привести к появлению вектора отказа в обслуживании. Например, установка этой опции для функции, вызываемой много раз с разными аргументами, может заполнить диск. Проверка TEST_FUZZ_WRITE предназначена для обеспечения некоторой защиты от такой возможности. Тем не менее, прежде чем использовать эту опцию, внимательно её обдумайте.

execute_with = "function"

Вместо прямого вызова целевой функции:

  • создайте замыкание типа FnOnce() -> R, где R — возвращаемый тип целевой функции, так чтобы вызов замыкания вызывал целевую функцию;
  • вызовите function с этим замыканием.

Вызов целевой функции таким образом позволяет function настроить окружение вызова. Это может быть полезно, например, для фаззинга [экстерналий Substrate].

no_auto_generate

Не пытайтесь [автоматически генерировать файлы корпуса] для целевой функции.

only_generic_args

Записывайте обобщённые аргументы целевой функции при запуске тестов, но не генерируйте файлы корпуса и не реализуйте фаззинг-харнес. Это может быть полезно, когда целевая функция является обобщённой, но неясно, какие параметры типа следует использовать для фаззинга.

Предполагаемый рабочий процесс: включите only_generic_args, затем выполните cargo test, а затем cargo test-fuzz --display generic-args. Один из полученных обобщённых аргументов может подойти в качестве parameters для generic_args. Аналогично, обобщённые аргументы, полученные в результате cargo test-fuzz --display impl-generic-args, могут подойти в качестве parameters для impl_generic_args.

Обратите внимание, однако, что сам по себе факт вызова целевой функции с определёнными параметрами во время тестов не означает, что аргументы целевой функции сериализуемы/десериализуемы при использовании этих параметров. Результаты --display generic-args/--display impl-generic-args носят лишь ориентировочный характер.

rename = "name"

Считайте, что целевая функция называется name, при добавлении модуля в охватывающую область видимости. Раскрытие макроса test_fuzz добавляет определение модуля в охватывающую область видимости. По умолчанию модуль называется следующим образом:

  • Если целевая функция не встречается в блоке impl, модуль называется target_fuzz__, где target — имя целевой функции.
  • Если целевая функция встречается в блоке impl, модуль называется path_target_fuzz__, где path — последний сегмент пути типа Self из блока impl.

Однако использование этой опции приводит к тому, что модуль вместо этого называется 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] к аргументам функций. Это даёт ещё один инструмент для работы со сложными типами.

Ниже приведён пример. Трейты `serde::Serialize` и `serde::Deserialize` не могут быть выведены для `Context`, поскольку он содержит `Mutex`. Однако `Context` реализует `Default`. Поэтому применение `#[serde(skip)]` к аргументу `Context` приводит к тому, что он пропускается при сериализации и принимает своё значение по умолчанию при десериализации.```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 conversions on the argument.

Макрос test_fuzz_impl

Каждый раз, когда макрос test_fuzz используется в блоке impl, перед impl должен находиться макрос test_fuzz_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 используется для взаимодействия с fuzz-целями, а также для управления их корпусами, сбоями (crashes), зависаниями (hangs) и рабочими очередями. Примеры вызова:

  1. Вывести список fuzz-целей ``` 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 при сериализации.
  • Инициализировать значения типа $ty с помощью $expr при десериализации.

Если точнее, 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` является unit-структурой, то `$expr` можно опустить. То есть `dont_care!($ty)` эквивалентно `dont_care!($ty, $ty)`.

#### `leak!`

Макрос `leak!` может помочь сериализовать аргументы целевой функции, которые являются ссылками и типы которых реализуют трейт [`ToOwned`]. Его следует использовать вместе с опцией [`convert`].

В частности, вызов следующей формы объявляет тип `LeakedX` и реализует для него трейты `From` и `test_fuzz::Into`:```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` в целом. Включите их в спецификации зависимостей `test-fuzz`, как описано в [The Cargo Book]. Например, чтобы включить возможность `cast_checks`, используйте:```toml
test-fuzz = { version = "*", features = ["cast_checks"] }

Пакет test-fuzz в настоящее время поддерживает следующие возможности:

cast_checks

Используйте cast_checks для автоматической проверки целевых функций на наличие недопустимых приведений типов.

Обратите внимание, что эта возможность включает cast_checks только для функций, аннотированных test_fuzz macro, а не для функций, которые они вызывают.

Форматы Serde

test-fuzz может сериализовать аргументы целей в нескольких форматах Serde. Ниже перечислены возможности, используемые для выбора формата.

  • serde_postcard - Postcard (по умолчанию)

  • serde_bincode - Bincode

Автоматически генерируемые файлы корпуса

cargo-test-fuzz может автоматически генерировать значения для типов, реализующих определённые трейты. Если все типы аргументов цели реализуют такие трейты, cargo-test-fuzz может автоматически генерировать файлы корпуса для цели.

Трейты, которые cargo-test-fuzz в настоящее время поддерживает, и значения, генерируемые для них, приведены ниже:

Trait(s)Value(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()

Обозначения

  • 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
  • Two - test_fuzz::runtime::traits::Two (по сути Add + One)

Переменные окружения

TEST_FUZZ_LOG

Во время раскрытия макроса:

  • Если TEST_FUZZ_LOG установлен в 1, выводите все инструментированные фаззинг-цели и определения модулей в стандартный вывод.
  • Если TEST_FUZZ_LOG установлен в имя крейта, выводите инструментированные фаззинг-цели и определения модулей этого крейта в стандартный вывод.

Это может быть полезно для отладки.

TEST_FUZZ_MANIFEST_PATH

При запуске цели извне каталога её пакета файл Cargo.toml пакета ищется по этому пути. Возможно, эту переменную окружения потребуется установить при использовании enable_in_production.

TEST_FUZZ_WRITE

Генерирует файлы корпуса, когда тесты не запускаются, для тех целей, для которых установлен enable_in_production.

Ограничения

Клонируемые аргументы

Аргументы цели должны реализовывать трейт Clone. Причина такого требования в том, что аргументы нужны в двух местах: во внутренней функции test-fuzz, которая записывает файлы корпуса, и в теле целевой функции. Чтобы разрешить этот конфликт, аргументы клонируются перед передачей во внутреннюю функцию, записывающую файлы корпуса.

Сериализуемые / десериализуемые аргументы

В общем случае аргументы цели должны реализовывать трейты serde::Serialize и serde::Deserialize, например, с помощью deriving them. Мы говорим «в общем случае», потому что test-fuzz умеет обрабатывать определённые особые случаи, которые в обычных условиях не были бы сериализуемыми/десериализуемыми. Например, аргумент типа &str при сериализации преобразуется в String, а при десериализации — обратно в &str. См. также generic_args и impl_generic_args выше.

Глобальные переменные

Фаззинг-харнессы, которые реализует test-fuzz, не инициализируют глобальные переменные. Хотя execute_with отчасти помогает, это не является полным решением. В общем случае фаззинг функции, которая полагается на глобальные переменные, требует специальных (ad-hoc) методов.

convert и generic_args / impl_generic_args

Эти опции несовместимы в следующем смысле. Если тип аргумента фаззинг-цели является параметром типа, convert будет пытаться сопоставить параметр типа, а не тип, которым этот параметр задан. Поддержка второго, по-видимому, потребовала бы имитации подстановки типов, которую выполнял бы компилятор. Однако в настоящее время это не реализовано.

Советы и рекомендации

  • #[cfg(test)] is not enabled для интеграционных тестов. Если ваша цель тестируется только интеграционными тестами, рассмотрите возможность использования enable_in_production и TEST_FUZZ_WRITE для генерации корпуса. (Однако обратите внимание на предупреждение, сопровождающее enable_in_production.)

  • Если вы знаете пакет, в котором находится ваша цель, передача -p <package> в cargo test/cargo test-fuzz может значительно сократить время сборки. Аналогично, если вы знаете, что ваша цель вызывается только из одного интеграционного теста, передача --test <name> может сократить время сборки.

  • Rust won't allow you to реализовать serde::Serialize для типов из других репозиториев. Но вы можете patch другие репозитории, чтобы сделать их типы сериализуемыми. Кроме того, cargo-clone может быть полезен для получения репозиториев зависимостей.

  • Serde attributes могут быть полезны при реализации serde::Serialize/serde::Deserialize для сложных типов.

Политика семантического версионирования

Мы оставляем за собой право изменять формат корпусов, сбоев, зависаний и рабочих очередей и считать такие изменения не ломающими совместимость.

Лицензия

test-fuzz лицензирован и распространяется под лицензией AGPLv3 с исключением Macros and Inline Functions Exception. Простыми словами, использование test_fuzz macro, test_fuzz_impl macro или convenience functions and macros библиотеки test-fuzz в вашем программном обеспечении не требует, чтобы оно распространялось под лицензией AGPLv3.