
Rust-Makros und Cargo-Subcommand zur Automatisierung von Fuzzing mit afl.rs, einschließlich Korpusgenerierung und Harness-Implementierung, integriert in Rusts Testframework.
test-fuzz ist ein Cargo-Subcommand und eine Sammlung von Rust-Makros, um bestimmte Aufgaben im Zusammenhang mit Fuzzing mit [afl.rs] zu automatisieren, darunter:
test-fuzz erreicht dies (teilweise) mithilfe der Testfunktionen von Rust. Um beispielsweise ein Fuzzing-Korpus zu generieren, zeichnet test-fuzz die Argumente eines Ziels jedes Mal auf, wenn es während eines Aufrufs von cargo test aufgerufen wird. Ebenso implementiert test-fuzz eine Fuzzing-Harness als zusätzlichen Test in einem von cargo-test generierten Binary. Diese enge Integration mit den Testfunktionen von Rust ist der Grund für den Namen test-fuzz.
Inhaltsverzeichnis
test_fuzz-Makro]test_fuzz_impl-Makro]cargo test-fuzz-Befehl]test-fuzz-Paket-Features]Installiere cargo-test-fuzz und [afl.rs] mit dem folgenden Befehl:```sh
cargo install cargo-test-fuzz cargo-afl
## Übersicht
Fuzzing mit `test-fuzz` besteht im Wesentlichen aus drei Schritten:\*
1. **Identifiziere ein Fuzz-Ziel**:
- Füge die folgenden `dependencies` zur `Cargo.toml`-Datei des Ziel-Crates hinzu:
```toml
serde = "*"
test-fuzz = "*"
```
- Setze das [`test_fuzz`]-Makro vor die Zielfunktion:
```rust
#[test_fuzz::test_fuzz]
fn foo(...) {
...
}
```
2. **Generiere einen Korpus**, indem du `cargo test` ausführst: ```
cargo test
cargo test-fuzz] ausführst: ```
cargo test-fuzz foo
* Ein zusätzlicher, vorbereitender Schritt kann nach einem Neustart erforderlich sein:```sh cargo afl system-config
Note that the above command runs `sudo` internally. Hence, you may be prompted to enter your password.
## Komponenten
### `test_fuzz`-Makro
Das Voranstellen des `test_fuzz`-Makros vor eine Funktion kennzeichnet diese als Fuzz-Target.
Die Hauptwirkungen des `test_fuzz`-Makros sind:
- Fügt dem Target Instrumentierung hinzu, um seine Argumente zu serialisieren und bei jedem Aufruf des Targets in eine Corpus-Datei zu schreiben. Die Instrumentierung ist durch `#[cfg(test)]` geschützt, sodass Corpus-Dateien nur bei der Ausführung von Tests erzeugt werden (siehe jedoch [`enable_in_production`] unten).
- Fügt einen Test hinzu, der Argumente von der Standardeingabe liest und deserialisiert und das Target darauf anwendet. Der Test prüft eine Umgebungsvariable, die von [`cargo test-fuzz`] gesetzt wird, sodass der Test während eines normalen Aufrufs von `cargo test` nicht versucht, blockierend von der Standardeingabe zu lesen. Der Test ist in ein Modul eingebettet, um die Wahrscheinlichkeit einer Namenskollision zu verringern. Derzeit lautet der Name des Moduls `target_fuzz`, wobei `target` der Name des Targets ist (siehe jedoch [`rename`] unten).
#### Argumente
##### `bounds = "where_predicates"`
Erzwingt `where_predicates` (z. B. Trait-Bounds) für die Struktur, die zum Serialisieren/Deserialisieren von Argumenten verwendet wird. Dies kann z. B. erforderlich sein, wenn der Argumenttyp eines Targets ein assoziierter Typ ist. Ein Beispiel finden Sie unter [associated_type.rs] in diesem Repository.
##### `generic_args = "parameters"`
Verwenden Sie `parameters` als Typparameter des Targets beim Fuzzing. Beispiel:```rust
#[test_fuzz(generic_args = "String")]
fn foo<T: Clone + Debug + Serialize>(x: &T) {
...
}
Hinweis: Die Argumente des Ziels müssen für jede Instanziierung seiner Typparameter serialisierbar sein. Die Argumente des Ziels müssen jedoch nur dann deserialisierbar sein, wenn das Ziel mit parameters instanziiert wird.
impl_generic_args = "parameters"Verwende parameters als Self-Typparameter des Ziels beim Fuzzing. Beispiel:```rust
#[test_fuzz_impl]
impl<T: Clone + Debug + Serialize> for Foo {
#[test_fuzz(impl_generic_args = "String")]
fn bar(&self, x: &T) {
...
}
}
Hinweis: Die Argumente des Ziels müssen für **jede** Instanziierung seiner `Self`-Typparameter serialisierbar sein. Allerdings müssen die Argumente des Ziels nur dann deserialisierbar sein, wenn das `Self` des Ziels mit `parameters` instanziiert wird.
##### `convert = "X, Y"`
Beim Serialisieren der Argumente des Ziels werden Werte des Typs `X` in den Typ `Y` konvertiert – unter Verwendung der Implementierung von `From<X>` durch `Y` – oder Werte des Typs `&X` in den Typ `Y` – unter Verwendung der Implementierung des nicht standardmäßigen Traits `test_fuzz::FromRef<X>` durch `Y`. Beim Deserialisieren werden diese Werte unter Verwendung der Implementierung des nicht standardmäßigen Traits `test_fuzz::Into<X>` durch `Y` zurück in den Typ `X` konvertiert.
Das heißt, die Verwendung von `convert = "X, Y"` muss von bestimmten Implementierungen begleitet sein. Wenn `X` [`Clone`] implementiert, dann kann `Y` Folgendes implementieren:```rust
impl From<X> for Y {
fn from(x: X) -> Self {
...
}
}
Wenn X [Clone] nicht implementiert, dann muss Y Folgendes implementieren:```rust
impl test_fuzz::FromRef for Y {
fn from_ref(x: &X) -> Self {
...
}
}
Zusätzlich muss `Y` das Folgende implementieren (unabhängig davon, ob `X` [`Clone`] implementiert):```rust
impl test_fuzz::Into<X> for Y {
fn into(self) -> X {
...
}
}
Die Definition von test_fuzz::Into ist identisch mit der von [std::convert::Into]. Der Grund für die Verwendung eines nicht standardkonformen Traits ist es, Konflikte zu vermeiden, die durch Blanket-Implementierungen von Standard-Traits entstehen könnten.
enable_in_productionErzeugen Sie Korpusdateien, wenn keine Tests ausgeführt werden, sofern die Umgebungsvariable [TEST_FUZZ_WRITE] gesetzt ist. Standardmäßig werden Korpusdateien nur beim Ausführen von Tests erzeugt, unabhängig davon, ob [TEST_FUZZ_WRITE] gesetzt ist. Wenn Sie ein Target von außerhalb seines Paketverzeichnisses ausführen, setzen Sie [TEST_FUZZ_MANIFEST_PATH] auf den Pfad der Cargo.toml-Datei des Pakets.
WARNUNG: Das Setzen von enable_in_production könnte einen Denial-of-Service-Vektor einführen. Wenn diese Option beispielsweise für eine Funktion gesetzt wird, die viele Male mit unterschiedlichen Argumenten aufgerufen wird, könnte dies die Festplatte füllen. Die Prüfung von [TEST_FUZZ_WRITE] soll einen gewissen Schutz gegen diese Möglichkeit bieten. Dennoch sollten Sie diese Option vor der Verwendung sorgfältig abwägen.
execute_with = "function"Anstatt das Target direkt aufzurufen:
FnOnce() -> R konstruieren, wobei R der Rückgabetyp des Targets ist, sodass der Aufruf der Closure das Target aufruft;function mit der Closure aufrufen.Das Target auf diese Weise aufzurufen, ermöglicht es function, die Umgebung des Aufrufs einzurichten. Dies kann beispielsweise beim Fuzzing von [Substrate externalities] nützlich sein.
no_auto_generateVersuchen Sie nicht, für das Target [Korpusdateien automatisch zu generieren].
only_generic_argsErfassen Sie die generischen Argumente des Targets beim Ausführen von Tests, generieren Sie jedoch keine Korpusdateien und implementieren Sie keine Fuzzing-Harness. Dies kann nützlich sein, wenn das Target eine generische Funktion ist, aber unklar ist, welche Typparameter für das Fuzzing verwendet werden sollten.