
Macro Rust e sottocomando Cargo per automatizzare il fuzzing con afl.rs, inclusa la generazione di corpus e l'implementazione di harness, integrati con il framework di test di Rust.
test-fuzz è un sottocomando di Cargo e una raccolta di macro Rust per automatizzare alcune attività relative al fuzzing con [afl.rs], tra cui:
test-fuzz realizza queste attività (in parte) utilizzando le strutture di test di Rust. Ad esempio, per generare un corpus di fuzzing, test-fuzz registra gli argomenti di un target ogni volta che viene chiamato durante un'invocazione di cargo test. Analogamente, test-fuzz implementa un harness di fuzzing come test aggiuntivo in un binario generato da cargo-test. Questa stretta integrazione con le strutture di test di Rust è ciò che motiva il nome test-fuzz.
Contenuti
test_fuzz macro]test_fuzz_impl macro]cargo test-fuzz comando]test-fuzz]Installa cargo-test-fuzz e [afl.rs] con il seguente comando:```sh
cargo install cargo-test-fuzz cargo-afl
## Panoramica
Il fuzzing con `test-fuzz` consiste essenzialmente in tre passaggi:\*
1. **Identificare un target di fuzzing**:
- Aggiungere le seguenti `dependencies` al file `Cargo.toml` della crate target:
```toml
serde = "*"
test-fuzz = "*"
```
- Anteporre alla funzione target la macro [`test_fuzz`]:
```rust
#[test_fuzz::test_fuzz]
fn foo(...) {
...
}
```
2. **Generare un corpus** eseguendo `cargo test`: ```
cargo test
cargo test-fuzz]: ```
cargo test-fuzz foo
* Potrebbe essere necessario un ulteriore passaggio preliminare dopo un riavvio:```sh cargo afl system-config
Si noti che il comando precedente esegue `sudo` internamente. Pertanto, potrebbe esserti richiesto di inserire la password.
## Componenti
### Macro `test_fuzz`
Anteporre la macro `test_fuzz` a una funzione indica che la funzione è un fuzz target.
Gli effetti principali della macro `test_fuzz` sono:
- Aggiunge strumentazione al target per serializzare i suoi argomenti e scriverli in un file di corpus ogni volta che il target viene chiamato. La strumentazione è protetta da `#[cfg(test)]`, così che i file di corpus vengano generati solo durante l'esecuzione dei test (vedi comunque [`enable_in_production`] di seguito).
- Aggiunge un test che legge e deserializza gli argomenti dallo standard input e applica il target agli stessi. Il test controlla una variabile d'ambiente, impostata da [`cargo test-fuzz`], in modo che il test non si blocchi tentando di leggere dallo standard input durante una normale invocazione di `cargo test`. Il test è racchiuso in un modulo per ridurre la probabilità di una collisione di nomi. Attualmente, il nome del modulo è `target_fuzz`, dove `target` è il nome del target (vedi comunque [`rename`] di seguito).
#### Argomenti
##### `bounds = "where_predicates"`
Impone `where_predicates` (ad esempio, trait bounds) sulla struct usata per serializzare/deserializzare gli argomenti. Questo può essere necessario, ad esempio, se il tipo di un argomento del target è un tipo associato. Per un esempio, vedere [associated_type.rs] in questo repository.
##### `generic_args = "parameters"`
Usa `parameters` come parametri di tipo del target durante il fuzzing. Esempio:```rust
#[test_fuzz(generic_args = "String")]
fn foo<T: Clone + Debug + Serialize>(x: &T) {
...
}
Nota: gli argomenti del target devono essere serializzabili per ogni istanziazione dei suoi parametri di tipo. Ma gli argomenti del target devono essere deserializzabili solo quando il target è istanziato con parameters.
impl_generic_args = "parameters"Usa parameters come parametri di tipo Self del target durante il fuzzing. Esempio:```rust
#[test_fuzz_impl]
impl<T: Clone + Debug + Serialize> for Foo {
#[test_fuzz(impl_generic_args = "String")]
fn bar(&self, x: &T) {
...
}
}
Nota: gli argomenti del target devono essere serializzabili per **ogni** istanziazione dei parametri di tipo `Self`. Ma gli argomenti del target devono essere deserializzabili solo quando il `Self` del target è istanziato con `parameters`.
##### `convert = "X, Y"`
Quando si serializzano gli argomenti del target, convertire i valori di tipo `X` in tipo `Y` usando l'implementazione di `From<X>` per `Y`, oppure quelli di tipo `&X` in tipo `Y` usando l'implementazione del trait non standard `test_fuzz::FromRef<X>` per `Y`. Quando si deserializzano, riconvertire quei valori al tipo `X` usando l'implementazione del trait non standard `test_fuzz::Into<X>` per `Y`.
Cioè, l'uso di `convert = "X, Y"` deve essere accompagnato da determinate implementazioni. Se `X` implementa [`Clone`], allora `Y` può implementare quanto segue:```rust
impl From<X> for Y {
fn from(x: X) -> Self {
...
}
}
Se X non implementa [Clone], allora Y deve implementare quanto segue:```rust
impl test_fuzz::FromRef for Y {
fn from_ref(x: &X) -> Self {
...
}
}
Inoltre, `Y` deve implementare quanto segue (indipendentemente dal fatto che `X` implementi [`Clone`]):```rust
impl test_fuzz::Into<X> for Y {
fn into(self) -> X {
...
}
}
The definition of test_fuzz::Into is identical to that of [std::convert::Into]. The reason for using a non-standard trait is to avoid conflicts that could arise from blanket implementations of standard traits.
enable_in_productionGenerate corpus files when not running tests, provided the environment variable [TEST_FUZZ_WRITE] is set. The default is to generate corpus files only when running tests, regardless of whether [TEST_FUZZ_WRITE] is set. When running a target from outside its package directory, set [TEST_FUZZ_MANIFEST_PATH] to the path of the package's Cargo.toml file.
WARNING: Setting enable_in_production could introduce a denial-of-service vector. For example, setting this option for a function that is called many times with different arguments could fill up the disk. The check of [TEST_FUZZ_WRITE] is meant to provide some defense against this possibility. Nonetheless, consider this option carefully before using it.
execute_with = "function"Rather than call the target directly:
FnOnce() -> R, where R is the target's return type, so that calling the closure calls the target;function with the closure.Calling the target in this way allows function to set up the call's environment. This can be useful, e.g., for fuzzing [Substrate externalities].
no_auto_generateDo not try to [auto-generate corpus files] for the target.
only_generic_argsRecord the target's generic args when running tests, but do not generate corpus files and do not implement a fuzzing harness. This can be useful when the target is a generic function, but it is unclear what type parameters should be used for fuzzing.
The intended workflow is: enable only_generic_args, then run cargo test followed by cargo test-fuzz --display generic-args. One of the resulting generic args might be usable as generic_args's parameters. Similarly, generic args resulting from cargo test-fuzz --display impl-generic-args might be usable as impl_generic_args's parameters.
Note, however, that just because a target was called with certain parameters during tests, it does not imply the target's arguments are serializable/deserializable when those parameters are used. The results of --display generic-args/--display impl-generic-args are merely suggestive.
rename = "name"