Modules Fuzz

Le fuzzing Rust est compatible avec 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 ainsi que la page du projet LLVM libFuzzer.

Le module rust_fuzz produit un binaire de fuzzing qui commence le fuzzing lorsqu'il est exécuté (comme les modules cc_fuzz). Comme le fuzzer utilise le moteur de fuzzing libFuzzer, il peut accepter un certain nombre d'arguments pour contrôler le fuzzing. Elles sont énumérées dans la documentation libFuzzer.

Les modules rust_fuzz sont une extension des modules rust_binary. Ils 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 création de modules rust_fuzz, l'indicateur --cfg fuzzing est émis. Il 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 fuzzing dans un fichier de compilation Android.bp avec le 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 fuzzing appelé par le moteur libFuzzer. L'argument data est une séquence d'octets fournie par le moteur libFuzzer à manipuler en tant qu'entrée pour le fuzzing de 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 premiers octets de données n'entraînent pas de nouveaux chemins d'exécution.

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

Pour savoir comment écrire des fuzzers Rust structurés, consultez le livre Rust Fuzz, la documentation officielle du projet Rust Fuzz.