Modules Fuzz

Le fuzzing Rust est pris en charge via la crate libfuzzer-sys, qui fournit des liaisons au moteur de fuzzing libFuzzer de LLVM. Pour en savoir plus, consultez le dépôt libfuzzer-sys et la page du projet libFuzzer LLVM.

Le module rust_fuzz produit un binaire de fuzzing qui commence à générer des données aléatoires lorsqu'il est exécuté (comme les modules cc_fuzz). Comme le fuzzeur exploite le moteur de fuzzing libFuzzer, il peut prendre un certain nombre d'arguments pour contrôler le fuzzing. Ils sont énumérés dans la documentation de libFuzzer.

Les modules rust_fuzz sont une extension des modules rust_binary et partagent donc les mêmes propriétés et considérations. De plus, ils implémentent de nombreuses propriétés et fonctionnalités identiques à celles des modules cc_fuzz.

Lors de la compilation de modules rust_fuzz, l'indicateur --cfg fuzzing est émis, ce qui peut être utilisé pour prendre en charge la compilation conditionnelle du code de la bibliothèque afin d'améliorer le fuzzing.

Écrire un fuzzer Rust de base

Vous pouvez définir un module de fuzz dans un fichier de compilation Android.bp à l'aide du code suivant:

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",
}

Le fichier fuzzer.rs contient 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();
    }
});

Ici, fuzz_target!(|data: &[u8]| { /* fuzz using data here */ }); définit le point d'entrée de la cible de fuzz appelé par le moteur libFuzzer. L'argument data est une séquence d'octets fournie par le moteur libFuzzer à manipuler en entrée pour fuzzer la fonction ciblée.

Dans cet exemple de fuzzer, seule la longueur des données est vérifiée pour déterminer s'il faut appeler la fonction heap_oob, dont l'appel entraîne une lecture hors limites. libFuzzer est un fuzzer guidé par la couverture. Il converge donc rapidement vers la longueur problématique, car il détermine que les 326 octets de données ne génèrent pas de nouveaux chemins d'exécution.

Vous trouverez cet exemple dans le répertoire tools/security/fuzzing/example_rust_fuzzer/. Pour voir un exemple légèrement plus complexe d'un autre fuzzer (qui fuzze une dépendance rustlib) dans l'arborescence, consultez legacy_blob_fuzzer.

Pour savoir comment écrire des fuzzers Rust respectueux de la structure, consultez le livre Rust Fuzz, la documentation officielle du projet Rust Fuzz.