
Macros de Rust y subcomando de Cargo para automatizar el fuzzing con afl.rs, incluida la generación de corpus y la implementación de harnesses, integrado con el framework de pruebas de Rust.
test-fuzz es un subcomando de Cargo y una colección de macros de Rust para automatizar ciertas tareas relacionadas con el fuzzing con [afl.rs], incluyendo:
test-fuzz logra esto (en parte) utilizando las facilidades de prueba de Rust. Por ejemplo, para generar un corpus de fuzzing, test-fuzz registra los argumentos de un objetivo cada vez que se le llama durante una ejecución de cargo test. Del mismo modo, test-fuzz implementa un harness de fuzzing como una prueba adicional en un binario generado por cargo-test. Esta integración estrecha con las facilidades de prueba de Rust es lo que motiva el nombre test-fuzz.
Contenido
test-fuzzInstala cargo-test-fuzz y [afl.rs] con el siguiente comando:```sh
cargo install cargo-test-fuzz cargo-afl
## Overview
El fuzzing con `test-fuzz` es esencialmente tres pasos:\*
1. **Identificar un objetivo de fuzzing**:
- Agrega las siguientes `dependencies` al archivo `Cargo.toml` del crate de destino:
```toml
serde = "*"
test-fuzz = "*"
```
- Antepón la macro [`test_fuzz`] a la función objetivo:
```rust
#[test_fuzz::test_fuzz]
fn foo(...) {
...
}
```
2. **Generar un corpus** ejecutando `cargo test`: ```
cargo test
cargo test-fuzz]: ```
cargo test-fuzz foo
* Puede ser necesario un paso preliminar adicional después de un reinicio:```sh cargo afl system-config
Ten en cuenta que el comando anterior ejecuta `sudo` internamente. Por lo tanto, es posible que se te solicite introducir tu contraseña.
## Componentes
### Macro `test_fuzz`
Anteponer la macro `test_fuzz` a una función indica que la función es un objetivo de fuzzing.
Los efectos principales de la macro `test_fuzz` son:
- Añadir instrumentación al objetivo para serializar sus argumentos y escribirlos en un archivo de corpus cada vez que se llama al objetivo. La instrumentación está protegida por `#[cfg(test)]` para que los archivos de corpus se generen solo al ejecutar pruebas (sin embargo, véase [`enable_in_production`] más abajo).
- Añadir una prueba que lea y deserialice argumentos desde la entrada estándar y aplique el objetivo a ellos. La prueba comprueba una variable de entorno, establecida por [`cargo test-fuzz`], para que la prueba no se bloquee al intentar leer desde la entrada estándar durante una invocación normal de `cargo test`. La prueba está contenida en un módulo para reducir la probabilidad de una colisión de nombres. Actualmente, el nombre del módulo es `target_fuzz`, donde `target` es el nombre del objetivo (sin embargo, véase [`rename`] más abajo).
#### Argumentos
##### `bounds = "where_predicates"`
Imponga `where_predicates` (p. ej., límites de trait) en la estructura utilizada para serializar/deserializar argumentos. Esto puede ser necesario, por ejemplo, si el tipo de argumento de un objetivo es un tipo asociado. Para ver un ejemplo, véase [associated_type.rs] en este repositorio.
##### `generic_args = "parameters"`
Use `parameters` como los parámetros de tipo del objetivo al realizar fuzzing. Ejemplo:```rust
#[test_fuzz(generic_args = "String")]
fn foo<T: Clone + Debug + Serialize>(x: &T) {
...
}
Note: Los argumentos del objetivo deben ser serializables para cada instanciación de sus parámetros de tipo. Pero los argumentos del objetivo solo deben ser deserializables cuando el objetivo se instancia con parameters.
impl_generic_args = "parameters"Usa parameters como los parámetros de tipo Self del objetivo al hacer fuzzing. Ejemplo:```rust
#[test_fuzz_impl]
impl<T: Clone + Debug + Serialize> for Foo {
#[test_fuzz(impl_generic_args = "String")]
fn bar(&self, x: &T) {
...
}
}
Nota: Los argumentos del objetivo deben ser serializables para **cada** instanciación de sus parámetros de tipo `Self`. Pero los argumentos del objetivo deben ser deserializables solo cuando el `Self` del objetivo esté instanciado con `parameters`.
##### `convert = "X, Y"`
Al serializar los argumentos del objetivo, convierte valores de tipo `X` a tipo `Y` usando la implementación de `From<X>` de `Y`, o de tipo `&X` a tipo `Y` usando la implementación del trait no estándar `test_fuzz::FromRef<X>` de `Y`. Al deserializar, convierte esos valores de vuelta al tipo `X` usando la implementación del trait no estándar `test_fuzz::Into<X>` de `Y`.
Es decir, el uso de `convert = "X, Y"` debe ir acompañado de ciertas implementaciones. Si `X` implementa [`Clone`], entonces `Y` puede implementar lo siguiente:```rust
impl From<X> for Y {
fn from(x: X) -> Self {
...
}
}
Si X no implementa [Clone], entonces Y debe implementar lo siguiente:```rust
impl test_fuzz::FromRef for Y {
fn from_ref(x: &X) -> Self {
...
}
}
Además, `Y` debe implementar lo siguiente (independientemente de si `X` implementa [`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_productionGenera archivos de corpus cuando no se están ejecutando pruebas, siempre que la variable de entorno [TEST_FUZZ_WRITE] esté establecida. El comportamiento predeterminado es generar archivos de corpus solo al ejecutar pruebas, independientemente de si [TEST_FUZZ_WRITE] está establecida. Al ejecutar un target desde fuera del directorio de su paquete, establece [TEST_FUZZ_MANIFEST_PATH] en la ruta del archivo Cargo.toml del paquete.
ADVERTENCIA: Establecer enable_in_production podría introducir un vector de denegación de servicio. Por ejemplo, establecer esta opción para una función que se llama muchas veces con distintos argumentos podría llenar el disco. La comprobación de [TEST_FUZZ_WRITE] pretende proporcionar cierta defensa contra esta posibilidad. No obstante, considera esta opción cuidadosamente antes de usarla.
execute_with = "function"En lugar de llamar directamente al target:
FnOnce() -> R, donde R es el tipo de retorno del target, de modo que llamar al closure llame al target;function con el closure.Llamar al target de esta manera permite que function configure el entorno de la llamada. Esto puede ser útil, por ejemplo, para fuzzing de [Substrate externalities].
no_auto_generateNo intentes [auto-generate corpus files] para el target.
only_generic_argsRegistra los argumentos genéricos del target al ejecutar pruebas, pero no genera archivos de corpus y no implementa un harness de fuzzing. Esto puede ser útil cuando el target es una función genérica, pero no está claro qué parámetros de tipo deberían usarse para el fuzzing.