cargo-fuzz é a forma padrão de fuzz em código Rust. Ele fia libFuzzer com um fluxo de trabalho nativo de Cargo: escreva um alvo de fuzz como uma função Rust ordinária levando bytes arbitrários, execute cargo fuzz run, e fuzzing guiado por cobertura caça por panics, overflows aritméticos e — em código unsafe — erros de memória genuínos. Rust seguro previne corrupção de memória, mas não previne panics, loops infinitos ou bugs de lógica, e qualquer bloco unsafe ou dependência C reintroduz a classe inteira de problemas de memória.
Instalação
| Passo | Comando |
|---|
| Instala | cargo install cargo-fuzz |
| Nightly necessário | rustup install nightly (libFuzzer precisa nightly) |
| Inicializa | cargo fuzz init (no seu crate) |
| Verificar | cargo fuzz --version |
Criando um Alvo de Fuzz
cargo fuzz init # cria diretório fuzz/
cargo fuzz add parse_input # cria um novo alvo
// 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); // panics aqui se tornam descobertas
}
});
Executando
| Comando | Descrição |
|---|
cargo fuzz run parse_input | Fuzz o alvo |
cargo fuzz run parse_input -- -max_total_time=300 | Executa por 5 minutos |
cargo fuzz run parse_input -j 8 | 8 jobs paralelos |
cargo fuzz list | Lista alvos |
cargo fuzz build | Construa sem executar |
Raw bytes desperdiçam tempo em inputs seu parser rejeita imediatamente. O crate arbitrary constrói valores digitados em vez disso:
use arbitrary::Arbitrary;
#[derive(Arbitrary, Debug)]
struct Config { name: String, retries: u8, enabled: bool }
fuzz_target!(|cfg: Config| {
let _ = my_crate::apply_config(cfg);
});
| Benefício | Por quê |
|---|
| Inputs de forma válida | Alcança lógica mais profunda |
| Alvos digitados | Fuzz APIs, não apenas parsers |
Saída Debug | Reprodutores legíveis |
Sanitizers
| Sanitizer | Flag |
|---|
| AddressSanitizer (padrão) | -s address |
| LeakSanitizer | incluído com ASan |
| MemorySanitizer | -s memory |
| ThreadSanitizer | -s thread |
| Nenhum (mais rápido) | -s none |
# Fuzz código unsafe FFI com ASan
cargo fuzz run ffi_target -s address
Corpus e Cobertura
| Comando | Propósito |
|---|
fuzz/corpus/<target>/ | Onde inputs interessantes acumulam |
cargo fuzz cmin <target> | Minimiza o corpus |
cargo fuzz tmin <target> <input> | Minimiza um input travando |
cargo fuzz coverage <target> | Gera dados de cobertura |
| Seed corpus | Adicione exemplos válidos para bootstrap |
Semear o corpus com inputs reais e válidos é a forma única mais eficaz para tornar fuzzing produtivo rapidamente.
Reproduzindo Crashes
# Crashes caem em fuzz/artifacts/<target>/
cargo fuzz run parse_input fuzz/artifacts/parse_input/crash-abc123
# Encolha para um caso mínimo
cargo fuzz tmin parse_input fuzz/artifacts/parse_input/crash-abc123
Converta o input minimizado em um #[test] regular para que o bug permanece corrigido.
O que Fuzz em Rust
| Bom alvo | Por quê |
|---|
| Parsers / deserializadores | Input não confiável, estado complexo |
Blocos unsafe | Segurança de memória realmente em risco |
| Limites FFI | Código C atrás da interface |
| Protocolo/máquinas de estado | Bugs de ordenação |
| Compressão / codificação | Território de bug clássico |
| Menos útil | Por quê |
|---|
| Aritmética pura segura | Panics geralmente são óbvios |
| Thin wrappers | Pouca lógica para explorar |
cargo-fuzz vs Outros Fuzzers
| Aspecto | cargo-fuzz | AFL++ (afl.rs) | LibAFL | honggfuzz-rs |
|---|
| Engine | libFuzzer | AFL++ | Custom | honggfuzz |
| Setup | Mais fácil para Rust | Moderado | Construa o seu | Fácil |
| Input estruturado | arbitrary | arbitrary | Tipos customizados | arbitrary |
| Melhor para | Fuzzing Rust padrão | Modos persistente/binary | Alvos incomuns | Feedback de hardware |
Para alvos não-Rust veja AFL++ ou honggfuzz; para fuzzers customizados veja LibAFL.
Recursos