1#[doc(hidden)]
6#[cfg(any(test, all(feature = "lib", fuzzing_libfuzzer)))]
7pub mod fuzzer {
8 use bolero_engine::{
9 driver, input, panic, Engine, Failure, Never, ScopedEngine, TargetLocation, Test,
10 };
11 use core::time::Duration;
12 use std::{
13 ffi::CString,
14 os::raw::{c_char, c_int},
15 sync::atomic,
16 };
17
18 extern "C" {
19 pub fn LLVMFuzzerStartTest(a: c_int, b: *const *const c_char) -> c_int;
21 }
22
23 type TestFn<'a> = &'a mut dyn FnMut(&[u8]);
24
25 static mut TESTFN: Option<TestFn> = None;
26
27 #[derive(Debug, Default)]
28 pub struct LibFuzzerEngine {}
29
30 impl LibFuzzerEngine {
31 pub fn new(_location: TargetLocation) -> Self {
32 Self::default()
33 }
34 }
35
36 impl<T: Test> Engine<T> for LibFuzzerEngine
37 where
38 T::Value: core::fmt::Debug,
39 {
40 type Output = Never;
41
42 fn run(self, mut test: T, options: driver::Options) -> Self::Output {
43 panic::set_hook();
44 panic::forward_panic(false);
45
46 let options = &options;
47 let mut cache = driver::cache::Cache::default();
48 let mut report = GeneratorReport::default();
49 report.spawn_timer();
50
51 start(&mut |slice: &[u8]| {
52 let mut input = input::cache::Bytes::new(slice, options, &mut cache);
53
54 match test.test(&mut input) {
55 Ok(is_valid) => {
56 report.on_result(is_valid);
57 }
58 Err(error) => {
59 eprintln!("test failed; shrinking input...");
60
61 let shrunken = test.shrink(slice.to_vec(), None, options);
62
63 if let Some(shrunken) = shrunken {
64 eprintln!("{:#}", shrunken);
65 } else {
66 let input = input::Bytes::new(slice, options);
67 eprintln!(
68 "{:#}",
69 Failure {
70 seed: None,
71 error,
72 input
73 }
74 );
75 }
76
77 std::process::abort();
78 }
79 }
80 })
81 }
82 }
83
84 impl ScopedEngine for LibFuzzerEngine {
85 type Output = Never;
86
87 fn run<F, R>(self, mut test: F, options: driver::Options) -> Self::Output
88 where
89 F: FnMut() -> R + core::panic::RefUnwindSafe,
90 R: bolero_engine::IntoResult,
91 {
92 panic::set_hook();
93 panic::forward_panic(false);
94
95 let options = &options;
96 let mut report = GeneratorReport::default();
99 report.spawn_timer();
100
101 let driver = bolero_engine::driver::bytes::Driver::new(&[][..], options);
103 let driver = bolero_engine::driver::object::Object(driver);
104 let driver = Box::new(driver);
105 let mut driver = Some(driver);
106
107 start(&mut |slice: &[u8]| {
108 let input: &'static [u8] = unsafe { core::mem::transmute::<&[u8], &[u8]>(slice) };
110 let mut drv = driver.take().unwrap();
111 drv.reset(input, options);
112 let (drv, result) = bolero_engine::any::run(drv, &mut test);
113 driver = Some(drv);
114
115 match result {
116 Ok(is_valid) => {
117 report.on_result(is_valid);
118 }
119 Err(error) => {
120 eprintln!(
121 "{:#}",
122 Failure {
123 seed: None,
124 error,
125 input: (),
126 }
127 );
128
129 std::process::abort();
130 }
131 }
132 });
133 }
134 }
135
136 #[derive(Default)]
137 struct GeneratorReport {
138 total_runs: u64,
139 window_runs: u64,
140 total_valid: u64,
141 window_valid: u64,
142 should_print: std::sync::Arc<atomic::AtomicBool>,
143 }
144
145 impl GeneratorReport {
146 pub fn spawn_timer(&self) {
147 let should_print = self.should_print.clone();
148 std::thread::spawn(move || {
149 while std::sync::Arc::strong_count(&should_print) > 1 {
150 std::thread::sleep(Duration::from_secs(1));
151 should_print.store(true, atomic::Ordering::Relaxed);
152 }
153 });
154 }
155
156 pub fn on_result(&mut self, is_valid: bool) {
157 self.window_runs += 1;
158 if is_valid {
159 self.window_valid += 1;
160 }
161
162 if self.window_runs == self.window_valid {
164 return;
165 }
166
167 if !self.should_print.swap(false, atomic::Ordering::Relaxed) {
168 return;
169 }
170
171 self.total_runs += self.window_runs;
172 self.total_valid += self.window_valid;
173
174 let total_perc = self.total_valid as f32 / self.total_runs as f32 * 100.0;
175 let window_perc = self.window_valid as f32 / self.window_runs as f32 * 100.0;
176 println!(
177 "#{}\tGENERATE\tvalid: {} ({:.2}%) valid/s: {} ({:.2}%)",
178 self.total_runs, self.total_valid, total_perc, self.window_valid, window_perc,
179 );
180 self.window_runs = 0;
181 self.window_valid = 0;
182 }
183 }
184
185 fn start<F: FnMut(&[u8])>(run_one_test: &mut F) -> Never {
186 unsafe {
187 TESTFN = Some(std::mem::transmute::<TestFn, TestFn>(
188 run_one_test as &mut dyn FnMut(&[u8]),
189 ));
190 }
191
192 let recursion_level = std::env::var("__BOLERO_LIBFUZZER_RECURSE")
195 .as_deref()
196 .unwrap_or("0")
197 .parse()
198 .unwrap_or(usize::MAX);
199
200 if recursion_level > 10 {
201 eprintln!("LOOPING BINARY");
202 std::process::exit(1);
203 }
204
205 std::env::set_var(
206 "__BOLERO_LIBFUZZER_RECURSE",
207 (recursion_level + 1).to_string(),
208 );
209
210 let args = std::env::args()
212 .next()
213 .as_deref()
214 .into_iter()
215 .chain(
216 std::env::var("BOLERO_LIBFUZZER_ARGS")
217 .expect("missing libfuzzer args")
218 .split(' '),
219 )
220 .map(|arg| CString::new(arg).unwrap())
221 .collect::<Vec<_>>();
222
223 let c_args = args
225 .iter()
226 .map(|arg| arg.as_ptr())
227 .chain(Some(core::ptr::null())) .collect::<Vec<_>>();
229
230 let res = unsafe { LLVMFuzzerStartTest(args.len() as c_int, c_args.as_ptr()) };
231
232 std::process::exit(res);
233 }
234
235 #[doc(hidden)]
236 #[no_mangle]
237 pub unsafe extern "C" fn LLVMFuzzerTestOneInput(data: *const u8, size: usize) -> i32 {
238 let data_slice = std::slice::from_raw_parts(data, size);
239 (TESTFN.as_mut().expect("uninitialized test function"))(data_slice);
240 0
241 }
242
243 #[doc(hidden)]
244 #[no_mangle]
245 pub unsafe extern "C" fn LLVMFuzzerInitialize(
246 _argc: *const isize,
247 _argv: *const *const *const u8,
248 ) -> isize {
249 0
250 }
251}
252
253#[doc(hidden)]
254#[cfg(all(feature = "lib", fuzzing_libfuzzer))]
255pub use fuzzer::*;