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}