1//! A library to generate __fuzzed__ C headers for use with `quickcheck` 2//! 3//! ## Example 4//! 5//! ```rust 6//! extern crate quickcheck; 7//! extern crate quickchecking; 8//! extern crate rand; 9//! use quickcheck::{Arbitrary, Gen, StdGen}; 10//! use quickchecking::fuzzers; 11//! use rand::thread_rng; 12//! 13//! fn main() { 14//! let generate_range: usize = 10; // Determines things like the length of 15//! // arbitrary vectors generated. 16//! let header = fuzzers::HeaderC::arbitrary( 17//! &mut StdGen::new(thread_rng(), generate_range)); 18//! println!("{}", header); 19//! } 20//! ``` 21//! 22#![deny(missing_docs)] 23#[macro_use] 24extern crate lazy_static; 25extern crate quickcheck; 26extern crate rand; 27extern crate tempdir; 28 29use quickcheck::{QuickCheck, StdGen, TestResult}; 30use rand::thread_rng; 31use std::error::Error; 32use std::fs::File; 33use std::io::Write; 34use std::path::PathBuf; 35use std::process::{Command, Output}; 36use std::sync::Mutex; 37use tempdir::TempDir; 38 39/// Contains definitions of and impls for types used to fuzz C declarations. 40pub mod fuzzers; 41 42// Global singleton, manages context across tests. For now that context is 43// only the output_path for inspecting fuzzed headers (if specified). 44struct Context { 45 output_path: Option<String>, 46} 47 48// Initialize global context. 49lazy_static! { 50 static ref CONTEXT: Mutex<Context> = 51 Mutex::new(Context { output_path: None }); 52} 53 54// Passes fuzzed header to the `csmith-fuzzing/predicate.py` script, returns 55// output of the associated command. 56fn run_predicate_script( 57 header: fuzzers::HeaderC, 58) -> Result<Output, Box<dyn Error>> { 59 let dir = TempDir::new("bindgen_prop")?; 60 let header_path = dir.path().join("prop_test.h"); 61 62 let mut header_file = File::create(&header_path)?; 63 header_file.write_all(header.to_string().as_bytes())?; 64 header_file.sync_all()?; 65 66 let header_path_string = header_path 67 .into_os_string() 68 .into_string() 69 .map_err(|_| "error converting path into String")?; 70 71 let mut predicate_script_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")); 72 predicate_script_path.push("../../csmith-fuzzing/predicate.py"); 73 74 let predicate_script_path_string = predicate_script_path 75 .into_os_string() 76 .into_string() 77 .map_err(|_| "error converting path into String")?; 78 79 // Copy generated temp files to output_path directory for inspection. 80 // If `None`, output path not specified, don't copy. 81 if let Some(ref path) = CONTEXT.lock().unwrap().output_path { 82 Command::new("cp") 83 .arg("-a") 84 .arg(dir.path().to_str().unwrap()) 85 .arg(path) 86 .output()?; 87 } 88 89 Ok(Command::new(&predicate_script_path_string) 90 .arg(&header_path_string) 91 .output()?) 92} 93 94// Generatable property. Pass generated headers off to run through the 95// `csmith-fuzzing/predicate.py` script. Success is measured by the success 96// status of that command. 97fn bindgen_prop(header: fuzzers::HeaderC) -> TestResult { 98 match run_predicate_script(header) { 99 Ok(o) => TestResult::from_bool(o.status.success()), 100 Err(e) => { 101 println!("{:?}", e); 102 TestResult::from_bool(false) 103 } 104 } 105} 106 107/// Instantiate a Quickcheck object and use it to run property tests using 108/// fuzzed C headers generated with types defined in the `fuzzers` module. 109/// Success/Failure is dictated by the result of passing the fuzzed headers 110/// to the `csmith-fuzzing/predicate.py` script. 111pub fn test_bindgen( 112 generate_range: usize, 113 tests: usize, 114 output_path: Option<&str>, 115) { 116 if let Some(path) = output_path { 117 CONTEXT.lock().unwrap().output_path = 118 Some(String::from(PathBuf::from(path).to_str().unwrap())); 119 } 120 121 QuickCheck::new() 122 .tests(tests) 123 .gen(StdGen::new(thread_rng(), generate_range)) 124 .quickcheck(bindgen_prop as fn(fuzzers::HeaderC) -> TestResult) 125} 126