Ir al contenido

cargo-fuzz - Fuzzing Rust con libFuzzer

cargo-fuzz - Fuzzing Rust con libFuzzer

cargo-fuzz es la forma estándar de hacer fuzzing a código Rust. Cableatea libFuzzer con un workflow nativo a Cargo: escribe un objetivo de fuzz como una función Rust ordinaria tomando bytes arbitrarios, ejecuta cargo fuzz run, y fuzzing guiado por cobertura caza panics, overflows aritméticos, y — en código unsafe — errores de memoria genuinos. Rust seguro previene corrupción de memoria, pero no previene panics, loops infinitos o bugs de lógica, y cualquier bloque unsafe o dependencia C reintroduce la clase completa de problemas de memoria.

Instalación

PasoComando
Instalarcargo install cargo-fuzz
Nightly requeridorustup install nightly (libFuzzer necesita nightly)
Inicializarcargo fuzz init (en tu crate)
Verificarcargo fuzz --version

Creando un objetivo de fuzz

cargo fuzz init                 # crea directorio fuzz/
cargo fuzz add parse_input      # crea un nuevo objetivo
// fuzz/fuzz_targets/parse_input.rs
#![no_main]
use libfuzzer_sys::fuzz_target;

fuzz_target!(|data: &[u8]| {
    if let Ok(s) = std::str::from_utf8(data) {
        let _ = my_crate::parse(s);   // los panics aquí se convierten en hallazgos
    }
});

Ejecutando

ComandoDescripción
cargo fuzz run parse_inputHacer fuzzing al objetivo
cargo fuzz run parse_input -- -max_total_time=300Ejecutar por 5 minutos
cargo fuzz run parse_input -j 88 trabajos paralelos
cargo fuzz listListar objetivos
cargo fuzz buildCompilar sin ejecutar

Entrada estructurada con Arbitrary

Los bytes raw desperdician tiempo en entradas que tu parser rechaza inmediatamente. El crate arbitrary construye valores tipados en su lugar:

use arbitrary::Arbitrary;

#[derive(Arbitrary, Debug)]
struct Config { name: String, retries: u8, enabled: bool }

fuzz_target!(|cfg: Config| {
    let _ = my_crate::apply_config(cfg);
});
BeneficioPor qué
Entradas válidas-formadasAlcanza lógica más profunda
Objetivos tipadosHacer fuzzing a APIs, no solo parsers
Salida DebugReproductores legibles

Sanitizers

SanitizerFlag
AddressSanitizer (por defecto)-s address
LeakSanitizerincluido con ASan
MemorySanitizer-s memory
ThreadSanitizer-s thread
Ninguno (más rápido)-s none
# Hacer fuzzing a código FFI unsafe con ASan
cargo fuzz run ffi_target -s address

Corpus y cobertura

ComandoPropósito
fuzz/corpus/<target>/Dónde se acumulan entradas interesantes
cargo fuzz cmin <target>Minimizar el corpus
cargo fuzz tmin <target> <input>Minimizar una entrada que crashea
cargo fuzz coverage <target>Generar datos de cobertura
Corpus semillaAñadir ejemplos válidos para bootstrap

Semillar el corpus con entradas reales válidas es la forma más efectiva de hacer que el fuzzing sea productivo rápidamente.

Reproduciendo crashes

# Los crashes aterrizan en fuzz/artifacts/<target>/
cargo fuzz run parse_input fuzz/artifacts/parse_input/crash-abc123

# Encogerlo a un caso mínimal
cargo fuzz tmin parse_input fuzz/artifacts/parse_input/crash-abc123

Convierte la entrada minimizada en un #[test] regular para que el bug permanezca arreglado.

Qué hacer fuzzing en Rust

Buen objetivoPor qué
Parsers / deserializersEntrada no confiable, estado complejo
Bloques unsafeSeguridad de memoria realmente en riesgo
Límites FFICódigo C detrás de la interfaz
Máquinas de estado/protocolosBugs de orden
Compresión / encodingTerritorio de bugs clásico
Menos útilPor qué
Aritmética segura puraLos panics usualmente son obvios
Wrappers finosPoca lógica a explorar

cargo-fuzz vs otros fuzzers

Aspectocargo-fuzzAFL++ (afl.rs)LibAFLhonggfuzz-rs
MotorlibFuzzerAFL++Personalizadohonggfuzz
SetupEl más fácil para RustModeradoConstruye el tuyoFácil
Entrada estructuradaarbitraryarbitraryTipos personalizadosarbitrary
Mejor paraFuzzing Rust estándarModos persistente/binarioObjetivos inusualesFeedback por hardware

Para objetivos non-Rust ve AFL++ o honggfuzz; para fuzzers personalizados ve LibAFL.

Recursos