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