Módulos de fuzzing

Se admite el fuzzing de Rust con el contenedor libfuzzer-sys, que proporciona vinculaciones al motor de fuzzing de libFuzzer de LLVM. Para obtener más información, consulta el repositorio libfuzzer-sys y la página del proyecto de libFuzzer de LLVM.

El módulo rust_fuzz genera un objeto binario fuzzer que inicia el proceso de fuzzing cuando se ejecuta (similar a los módulos de cc_fuzz). A medida que el fuzzer aprovecha el motor de fuzzing libFuzzer, es posible que se necesiten varios argumentos para controlar el proceso, que se indican en la documentación de libFuzzer.

Los módulos rust_fuzz son una extensión de los módulos rust_binary y, por lo tanto, comparten las mismas propiedades y consideraciones. Además, implementan muchas de las mismas propiedades y funciones que los módulos cc_fuzz.

Cuando se compilan módulos rust_fuzz, se emite la marca --cfg fuzzing que se puede usar para asistir en la compilación condicional de código de biblioteca para mejorar el fuzzing.

Cómo escribir un fuzzer básico de Rust

Puedes definir un módulo de fuzz en un archivo de compilación Android.bp con 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",
}

El archivo fuzzer.rs contiene un fuzzer simple:

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();
    }
});

Aquí, fuzz_target!(|data: &[u8]| { /* fuzz using data here */ }); define el punto de entrada del objetivo de fuzz al que llama el motor libFuzzer. El argumento data es una secuencia de bytes que proporciona el motor libFuzzer para que se manipule como entrada para el fuzzing de la función objetivo.

En este fuzzer de ejemplo, solo se verifica la longitud de los datos para determinar si se debe llamar a la función heap_oob, cuya llamada genera una lectura fuera de los límites. libFuzzer es un fuzzer guiado por cobertura, por lo que converge rápido en la longitud problemática cuando determina que los primeros 326 B de datos no generan rutas de ejecución nuevas.

Encuentra este ejemplo en un árbol en tools/security/fuzzing/example_rust_fuzzer/. Para ver un ejemplo un poco más complejo de otro fuzzer (que realiza fuzzing en una dependencia rustlib) en un árbol, consulta legacy_blob_fuzzer.

Para obtener asesoramiento sobre cómo escribir fuzzers con reconocimiento de estructura, consulta el libro sobre fuzz de Rust, la documentación oficial del proyecto de fuzzing de Rust.