Módulos de fuzz

A caixa libfuzzer-sys, que fornece vinculações ao mecanismo de fuzzing libFuzzer do LLVM, também oferece suporte ao fuzzing do Rust. Para saber mais, consulte o repositório libfuzzer-sys e a página do projeto libFuzzer do LLVM (links em inglês).

O módulo rust_fuzz produz um binário fuzzer que inicia o fuzzing quando é executado, de forma semelhante aos módulos cc_fuzz. Como o fuzzer aproveita o mecanismo de fuzzing libFuzzer, ele pode usar vários argumentos para controlar o fuzzer. Eles estão enumerados na documentação do libFuzzer (em inglês).

Os módulos rust_fuzz são uma extensão dos módulos rust_binary e, por isso, compartilham as mesmas propriedades e considerações. Além disso, eles implementam muitas das mesmas propriedades e funcionalidades que os módulos cc_fuzz.

Ao criar módulos rust_fuzz, a flag --cfg fuzzing é emitida. Ela pode ser usada para oferecer suporte à compilação condicional de código de biblioteca para melhorar o fuzzing.

Criar um fuzzer básico usando o Rust

Você pode definir um módulo fuzz em um arquivo de build Android.bp com este código:

rust_fuzz {
    name: "example_rust_fuzzer",
    srcs: ["fuzzer.rs"],

    // Config for running the target on fuzzing infrastructure can be set under
    // fuzz_config. This shares the same properties as cc_fuzz's fuzz_config.
    fuzz_config: {
        fuzz_on_haiku_device: true,
        fuzz_on_haiku_host: false,
    },

    // Path to a corpus of sample inputs, optional. See https://llvm.org/docs/LibFuzzer.html#corpus
    corpus: ["testdata/*"],

    // Path to a dictionary of sample byte sequences, optional. See https://llvm.org/docs/LibFuzzer.html#dictionaries
    dictionary: "example_rust_fuzzer.dict",
}

O arquivo fuzzer.rs contém um fuzzer simples:

fn heap_oob() {
    let xs = vec![0, 1, 2, 3];
    let val = unsafe { *xs.as_ptr().offset(4) };
    println!("Out-of-bounds heap value: {}", val);
}

fuzz_target!(|data: &[u8]| {
    let magic_number = 327;
    if data.len() == magic_number {
        heap_oob();
    }
});

Aqui, o fuzz_target!(|data: &[u8]| { /* fuzz using data here */ }); define o ponto de entrada de destino do fuzz chamado pelo mecanismo libFuzzer. O argumento data é uma sequência de bytes fornecidos pelo mecanismo libFuzzer a ser manipulado como entrada para converter a função de destino.

Neste fuzzer de exemplo, apenas a quantidade de dados é verificada para determinar se a função heap_oob será chamada, o que resulta em uma leitura fora dos limites. O libFuzzer é um fuzzer guiado por cobertura. Portanto, ele converge rapidamente para o tamanho problemático, porque determina que os primeiros 326 B de dados não resultam em novos caminhos de execução.

Veja este exemplo, na árvore, em tools/security/fuzzing/example_rust_fuzzer/. Para um exemplo um pouco mais complexo de outro fuzzer, que esmaece uma dependência rustlib na árvore, consulte o legacy_blob_fuzzer.

Para saber como programar fuzzers com reconhecimento da estrutura, consulte o livro Rust Fuzz, que é a documentação oficial para o projeto Rust Fuzz (links em inglês).