//! A library to generate __fuzzed__ C headers for use with `quickcheck` //! //! ## Example //! //! ```rust //! extern crate quickcheck; //! extern crate quickchecking; //! extern crate rand; //! use quickcheck::{Arbitrary, Gen, StdGen}; //! use quickchecking::fuzzers; //! use rand::thread_rng; //! //! fn main() { //! let generate_range: usize = 10; // Determines things like the length of //! // arbitrary vectors generated. //! let header = fuzzers::HeaderC::arbitrary( //! &mut StdGen::new(thread_rng(), generate_range)); //! println!("{}", header); //! } //! ``` //! #![deny(missing_docs)] #[macro_use] extern crate lazy_static; extern crate quickcheck; extern crate rand; extern crate tempdir; use std::sync::Mutex; use quickcheck::{QuickCheck, StdGen, TestResult}; use std::fs::File; use std::io::Write; use tempdir::TempDir; use std::process::{Command, Output}; use std::path::PathBuf; use std::error::Error; use rand::thread_rng; /// Contains definitions of and impls for types used to fuzz C declarations. pub mod fuzzers; // Global singleton, manages context across tests. For now that context is // only the output_path for inspecting fuzzed headers (if specified). struct Context { output_path: Option, } // Initialize global context. lazy_static! { static ref CONTEXT: Mutex = Mutex::new(Context { output_path: None }); } // Passes fuzzed header to the `csmith-fuzzing/predicate.py` script, returns // output of the associated command. fn run_predicate_script(header: fuzzers::HeaderC) -> Result> { let dir = TempDir::new("bindgen_prop")?; let header_path = dir.path().join("prop_test.h"); let mut header_file = File::create(&header_path)?; header_file.write_all(header.to_string().as_bytes())?; header_file.sync_all()?; let header_path_string; match header_path.into_os_string().into_string() { Ok(s) => header_path_string = s, Err(_) => return Err(From::from("error converting path into String")), } let mut predicate_script_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); predicate_script_path.push("../../csmith-fuzzing/predicate.py"); let predicate_script_path_string; match predicate_script_path.into_os_string().into_string() { Ok(s) => predicate_script_path_string = s, Err(_) => return Err(From::from("error converting path into String")), } // Copy generated temp files to output_path directory for inspection. // If `None`, output path not specified, don't copy. match CONTEXT.lock().unwrap().output_path { Some(ref path) => { Command::new("cp") .arg("-a") .arg(&dir.path().to_str().unwrap()) .arg(&path) .output()?; } None => {} } Ok(Command::new(&predicate_script_path_string) .arg(&header_path_string) .output()?) } // Generatable property. Pass generated headers off to run through the // `csmith-fuzzing/predicate.py` script. Success is measured by the success // status of that command. fn bindgen_prop(header: fuzzers::HeaderC) -> TestResult { match run_predicate_script(header) { Ok(o) => return TestResult::from_bool(o.status.success()), Err(e) => { println!("{:?}", e); return TestResult::from_bool(false); } } } /// Instantiate a Quickcheck object and use it to run property tests using /// fuzzed C headers generated with types defined in the `fuzzers` module. /// Success/Failure is dictated by the result of passing the fuzzed headers /// to the `csmith-fuzzing/predicate.py` script. pub fn test_bindgen(generate_range: usize, tests: usize, output_path: Option<&str>) { match output_path { Some(path) => { CONTEXT.lock().unwrap().output_path = Some(String::from(PathBuf::from(path).to_str().unwrap())); } None => {} // Path not specified, don't provide output. } QuickCheck::new() .tests(tests) .gen(StdGen::new(thread_rng(), generate_range)) .quickcheck(bindgen_prop as fn(fuzzers::HeaderC) -> TestResult) }