bolero_afl/
lib.rs

1//! afl plugin for bolero
2//!
3//! This crate should not be used directly. Instead, use `bolero`.
4
5#[doc(hidden)]
6#[cfg(any(test, all(feature = "lib", fuzzing_afl)))]
7pub mod fuzzer {
8    use bolero_engine::{driver, input, panic, Engine, Never, ScopedEngine, TargetLocation, Test};
9    use std::io::Read;
10
11    extern "C" {
12        // from the afl-llvm-rt
13        fn __afl_persistent_loop(counter: usize) -> isize;
14        fn __afl_manual_init();
15    }
16
17    #[used]
18    static PERSIST_MARKER: &str = "##SIG_AFL_PERSISTENT##\0";
19
20    #[used]
21    static DEFERED_MARKER: &str = "##SIG_AFL_DEFER_FORKSRV##\0";
22
23    #[derive(Debug, Default)]
24    pub struct AflEngine {}
25
26    impl AflEngine {
27        pub fn new(_location: TargetLocation) -> Self {
28            Self::default()
29        }
30    }
31
32    impl<T: Test> Engine<T> for AflEngine
33    where
34        T::Value: core::fmt::Debug,
35    {
36        type Output = Never;
37
38        fn run(self, mut test: T, options: driver::Options) -> Self::Output {
39            panic::set_hook();
40
41            let mut input = AflInput::new(options);
42
43            unsafe {
44                __afl_manual_init();
45            }
46
47            while unsafe { __afl_persistent_loop(1000) } != 0 {
48                if test.test(&mut input.test_input()).is_err() {
49                    std::process::abort();
50                }
51            }
52
53            std::process::exit(0);
54        }
55    }
56
57    impl ScopedEngine for AflEngine {
58        type Output = Never;
59
60        fn run<F, R>(self, mut test: F, options: driver::Options) -> Self::Output
61        where
62            F: FnMut() -> R + core::panic::RefUnwindSafe,
63            R: bolero_engine::IntoResult,
64        {
65            panic::set_hook();
66
67            // extend the lifetime of the bytes so it can be stored in local storage
68            let driver = bolero_engine::driver::bytes::Driver::new(vec![], &options);
69            let driver = bolero_engine::driver::object::Object(driver);
70            let mut driver = Box::new(driver);
71
72            let mut input = AflInput::new(options);
73
74            unsafe {
75                __afl_manual_init();
76            }
77
78            while unsafe { __afl_persistent_loop(1000) } != 0 {
79                input.reset();
80                let bytes = core::mem::take(&mut input.input);
81                let tmp = driver.reset(bytes, &input.options);
82                let (drv, result) = bolero_engine::any::run(driver, &mut test);
83                driver = drv;
84                input.input = driver.reset(tmp, &input.options);
85
86                if result.is_err() {
87                    std::process::abort();
88                }
89            }
90
91            std::process::exit(0);
92        }
93    }
94
95    #[derive(Debug)]
96    pub struct AflInput {
97        options: driver::Options,
98        input: Vec<u8>,
99    }
100
101    impl AflInput {
102        fn new(options: driver::Options) -> Self {
103            Self {
104                options,
105                input: vec![],
106            }
107        }
108
109        fn reset(&mut self) {
110            self.input.clear();
111            std::io::stdin()
112                .read_to_end(&mut self.input)
113                .expect("could not read next input");
114        }
115
116        fn test_input(&mut self) -> input::Bytes {
117            self.reset();
118            input::Bytes::new(&self.input, &self.options)
119        }
120    }
121}
122
123#[doc(hidden)]
124#[cfg(all(feature = "lib", fuzzing_afl))]
125pub use fuzzer::*;
126
127#[doc(hidden)]
128#[cfg(feature = "bin")]
129pub mod bin {
130    use std::{
131        ffi::CString,
132        os::raw::{c_char, c_int},
133    };
134
135    extern "C" {
136        // entrypoint for afl
137        pub fn afl_fuzz_main(a: c_int, b: *const *const c_char) -> c_int;
138    }
139
140    /// Should only be used by `cargo-bolero`
141    ///
142    /// # Safety
143    ///
144    /// Use `cargo-bolero`
145    pub unsafe fn exec<Args: Iterator<Item = String>>(args: Args) {
146        // create a vector of zero terminated strings
147        let args = args
148            .map(|arg| CString::new(arg).unwrap())
149            .collect::<Vec<_>>();
150
151        // convert the strings to raw pointers
152        let c_args = args
153            .iter()
154            .map(|arg| arg.as_ptr())
155            .chain(Some(core::ptr::null())) // add a null pointer to the end
156            .collect::<Vec<_>>();
157
158        let status = afl_fuzz_main(args.len() as c_int, c_args.as_ptr());
159        if status != 0 {
160            std::process::exit(status);
161        }
162    }
163}
164
165#[doc(hidden)]
166#[cfg(feature = "bin")]
167pub use bin::*;