1use std::env;
9use std::io::{self, Read};
10use std::os::raw::c_char;
11use std::panic;
12
13unsafe 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
22unsafe 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#[allow(non_upper_case_globals)]
221#[doc(hidden)]
222#[unsafe(no_mangle)]
223pub static mut __afl_sharedmem_fuzzing: i32 = 1;
224
225pub fn fuzz<F>(hook: bool, closure: F)
246where
247 F: FnMut(&[u8]) + std::panic::RefUnwindSafe,
248{
249 fuzz_with_reset(hook, closure, || {});
250}
251
252pub 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 static PERSIST_MARKER: &str = "##SIG_AFL_PERSISTENT##\0";
282 static DEFERED_MARKER: &str = "##SIG_AFL_DEFER_FORKSRV##\0";
283
284 unsafe { std::ptr::read_volatile(&raw const PERSIST_MARKER) }; unsafe { std::ptr::read_volatile(&raw const DEFERED_MARKER) };
288 if hook {
292 let prev_hook = std::panic::take_hook();
293 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 unsafe { __afl_manual_init() };
312
313 if unsafe { __afl_fuzz_ptr.is_null() } {
314 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 std::process::abort();
332 }
333 } else {
334 while unsafe { __afl_persistent_loop(loop_count) } != 0 {
335 let input_ref = unsafe {
337 let input_len = *__afl_fuzz_len as usize;
339 std::slice::from_raw_parts(__afl_fuzz_ptr, input_len)
340 };
341
342 let did_panic = std::panic::catch_unwind(panic::AssertUnwindSafe(|| {
347 closure(input_ref);
348 }))
349 .is_err();
350
351 if did_panic {
352 std::process::abort();
355 }
356
357 input.clear();
358 reset();
359 }
360 }
361}
362
363#[macro_export]
386macro_rules! fuzz {
387 ( $($x:tt)* ) => { $crate::__fuzz!(true, $($x)*) }
388}
389
390#[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#[macro_export]
458macro_rules! fuzz_with_reset {
459 ( $($x:tt)* ) => { $crate::__fuzz!(true, $($x)*) }
460}
461
462#[macro_export]
465macro_rules! fuzz_with_reset_nohook {
466 ( $($x:tt)* ) => { $crate::__fuzz!(false, $($x)*) }
467}