use std::io::{self, Read};
use std::panic;
mod common;
pub use common::*;
extern "C" {
fn __afl_persistent_loop(counter: usize) -> isize;
fn __afl_manual_init();
static __afl_fuzz_len: *const u32;
static __afl_fuzz_ptr: *const u8;
}
#[no_mangle]
pub static __afl_sharedmem_fuzzing: i32 = 1;
pub fn fuzz<F>(hook: bool, mut closure: F)
where
F: FnMut(&[u8]) + std::panic::RefUnwindSafe,
{
static PERSIST_MARKER: &'static str = "##SIG_AFL_PERSISTENT##\0";
static DEFERED_MARKER: &'static str = "##SIG_AFL_DEFER_FORKSRV##\0";
unsafe { std::ptr::read_volatile(&PERSIST_MARKER) }; unsafe { std::ptr::read_volatile(&DEFERED_MARKER) };
if hook {
std::panic::set_hook(Box::new(|_| {
std::process::abort();
}));
}
let mut input = vec![];
unsafe { __afl_manual_init() };
while unsafe { __afl_persistent_loop(1000) } != 0 {
let input_ref = if unsafe { __afl_fuzz_ptr.is_null() } {
let result = io::stdin().read_to_end(&mut input);
if result.is_err() {
return;
}
&input
} else {
unsafe {
let input_len = *__afl_fuzz_len as usize;
std::slice::from_raw_parts(__afl_fuzz_ptr, input_len)
}
};
let did_panic = std::panic::catch_unwind(panic::AssertUnwindSafe(|| {
closure(input_ref);
}))
.is_err();
if did_panic {
std::process::abort();
}
#[cfg(feature = "reset_lazy_static")]
unsafe {
lazy_static::lazy::reset();
}
input.clear();
}
}
#[macro_export]
macro_rules! fuzz {
( $($x:tt)* ) => { $crate::__fuzz!(true, $($x)*) }
}
#[macro_export]
macro_rules! fuzz_nohook {
( $($x:tt)* ) => { $crate::__fuzz!(false, $($x)*) }
}
#[macro_export]
macro_rules! __fuzz {
($hook:expr, |$buf:ident| $body:block) => {
$crate::fuzz($hook, |$buf| $body);
};
($hook:expr, |$buf:ident: &[u8]| $body:block) => {
$crate::fuzz($hook, |$buf| $body);
};
($hook:expr, |$buf:ident: $dty: ty| $body:block) => {
$crate::fuzz($hook, |$buf| {
let $buf: $dty = {
use arbitrary::{Arbitrary, RingBuffer};
if let Ok(d) = RingBuffer::new($buf, $buf.len())
.and_then(|mut b| Arbitrary::arbitrary(&mut b).map_err(|_| ""))
{
d
} else {
return;
}
};
$body
});
};
}
#[cfg(test)]
mod test {
use std::{path, process, thread, time};
fn target_dir_path() -> &'static path::Path {
if path::Path::new("../target/debug/cargo-afl").exists() {
path::Path::new("../target/debug/")
} else if path::Path::new("target/debug/cargo-afl").exists() {
path::Path::new("target/debug/")
} else {
panic!("Could not find cargo-afl binary");
}
}
fn cargo_afl_path() -> path::PathBuf {
target_dir_path().join("cargo-afl")
}
fn examples_hello_path() -> path::PathBuf {
target_dir_path().join("examples").join("hello")
}
#[test]
fn integration() {
let temp_dir =
tempdir::TempDir::new("aflrs").expect("Could not create temporary directory");
let temp_dir_path = temp_dir.path();
let mut child = process::Command::new(cargo_afl_path())
.arg("afl")
.arg("fuzz")
.stdout(process::Stdio::inherit())
.stderr(process::Stdio::inherit())
.arg("-i")
.arg(".")
.arg("-o")
.arg(temp_dir_path)
.arg(examples_hello_path())
.spawn()
.expect("Could not run cargo afl fuzz");
thread::sleep(time::Duration::from_secs(10));
for _ in 0..5 {
thread::sleep(time::Duration::from_secs(1));
let _ = child.kill();
}
assert!(temp_dir_path.join("default").join("fuzzer_stats").is_file());
}
}