lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

fuzz_qualification.rs (3736B)


      1 use std::{collections::BTreeSet, fs, path::Path, process::Command};
      2 
      3 use serde::Deserialize;
      4 
      5 #[derive(Debug, Deserialize)]
      6 struct Contract {
      7     schema_version: u16,
      8     harness: String,
      9     engine: String,
     10     toolchain: String,
     11     smoke_runs: u32,
     12     max_input_bytes: usize,
     13     targets: Vec<String>,
     14 }
     15 
     16 #[derive(Debug, Deserialize)]
     17 struct HarnessManifest {
     18     bin: Vec<Bin>,
     19 }
     20 
     21 #[derive(Debug, Deserialize)]
     22 struct Bin {
     23     name: String,
     24 }
     25 
     26 pub fn run(root: &Path) -> Result<(), String> {
     27     let contract = load(root)?;
     28     validate(root, &contract)?;
     29     run_cargo_fuzz(root, &["check".to_owned()], &contract)?;
     30     for target in &contract.targets {
     31         run_cargo_fuzz(
     32             root,
     33             &[
     34                 "run".to_owned(),
     35                 target.clone(),
     36                 "--".to_owned(),
     37                 format!("-runs={}", contract.smoke_runs),
     38                 format!("-max_len={}", contract.max_input_bytes),
     39             ],
     40             &contract,
     41         )?;
     42     }
     43     Ok(())
     44 }
     45 
     46 fn load(root: &Path) -> Result<Contract, String> {
     47     let path = root.join("contracts/releases/fuzz_matrix.toml");
     48     let raw = fs::read_to_string(&path)
     49         .map_err(|error| format!("failed to read {}: {error}", path.display()))?;
     50     toml::from_str(&raw).map_err(|error| format!("failed to parse {}: {error}", path.display()))
     51 }
     52 
     53 fn validate(root: &Path, contract: &Contract) -> Result<(), String> {
     54     if contract.schema_version != 1
     55         || contract.engine != "libfuzzer"
     56         || !contract.toolchain.starts_with("nightly-")
     57         || contract.smoke_runs < 1_000
     58         || contract.max_input_bytes < 16_384
     59     {
     60         return Err("invalid fuzz qualification contract".to_owned());
     61     }
     62     let expected = contract.targets.iter().collect::<BTreeSet<_>>();
     63     if expected.len() != 8 || expected.len() != contract.targets.len() {
     64         return Err("fuzz matrix requires exactly eight unique parser targets".to_owned());
     65     }
     66     let path = root.join(&contract.harness).join("Cargo.toml");
     67     let raw = fs::read_to_string(&path)
     68         .map_err(|error| format!("failed to read {}: {error}", path.display()))?;
     69     let manifest = toml::from_str::<HarnessManifest>(&raw)
     70         .map_err(|error| format!("failed to parse {}: {error}", path.display()))?;
     71     let actual = manifest
     72         .bin
     73         .iter()
     74         .map(|bin| &bin.name)
     75         .collect::<BTreeSet<_>>();
     76     if actual != expected {
     77         return Err(format!(
     78             "fuzz target inventory drift: expected {expected:?}, found {actual:?}"
     79         ));
     80     }
     81     Ok(())
     82 }
     83 
     84 fn run_cargo_fuzz(root: &Path, args: &[String], contract: &Contract) -> Result<(), String> {
     85     let mut command_args = vec![
     86         format!("+{}", contract.toolchain),
     87         "fuzz".to_owned(),
     88         args[0].clone(),
     89         "--fuzz-dir".to_owned(),
     90         contract.harness.clone(),
     91     ];
     92     command_args.extend_from_slice(&args[1..]);
     93     eprintln!("cargo {}", command_args.join(" "));
     94     let status = Command::new("cargo")
     95         .args(&command_args)
     96         .current_dir(root)
     97         .status()
     98         .map_err(|error| format!("failed to start cargo-fuzz: {error}"))?;
     99     if status.success() {
    100         Ok(())
    101     } else {
    102         Err(format!(
    103             "fuzz qualification failed: cargo {}",
    104             command_args.join(" ")
    105         ))
    106     }
    107 }
    108 
    109 #[cfg(test)]
    110 mod tests {
    111     use super::{load, validate};
    112 
    113     #[test]
    114     fn current_contract_matches_all_parser_harnesses() {
    115         let root = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
    116             .parent()
    117             .and_then(std::path::Path::parent)
    118             .expect("workspace root");
    119         validate(root, &load(root).expect("contract")).expect("valid fuzz matrix");
    120     }
    121 }