Skip to main content

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::os::raw::c_char;
11use std::panic;
12
13// those functions are provided by the afl-compiler-rt static library
14unsafe extern "C" {
15    fn __afl_persistent_loop(counter: usize) -> isize;
16    fn __afl_manual_init();
17
18    static __afl_fuzz_len: *const u32;
19    static __afl_fuzz_ptr: *const u8;
20}
21
22// AFL++ IJON functions in afl-compiler-rt
23unsafe extern "C" {
24    pub fn ijon_max(addr: u32, val: u64);
25    pub fn ijon_min(addr: u32, val: u64);
26    pub fn ijon_set(addr: u32, val: u32);
27    pub fn ijon_inc(addr: u32, val: u32);
28    pub fn ijon_xor_state(val: u32);
29    pub fn ijon_reset_state();
30    pub fn ijon_simple_hash(x: u64) -> u64;
31    pub fn ijon_hashint(old: u32, val: u32) -> u32;
32    pub fn ijon_hashstr(old: u32, val: *const c_char) -> u32;
33    pub fn ijon_hashmen(old: u32, val: *const u8, len: usize) -> u32;
34    pub fn ijon_hashstack_backtrace() -> u32;
35    pub fn ijon_hashstack() -> u32;
36    pub fn ijon_strdist(a: *const u8, b: *const u8) -> u32;
37    pub fn ijon_memdist(a: *const u8, b: *const u8, len: usize) -> u32;
38    pub fn ijon_max_variadic(addr: u32, ...);
39    pub fn ijon_min_variadic(addr: u32, ...);
40}
41
42#[macro_export]
43macro_rules! ijon_inc {
44    ($x:expr) => {{
45        unsafe {
46            static mut loc: u32 = 0;
47            if loc == 0 {
48                let cfile = std::ffi::CString::new(file!()).unwrap();
49                loc = afl::ijon_hashstr(line!(), cfile.as_ptr());
50            }
51            afl::ijon_inc(loc, $x)
52        };
53    }};
54}
55
56#[macro_export]
57macro_rules! ijon_max {
58    ($($x:expr),+ $(,)?) => {{
59        unsafe {
60            static mut loc: u32 = 0;
61            if loc == 0 {
62                let cfile = std::ffi::CString::new(file!()).unwrap();
63                loc = afl::ijon_hashstr(line!(), cfile.as_ptr());
64            }
65            afl::ijon_max_variadic(_IJON_LOC_CACHE, $($x),+, 0u64)
66        };
67    }};
68}
69
70#[macro_export]
71macro_rules! ijon_min {
72    ($($x:expr),+ $(,)?) => {{
73        unsafe {
74            static mut loc: u32 = 0;
75            if loc == 0 {
76                let cfile = std::ffi::CString::new(file!()).unwrap();
77                loc = afl::ijon_hashstr(line!(), cfile.as_ptr());
78            }
79            afl::ijon_min_variadic(loc, $($x),+, 0u64)
80        };
81    }};
82}
83
84#[macro_export]
85macro_rules! ijon_set {
86    ($x:expr) => {{
87        unsafe {
88            static mut loc: u32 = 0;
89            if loc == 0 {
90                let cfile = std::ffi::CString::new(file!()).unwrap();
91                loc = afl::ijon_hashstr(line!(), cfile.as_ptr());
92            }
93            afl::ijon_set(loc, $x)
94        };
95    }};
96}
97
98#[macro_export]
99macro_rules! ijon_state {
100    ($n:expr) => {
101        unsafe { afl::ijon_xor_state($n) }
102    };
103}
104
105#[macro_export]
106macro_rules! ijon_ctx {
107    ($x:expr) => {{
108        let cfile = std::ffi::CString::new(file!()).unwrap();
109        let hash = unsafe { afl::ijon_hashstr(line!(), cfile.as_ptr()) };
110        unsafe { afl::ijon_xor_state(hash) };
111        let temp = $x;
112        unsafe { afl::ijon_xor_state(hash) };
113        temp
114    }};
115}
116
117#[macro_export]
118macro_rules! ijon_max_at {
119    ($addr:expr, $x:expr) => {
120        unsafe { afl::ijon_max($addr, $x) }
121    };
122}
123
124#[macro_export]
125macro_rules! ijon_min_at {
126    ($addr:expr, $x:expr) => {
127        unsafe { afl::ijon_min($addr, $x) }
128    };
129}
130
131#[macro_export]
132macro_rules! _ijon_abs_dist {
133    ($x:expr, $y:expr) => {
134        if $x < $y { $y - $x } else { $x - $y }
135    };
136}
137
138#[macro_export]
139macro_rules! ijon_bits {
140    ($x:expr) => {
141        unsafe {
142            afl::ijon_set(afl::ijon_hashint(
143                afl::ijon_hashstack(),
144                if $x == 0 {
145                    0
146                } else {
147                    $x.leading_zeros() as u32
148                },
149            ))
150        }
151    };
152}
153
154#[macro_export]
155macro_rules! ijon_strdist {
156    ($x:expr, $y:expr) => {
157        unsafe {
158            afl::ijon_set(afl::ijon_hashint(
159                afl::ijon_hashstack(),
160                afl::ijon_strdist($x, $y),
161            ))
162        }
163    };
164}
165
166#[macro_export]
167macro_rules! ijon_dist {
168    ($x:expr, $y:expr) => {
169        unsafe {
170            afl::ijon_set(afl::ijon_hashint(
171                afl::ijon_hashstack(),
172                $crate::_ijon_abs_dist!($x, $y),
173            ))
174        }
175    };
176}
177
178#[macro_export]
179macro_rules! ijon_cmp {
180    ($x:expr, $y:expr) => {
181        unsafe {
182            afl::ijon_inc(afl::ijon_hashint(
183                afl::ijon_hashstack(),
184                ($x ^ $y).count_ones(),
185            ))
186        }
187    };
188}
189
190#[macro_export]
191macro_rules! ijon_stack_max {
192    ($x:expr) => {{
193        unsafe {
194            static mut loc: u32 = 0;
195            if loc == 0 {
196                let cfile = std::ffi::CString::new(file!()).unwrap();
197                loc = afl::ijon_hashstr(line!(), cfile.as_ptr());
198            }
199            afl::ijon_max(afl::ijon_hashint(loc, afl::ijon_hashstack()), $x)
200        };
201    }};
202}
203
204#[macro_export]
205macro_rules! ijon_stack_min {
206    ($x:expr) => {{
207        unsafe {
208            static mut loc: u32 = 0;
209            if loc == 0 {
210                let cfile = std::ffi::CString::new(file!()).unwrap();
211                loc = afl::ijon_hashstr(line!(), cfile.as_ptr());
212            }
213            afl::ijon_min(afl::ijon_hashint(loc, afl::ijon_hashstack()), $x)
214        };
215    }};
216}
217
218// end if AFL++ IJON functions
219
220#[allow(non_upper_case_globals)]
221#[doc(hidden)]
222#[unsafe(no_mangle)]
223pub static mut __afl_sharedmem_fuzzing: i32 = 1;
224
225/// Fuzz a closure by passing it a `&[u8]`
226///
227/// This slice contains a "random" quantity of "random" data.
228///
229/// ```rust,no_run
230/// # extern crate afl;
231/// # use afl::fuzz;
232/// # fn main() {
233/// fuzz(true, |data|{
234///     if data.len() != 6 {return}
235///     if data[0] != b'q' {return}
236///     if data[1] != b'w' {return}
237///     if data[2] != b'e' {return}
238///     if data[3] != b'r' {return}
239///     if data[4] != b't' {return}
240///     if data[5] != b'y' {return}
241///     panic!("BOOM")
242/// });
243/// # }
244/// ```
245pub fn fuzz<F>(hook: bool, closure: F)
246where
247    F: FnMut(&[u8]) + std::panic::RefUnwindSafe,
248{
249    fuzz_with_reset(hook, closure, || {});
250}
251
252/// Like [`fuzz()`], but calls a `reset` closure after each successful iteration.
253///
254/// This is useful when the fuzz target uses static state (e.g., `OnceLock`, `lazy_static`)
255/// that must be cleared between iterations in AFL++ persistent mode. Without resetting,
256/// code paths that run only on the first iteration cause AFL's stability metric to drop.
257///
258/// ```rust,no_run
259/// # extern crate afl;
260/// # use afl::fuzz_with_reset;
261/// # use std::sync::Mutex;
262/// # static CACHE: Mutex<Option<Vec<u8>>> = Mutex::new(None);
263/// # fn main() {
264/// fuzz_with_reset(true, |data| {
265///     let mut cache = CACHE.lock().unwrap();
266///     if cache.is_none() {
267///         *cache = Some(data.to_vec());
268///     }
269/// }, || {
270///     *CACHE.lock().unwrap() = None;
271/// });
272/// # }
273/// ```
274pub fn fuzz_with_reset<F, R>(hook: bool, mut closure: F, mut reset: R)
275where
276    F: FnMut(&[u8]) + std::panic::RefUnwindSafe,
277    R: FnMut(),
278{
279    // this marker strings needs to be in the produced executable for
280    // afl-fuzz to detect `persistent mode` and `defered mode`
281    static PERSIST_MARKER: &str = "##SIG_AFL_PERSISTENT##\0";
282    static DEFERED_MARKER: &str = "##SIG_AFL_DEFER_FORKSRV##\0";
283
284    // we now need a fake instruction to prevent the compiler from optimizing out
285    // those marker strings
286    unsafe { std::ptr::read_volatile(&raw const PERSIST_MARKER) }; // hack used in https://github.com/bluss/bencher for black_box()
287    unsafe { std::ptr::read_volatile(&raw const DEFERED_MARKER) };
288    // unsafe { asm!("" : : "r"(&PERSIST_MARKER)) }; // hack used in nightly's back_box(), requires feature asm
289    // unsafe { asm!("" : : "r"(&DEFERED_MARKER)) };
290
291    if hook {
292        let prev_hook = std::panic::take_hook();
293        // sets panic hook to abort
294        std::panic::set_hook(Box::new(move |panic_info| {
295            prev_hook(panic_info);
296            std::process::abort();
297        }));
298    }
299
300    let mut input = vec![];
301
302    let loop_count = if let Ok(value) = env::var("AFL_FUZZER_LOOPCOUNT") {
303        value
304            .parse()
305            .expect("Failed to parse environment variable to a number")
306    } else {
307        usize::MAX
308    };
309
310    // initialize forkserver there
311    unsafe { __afl_manual_init() };
312
313    if unsafe { __afl_fuzz_ptr.is_null() } {
314        // In-memory testcase delivery is not enabled; the target is not running
315        // in a persistent loop, so `reset` is not needed here.
316        // Get buffer from AFL++ through stdin.
317        let result = io::stdin().read_to_end(&mut input);
318        if result.is_err() {
319            return;
320        }
321        let input_ref = &input;
322
323        let did_panic = std::panic::catch_unwind(panic::AssertUnwindSafe(|| {
324            closure(input_ref);
325        }))
326        .is_err();
327
328        if did_panic {
329            // hopefully the custom panic hook will be called before and abort the
330            // process before the stack frames are unwinded.
331            std::process::abort();
332        }
333    } else {
334        while unsafe { __afl_persistent_loop(loop_count) } != 0 {
335            // get the testcase from the fuzzer
336            let input_ref = unsafe {
337                // get the testcase from the shared memory
338                let input_len = *__afl_fuzz_len as usize;
339                std::slice::from_raw_parts(__afl_fuzz_ptr, input_len)
340            };
341
342            // We still catch unwinding panics just in case the fuzzed code modifies
343            // the panic hook.
344            // If so, the fuzzer will be unable to tell different bugs apart and you will
345            // only be able to find one bug at a time before fixing it to then find a new one.
346            let did_panic = std::panic::catch_unwind(panic::AssertUnwindSafe(|| {
347                closure(input_ref);
348            }))
349            .is_err();
350
351            if did_panic {
352                // hopefully the custom panic hook will be called before and abort the
353                // process before the stack frames are unwinded.
354                std::process::abort();
355            }
356
357            input.clear();
358            reset();
359        }
360    }
361}
362
363/// Fuzz a closure-like block of code by passing it an object of arbitrary type.
364///
365/// You can choose the type of the argument using the syntax as in the example below.
366/// Please check out the `arbitrary` crate to see which types are available.
367///
368/// For performance reasons, it is recommended that you use the native type `&[u8]` when possible.
369///
370/// ```rust,no_run
371/// # #[macro_use] extern crate afl;
372/// # fn main() {
373/// fuzz!(|data: &[u8]| {
374///     if data.len() != 6 {return}
375///     if data[0] != b'q' {return}
376///     if data[1] != b'w' {return}
377///     if data[2] != b'e' {return}
378///     if data[3] != b'r' {return}
379///     if data[4] != b't' {return}
380///     if data[5] != b'y' {return}
381///     panic!("BOOM")
382/// });
383/// # }
384/// ```
385#[macro_export]
386macro_rules! fuzz {
387    ( $($x:tt)* ) => { $crate::__fuzz!(true, $($x)*) }
388}
389
390/// Like `fuzz!` above, but panics that are caught inside the fuzzed code are not turned into
391/// crashes.
392#[macro_export]
393macro_rules! fuzz_nohook {
394    ( $($x:tt)* ) => { $crate::__fuzz!(false, $($x)*) }
395}
396
397#[doc(hidden)]
398#[macro_export]
399macro_rules! __reset_or_noop {
400    () => {
401        || {}
402    };
403    ($reset:expr) => {
404        $reset
405    };
406}
407
408#[doc(hidden)]
409#[macro_export]
410macro_rules! __fuzz {
411    ($hook:expr, |$buf:ident| $body:expr $(, $reset:expr)?) => {
412        $crate::fuzz_with_reset($hook, |$buf| $body, $crate::__reset_or_noop!($($reset)?));
413    };
414    ($hook:expr, |$buf:ident: &[u8]| $body:expr $(, $reset:expr)?) => {
415        $crate::fuzz_with_reset($hook, |$buf| $body, $crate::__reset_or_noop!($($reset)?));
416    };
417    ($hook:expr, |$buf:ident: $dty: ty| $body:expr $(, $reset:expr)?) => {
418        $crate::fuzz_with_reset(
419            $hook,
420            |$buf| {
421                let $buf: $dty = {
422                    let mut data = ::arbitrary::Unstructured::new($buf);
423                    if let Ok(d) = ::arbitrary::Arbitrary::arbitrary(&mut data).map_err(|_| "") {
424                        d
425                    } else {
426                        return;
427                    }
428                };
429
430                $body
431            },
432            $crate::__reset_or_noop!($($reset)?),
433        );
434    };
435}
436
437/// Like [`fuzz!`], but accepts a second closure that resets state after each iteration.
438///
439/// This is useful when the fuzz target uses static state (e.g., `OnceLock`, `lazy_static`)
440/// that must be cleared between iterations in AFL++ persistent mode.
441///
442/// ```rust,no_run
443/// # #[macro_use] extern crate afl;
444/// # use std::sync::Mutex;
445/// # static CACHE: Mutex<Option<Vec<u8>>> = Mutex::new(None);
446/// # fn main() {
447/// fuzz_with_reset!(|data: &[u8]| {
448///     let mut cache = CACHE.lock().unwrap();
449///     if cache.is_none() {
450///         *cache = Some(data.to_vec());
451///     }
452/// }, || {
453///     *CACHE.lock().unwrap() = None;
454/// });
455/// # }
456/// ```
457#[macro_export]
458macro_rules! fuzz_with_reset {
459    ( $($x:tt)* ) => { $crate::__fuzz!(true, $($x)*) }
460}
461
462/// Like [`fuzz_with_reset!`], but panics that are caught inside the fuzzed code are not turned
463/// into crashes.
464#[macro_export]
465macro_rules! fuzz_with_reset_nohook {
466    ( $($x:tt)* ) => { $crate::__fuzz!(false, $($x)*) }
467}