112a9d9c8Sopenharmony_ci//! A library to generate __fuzzed__ C headers for use with `quickcheck` 212a9d9c8Sopenharmony_ci//! 312a9d9c8Sopenharmony_ci//! ## Example 412a9d9c8Sopenharmony_ci//! 512a9d9c8Sopenharmony_ci//! ```rust 612a9d9c8Sopenharmony_ci//! extern crate quickcheck; 712a9d9c8Sopenharmony_ci//! extern crate quickchecking; 812a9d9c8Sopenharmony_ci//! extern crate rand; 912a9d9c8Sopenharmony_ci//! use quickcheck::{Arbitrary, Gen, StdGen}; 1012a9d9c8Sopenharmony_ci//! use quickchecking::fuzzers; 1112a9d9c8Sopenharmony_ci//! use rand::thread_rng; 1212a9d9c8Sopenharmony_ci//! 1312a9d9c8Sopenharmony_ci//! fn main() { 1412a9d9c8Sopenharmony_ci//! let generate_range: usize = 10; // Determines things like the length of 1512a9d9c8Sopenharmony_ci//! // arbitrary vectors generated. 1612a9d9c8Sopenharmony_ci//! let header = fuzzers::HeaderC::arbitrary( 1712a9d9c8Sopenharmony_ci//! &mut StdGen::new(thread_rng(), generate_range)); 1812a9d9c8Sopenharmony_ci//! println!("{}", header); 1912a9d9c8Sopenharmony_ci//! } 2012a9d9c8Sopenharmony_ci//! ``` 2112a9d9c8Sopenharmony_ci//! 2212a9d9c8Sopenharmony_ci#![deny(missing_docs)] 2312a9d9c8Sopenharmony_ci#[macro_use] 2412a9d9c8Sopenharmony_ciextern crate lazy_static; 2512a9d9c8Sopenharmony_ciextern crate quickcheck; 2612a9d9c8Sopenharmony_ciextern crate rand; 2712a9d9c8Sopenharmony_ciextern crate tempdir; 2812a9d9c8Sopenharmony_ci 2912a9d9c8Sopenharmony_ciuse quickcheck::{QuickCheck, StdGen, TestResult}; 3012a9d9c8Sopenharmony_ciuse rand::thread_rng; 3112a9d9c8Sopenharmony_ciuse std::error::Error; 3212a9d9c8Sopenharmony_ciuse std::fs::File; 3312a9d9c8Sopenharmony_ciuse std::io::Write; 3412a9d9c8Sopenharmony_ciuse std::path::PathBuf; 3512a9d9c8Sopenharmony_ciuse std::process::{Command, Output}; 3612a9d9c8Sopenharmony_ciuse std::sync::Mutex; 3712a9d9c8Sopenharmony_ciuse tempdir::TempDir; 3812a9d9c8Sopenharmony_ci 3912a9d9c8Sopenharmony_ci/// Contains definitions of and impls for types used to fuzz C declarations. 4012a9d9c8Sopenharmony_cipub mod fuzzers; 4112a9d9c8Sopenharmony_ci 4212a9d9c8Sopenharmony_ci// Global singleton, manages context across tests. For now that context is 4312a9d9c8Sopenharmony_ci// only the output_path for inspecting fuzzed headers (if specified). 4412a9d9c8Sopenharmony_cistruct Context { 4512a9d9c8Sopenharmony_ci output_path: Option<String>, 4612a9d9c8Sopenharmony_ci} 4712a9d9c8Sopenharmony_ci 4812a9d9c8Sopenharmony_ci// Initialize global context. 4912a9d9c8Sopenharmony_cilazy_static! { 5012a9d9c8Sopenharmony_ci static ref CONTEXT: Mutex<Context> = 5112a9d9c8Sopenharmony_ci Mutex::new(Context { output_path: None }); 5212a9d9c8Sopenharmony_ci} 5312a9d9c8Sopenharmony_ci 5412a9d9c8Sopenharmony_ci// Passes fuzzed header to the `csmith-fuzzing/predicate.py` script, returns 5512a9d9c8Sopenharmony_ci// output of the associated command. 5612a9d9c8Sopenharmony_cifn run_predicate_script( 5712a9d9c8Sopenharmony_ci header: fuzzers::HeaderC, 5812a9d9c8Sopenharmony_ci) -> Result<Output, Box<dyn Error>> { 5912a9d9c8Sopenharmony_ci let dir = TempDir::new("bindgen_prop")?; 6012a9d9c8Sopenharmony_ci let header_path = dir.path().join("prop_test.h"); 6112a9d9c8Sopenharmony_ci 6212a9d9c8Sopenharmony_ci let mut header_file = File::create(&header_path)?; 6312a9d9c8Sopenharmony_ci header_file.write_all(header.to_string().as_bytes())?; 6412a9d9c8Sopenharmony_ci header_file.sync_all()?; 6512a9d9c8Sopenharmony_ci 6612a9d9c8Sopenharmony_ci let header_path_string = header_path 6712a9d9c8Sopenharmony_ci .into_os_string() 6812a9d9c8Sopenharmony_ci .into_string() 6912a9d9c8Sopenharmony_ci .map_err(|_| "error converting path into String")?; 7012a9d9c8Sopenharmony_ci 7112a9d9c8Sopenharmony_ci let mut predicate_script_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 7212a9d9c8Sopenharmony_ci predicate_script_path.push("../../csmith-fuzzing/predicate.py"); 7312a9d9c8Sopenharmony_ci 7412a9d9c8Sopenharmony_ci let predicate_script_path_string = predicate_script_path 7512a9d9c8Sopenharmony_ci .into_os_string() 7612a9d9c8Sopenharmony_ci .into_string() 7712a9d9c8Sopenharmony_ci .map_err(|_| "error converting path into String")?; 7812a9d9c8Sopenharmony_ci 7912a9d9c8Sopenharmony_ci // Copy generated temp files to output_path directory for inspection. 8012a9d9c8Sopenharmony_ci // If `None`, output path not specified, don't copy. 8112a9d9c8Sopenharmony_ci if let Some(ref path) = CONTEXT.lock().unwrap().output_path { 8212a9d9c8Sopenharmony_ci Command::new("cp") 8312a9d9c8Sopenharmony_ci .arg("-a") 8412a9d9c8Sopenharmony_ci .arg(dir.path().to_str().unwrap()) 8512a9d9c8Sopenharmony_ci .arg(path) 8612a9d9c8Sopenharmony_ci .output()?; 8712a9d9c8Sopenharmony_ci } 8812a9d9c8Sopenharmony_ci 8912a9d9c8Sopenharmony_ci Ok(Command::new(&predicate_script_path_string) 9012a9d9c8Sopenharmony_ci .arg(&header_path_string) 9112a9d9c8Sopenharmony_ci .output()?) 9212a9d9c8Sopenharmony_ci} 9312a9d9c8Sopenharmony_ci 9412a9d9c8Sopenharmony_ci// Generatable property. Pass generated headers off to run through the 9512a9d9c8Sopenharmony_ci// `csmith-fuzzing/predicate.py` script. Success is measured by the success 9612a9d9c8Sopenharmony_ci// status of that command. 9712a9d9c8Sopenharmony_cifn bindgen_prop(header: fuzzers::HeaderC) -> TestResult { 9812a9d9c8Sopenharmony_ci match run_predicate_script(header) { 9912a9d9c8Sopenharmony_ci Ok(o) => TestResult::from_bool(o.status.success()), 10012a9d9c8Sopenharmony_ci Err(e) => { 10112a9d9c8Sopenharmony_ci println!("{:?}", e); 10212a9d9c8Sopenharmony_ci TestResult::from_bool(false) 10312a9d9c8Sopenharmony_ci } 10412a9d9c8Sopenharmony_ci } 10512a9d9c8Sopenharmony_ci} 10612a9d9c8Sopenharmony_ci 10712a9d9c8Sopenharmony_ci/// Instantiate a Quickcheck object and use it to run property tests using 10812a9d9c8Sopenharmony_ci/// fuzzed C headers generated with types defined in the `fuzzers` module. 10912a9d9c8Sopenharmony_ci/// Success/Failure is dictated by the result of passing the fuzzed headers 11012a9d9c8Sopenharmony_ci/// to the `csmith-fuzzing/predicate.py` script. 11112a9d9c8Sopenharmony_cipub fn test_bindgen( 11212a9d9c8Sopenharmony_ci generate_range: usize, 11312a9d9c8Sopenharmony_ci tests: usize, 11412a9d9c8Sopenharmony_ci output_path: Option<&str>, 11512a9d9c8Sopenharmony_ci) { 11612a9d9c8Sopenharmony_ci if let Some(path) = output_path { 11712a9d9c8Sopenharmony_ci CONTEXT.lock().unwrap().output_path = 11812a9d9c8Sopenharmony_ci Some(String::from(PathBuf::from(path).to_str().unwrap())); 11912a9d9c8Sopenharmony_ci } 12012a9d9c8Sopenharmony_ci 12112a9d9c8Sopenharmony_ci QuickCheck::new() 12212a9d9c8Sopenharmony_ci .tests(tests) 12312a9d9c8Sopenharmony_ci .gen(StdGen::new(thread_rng(), generate_range)) 12412a9d9c8Sopenharmony_ci .quickcheck(bindgen_prop as fn(fuzzers::HeaderC) -> TestResult) 12512a9d9c8Sopenharmony_ci} 126