1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176
// Copyright 2015 Keegan McAllister.
// Copyright 2016 Corey Farwell.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// See `LICENSE` in this repository.
use std::env;
use std::io::{self, Read};
use std::panic;
// those functions are provided by the afl-llvm-rt static library
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;
}
#[doc(hidden)]
#[no_mangle]
pub static mut __afl_sharedmem_fuzzing: i32 = 1;
/// Fuzz a closure by passing it a `&[u8]`
///
/// This slice contains a "random" quantity of "random" data.
///
/// ```rust,no_run
/// # extern crate afl;
/// # use afl::fuzz;
/// # fn main() {
/// fuzz(true, |data|{
/// if data.len() != 6 {return}
/// if data[0] != b'q' {return}
/// if data[1] != b'w' {return}
/// if data[2] != b'e' {return}
/// if data[3] != b'r' {return}
/// if data[4] != b't' {return}
/// if data[5] != b'y' {return}
/// panic!("BOOM")
/// });
/// # }
/// ```
pub fn fuzz<F>(hook: bool, mut closure: F)
where
F: FnMut(&[u8]) + std::panic::RefUnwindSafe,
{
// this marker strings needs to be in the produced executable for
// afl-fuzz to detect `persistent mode` and `defered mode`
static PERSIST_MARKER: &str = "##SIG_AFL_PERSISTENT##\0";
static DEFERED_MARKER: &str = "##SIG_AFL_DEFER_FORKSRV##\0";
// we now need a fake instruction to prevent the compiler from optimizing out
// those marker strings
unsafe { std::ptr::read_volatile(&PERSIST_MARKER) }; // hack used in https://github.com/bluss/bencher for black_box()
unsafe { std::ptr::read_volatile(&DEFERED_MARKER) };
// unsafe { asm!("" : : "r"(&PERSIST_MARKER)) }; // hack used in nightly's back_box(), requires feature asm
// unsafe { asm!("" : : "r"(&DEFERED_MARKER)) };
if hook {
let prev_hook = std::panic::take_hook();
// sets panic hook to abort
std::panic::set_hook(Box::new(move |panic_info| {
prev_hook(panic_info);
std::process::abort();
}));
}
let mut input = vec![];
let loop_count = if let Ok(value) = env::var("AFL_FUZZER_LOOPCOUNT") {
value
.parse()
.expect("Failed to parse environment variable to a number")
} else {
usize::MAX
};
// initialize forkserver there
unsafe { __afl_manual_init() };
while unsafe { __afl_persistent_loop(loop_count) } != 0 {
// get the testcase from the fuzzer
let input_ref = if unsafe { __afl_fuzz_ptr.is_null() } {
// in-memory testcase delivery is not enabled
// get buffer from AFL++ through stdin
let result = io::stdin().read_to_end(&mut input);
if result.is_err() {
return;
}
&input
} else {
unsafe {
// get the testcase from the shared memory
let input_len = *__afl_fuzz_len as usize;
std::slice::from_raw_parts(__afl_fuzz_ptr, input_len)
}
};
// We still catch unwinding panics just in case the fuzzed code modifies
// the panic hook.
// If so, the fuzzer will be unable to tell different bugs apart and you will
// only be able to find one bug at a time before fixing it to then find a new one.
let did_panic = std::panic::catch_unwind(panic::AssertUnwindSafe(|| {
closure(input_ref);
}))
.is_err();
if did_panic {
// hopefully the custom panic hook will be called before and abort the
// process before the stack frames are unwinded.
std::process::abort();
}
input.clear();
}
}
/// Fuzz a closure-like block of code by passing it an object of arbitrary type.
///
/// You can choose the type of the argument using the syntax as in the example below.
/// Please check out the `arbitrary` crate to see which types are available.
///
/// For performance reasons, it is recommended that you use the native type `&[u8]` when possible.
///
/// ```rust,no_run
/// # #[macro_use] extern crate afl;
/// # fn main() {
/// fuzz!(|data: &[u8]| {
/// if data.len() != 6 {return}
/// if data[0] != b'q' {return}
/// if data[1] != b'w' {return}
/// if data[2] != b'e' {return}
/// if data[3] != b'r' {return}
/// if data[4] != b't' {return}
/// if data[5] != b'y' {return}
/// panic!("BOOM")
/// });
/// # }
/// ```
#[macro_export]
macro_rules! fuzz {
( $($x:tt)* ) => { $crate::__fuzz!(true, $($x)*) }
}
/// Like `fuzz!` above, but panics that are caught inside the fuzzed code are not turned into
/// crashes.
#[macro_export]
macro_rules! fuzz_nohook {
( $($x:tt)* ) => { $crate::__fuzz!(false, $($x)*) }
}
#[doc(hidden)]
#[macro_export]
macro_rules! __fuzz {
($hook:expr, |$buf:ident| $body:expr) => {
$crate::fuzz($hook, |$buf| $body);
};
($hook:expr, |$buf:ident: &[u8]| $body:expr) => {
$crate::fuzz($hook, |$buf| $body);
};
($hook:expr, |$buf:ident: $dty: ty| $body:expr) => {
$crate::fuzz($hook, |$buf| {
let $buf: $dty = {
let mut data = ::arbitrary::Unstructured::new($buf);
if let Ok(d) = ::arbitrary::Arbitrary::arbitrary(&mut data).map_err(|_| "") {
d
} else {
return;
}
};
$body
});
};
}