1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
//! ## About Honggfuzz
//! 
//! Honggfuzz is a security oriented fuzzer with powerful analysis options. Supports evolutionary, feedback-driven fuzzing based on code coverage (software- and hardware-based).
//! 
//! * project homepage [honggfuzz.com](http://honggfuzz.com/)
//! * project repository [github.com/google/honggfuzz](https://github.com/google/honggfuzz)
//! * this upstream project is maintained by Google, but ...
//! * this is NOT an official Google product
//! 
//! ## Compatibility
//! 
//! * __Rust__: stable, beta, nightly
//! * __OS__: GNU/Linux, macOS, FreeBSD, NetBSD, Android, WSL (Windows Subsystem for Linux)
//! * __Arch__: x86_64, x86, arm64-v8a, armeabi-v7a, armeabi
//! * __Sanitizer__: none, address, thread, leak 
//! 
//! ## Dependencies
//! 
//! ### Linux
//! 
//! * C compiler: `cc`
//! * GNU Make: `make`
//! * GNU Binutils development files for the BFD library: `libbfd.h`
//! * libunwind development files: `libunwind.h`
//! 
//! For example on Debian and its derivatives:
//! 
//! ```sh
//! sudo apt install build-essential binutils-dev libunwind-dev
//! ```
//! 
//! ## How to use this crate
//! 
//! Install honggfuzz commands to build with instrumentation and fuzz
//! 
//! ```sh
//! # installs hfuzz and honggfuzz subcommands in cargo
//! cargo install honggfuzz
//! ```
//! 
//! Add to your dependencies
//! 
//! ```toml
//! [dependencies]
//! honggfuzz = "0.5"
//! ```
//! 
//! Create a target to fuzz
//! 
//! ```rust,should_panic
//! use honggfuzz::fuzz;
//! 
//! fn main() {
//!     // Here you can parse `std::env::args and 
//!     // setup / initialize your project
//! 
//!     // You have full control over the loop but
//!     // you're supposed to call `fuzz` ad vitam aeternam
//!     loop {
//!         // The fuzz macro gives an arbitrary object (see `arbitrary crate`)
//!         // to a closure-like block of code.
//!         // For performance reasons, it is recommended that you use the native type
//!         // `&[u8]` when possible.
//!         // Here, this slice will contain a "random" quantity of "random" data.
//!         fuzz!(|data: &[u8]| {
//!             if data.len() != 3 {return}
//!             if data[0] != b'h' {return}
//!             if data[1] != b'e' {return}
//!             if data[2] != b'y' {return}
//!             panic!("BOOM")
//!         });
//!     }
//! }
//! 
//! ```
//! 
//! Fuzz for fun and profit !
//! 
//! ```sh
//! # builds with fuzzing instrumentation and then fuzz the "example" target
//! cargo hfuzz run example
//! ```
//! 
//! Once you got a crash, replay it easily in a debug environment
//! 
//! ```sh
//! # builds the target in debug mode and replays automatically the crash in gdb
//! cargo hfuzz run-debug example fuzzing_workspace/*.fuzz
//! ```
//! 
//! You can also build and run your project without compile-time software instrumentation (LLVM's SanCov passes)
//! 
//! This allows you for example to try hardware-only feedback driven fuzzing:
//! 
//! ```sh
//! # builds without fuzzing instrumentation and then fuzz the "example" target using hardware-based feedback
//! HFUZZ_RUN_ARGS="--linux_perf_ipt_block --linux_perf_instr --linux_perf_branch" cargo hfuzz run-no-instr example
//! ```
//! 
//! Clean
//! 
//! ```sh
//! # a wrapper on "cargo clean" which cleans the fuzzing_target directory
//! cargo hfuzz clean 
//! ```
//! 
//! Version
//! 
//! ```sh
//! cargo hfuzz version
//! ```
//! 
//! ### Environment variables
//! 
//! #### `RUSTFLAGS`
//! 
//! You can use `RUSTFLAGS` to send additional arguments to `rustc`.
//! 
//! For instance, you can enable the use of LLVM's [sanitizers](https://github.com/japaric/rust-san).
//! This is a recommended option if you want to test your `unsafe` rust code but it will have an impact on performance.
//! 
//! ```sh
//! RUSTFLAGS="-Z sanitizer=address" cargo hfuzz run example
//! ```
//! 
//! #### `HFUZZ_BUILD_ARGS`
//! 
//! You can use `HFUZZ_BUILD_ARGS` to send additional arguments to `cargo build`.
//! 
//! #### `HFUZZ_RUN_ARGS`
//! 
//! You can use `HFUZZ_RUN_ARGS` to send additional arguments to `honggfuzz`.
//! See [USAGE](https://github.com/google/honggfuzz/blob/master/docs/USAGE.md) for the list of those.
//! 
//! For example:
//! 
//! ```sh
//! # 1 second of timeout
//! # use 12 fuzzing thread
//! # be verbose
//! # stop after 1000000 fuzzing iteration
//! # exit upon crash
//! HFUZZ_RUN_ARGS="-t 1 -n 12 -v -N 1000000 --exit_upon_crash" cargo hfuzz run example
//! ```
//! 
//! #### `HFUZZ_DEBUGGER`
//! 
//! By default we use `rust-lldb` but you can change it to `rust-gdb`, `gdb`, `/usr/bin/lldb-7` ...
//! 
//! #### `CARGO_TARGET_DIR`
//! 
//! Target compilation directory, defaults to `hfuzz_target` to not clash with `cargo build`'s default `target` directory.
//! 
//! #### `HFUZZ_WORKSPACE`
//! 
//! Honggfuzz working directory, defaults to `hfuzz_workspace`.
//! 
//! #### `HFUZZ_INPUT`
//! 
//! Honggfuzz input files (also called "corpus"), defaults to `$HFUZZ_WORKSPACE/{TARGET}/input`.
//! 
//! ## Conditionnal compilation
//! 
//! Sometimes, it is necessary to make some specific adaptation to your code to yield a better fuzzing efficiency.
//! 
//! For instance:
//! - Make you software behavior as much as possible deterministic on the fuzzing input
//!   - [PRNG](https://en.wikipedia.org/wiki/Pseudorandom_number_generator)s must be seeded with a constant or the fuzzer input
//!   - Behavior shouldn't change based on the computer's clock.
//!   - Avoid potential undeterministic behavior from racing threads.
//!   - ...
//! - Never ever call `std::process::exit()`.
//! - Disable logging and other unnecessary functionnalities.
//! - Try to avoid modifying global state when possible.
//! 
//! 
//! When building with `cargo hfuzz`, the argument `--cfg fuzzing` is passed to `rustc` to allow you to condition the compilation of thoses adaptations thanks to the `cfg` macro like so:
//! 
//! ```rust
//! # use rand::{self, Rng, SeedableRng};
//! # use rand_chacha;
//! # fn main() {
//! #[cfg(fuzzing)]
//! let mut rng = rand_chacha::ChaCha8Rng::from_seed(&[0]);
//! #[cfg(not(fuzzing))]
//! let mut rng = rand::thread_rng();
//! # }
//! ```
//! 
//! Also, when building in debug mode, the `fuzzing_debug` argument is added in addition to `fuzzing`.
//! 
//! For more information about conditional compilation, please see the [reference](https://doc.rust-lang.org/reference/attributes.html#conditional-compilation).
//! 
//! ## Relevant documentation about honggfuzz
//! * [USAGE](https://github.com/google/honggfuzz/blob/master/docs/USAGE.md)
//! * [FeedbackDrivenFuzzing](https://github.com/google/honggfuzz/blob/master/docs/FeedbackDrivenFuzzing.md)
//! * [PersistentFuzzing](https://github.com/google/honggfuzz/blob/master/docs/PersistentFuzzing.md)
//! 
//! ## About Rust fuzzing
//!  
//! There is other projects providing Rust fuzzing support at [github.com/rust-fuzz](https://github.com/rust-fuzz). 
//!  
//! You'll find support for [AFL](https://github.com/rust-fuzz/afl.rs) and LLVM's [LibFuzzer](https://github.com/rust-fuzz/cargo-fuzz) and there is also a [trophy case](https://github.com/rust-fuzz/trophy-case) ;-) .
//! 
//! This crate was inspired by those projects!

/// Re-export of arbitrary crate used to generate structured inputs
pub use arbitrary;

#[cfg(all(fuzzing, not(fuzzing_debug)))]
extern "C" {
    fn HF_ITER(buf_ptr: *mut *const u8, len_ptr: *mut usize );
}

/// Fuzz a closure by passing it a `&[u8]`
///
/// This slice contains a "random" quantity of "random" data.
///
/// For perstistent fuzzing to work, you have to call it ad vita aeternam in an infinite loop.
///
/// The closure is assumed to be unwind-safe, which might be unsafe. For more info, check the
/// [`std::panic::UnwindSafe`] trait.
///
/// ```rust,should_panic
/// # use honggfuzz::fuzz;
/// # fn main() {
/// loop {
///     fuzz(|data|{
///         if data.len() != 3 {return}
///         if data[0] != b'h' {return}
///         if data[1] != b'e' {return}
///         if data[2] != b'y' {return}
///         panic!("BOOM")
///     });
/// }
/// # }
/// ```
#[cfg(not(fuzzing))]
#[allow(unused_variables)]
pub fn fuzz<F>(closure: F) where F: FnOnce(&[u8]) {
    eprintln!("This executable hasn't been built with \"cargo hfuzz\".");
    eprintln!("Try executing \"cargo hfuzz build\" and check out \"hfuzz_target\" directory.");
    eprintln!("Or execute \"cargo hfuzz run TARGET\"");
    std::process::exit(17);
}

// Registers a panic hook that aborts the process before unwinding.
// It is useful to abort before unwinding so that the fuzzer will then be
// able to analyse the process stack frames to tell different bugs appart.
#[cfg(all(fuzzing, not(fuzzing_debug)))]
lazy_static::lazy_static! {
    static ref PANIC_HOOK: () = {
        std::panic::set_hook(Box::new(|_| {
            std::process::abort();
        }))
    };
}

#[cfg(all(fuzzing, not(fuzzing_debug)))]
pub fn fuzz<F>(closure: F) where F: FnOnce(&[u8]) {
    use std::mem::MaybeUninit;

    // sets panic hook if not already done
    lazy_static::initialize(&PANIC_HOOK);

    // get buffer from honggfuzz runtime
    let buf;

    let mut buf_ptr = MaybeUninit::<*const u8>::uninit();
    let mut len_ptr = MaybeUninit::<usize>::uninit();

    unsafe {
        HF_ITER(buf_ptr.as_mut_ptr(), len_ptr.as_mut_ptr());
        buf = ::std::slice::from_raw_parts(buf_ptr.assume_init(), len_ptr.assume_init());
    }

    // We still catch unwinding panics just in case the fuzzed code modifies
    // the panic hook.
    // If so, the fuzzer will be unable to tell different bugs appart and you will
    // only be able to find one bug at a time before fixing it to then find a new one.
    // The closure is assumed to be unwind-safe, which might be unsafe. For more info, check the
    // [`std::panic::UnwindSafe`] trait.
    let did_panic = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
        closure(buf);
    })).is_err();

    if did_panic {
        // hopefully the custom panic hook will be called before and abort the
        // process before the stack frames are unwinded.
        std::process::abort();
    }
}

#[cfg(all(fuzzing, fuzzing_debug))]
pub fn fuzz<F>(closure: F) where F: FnOnce(&[u8]) {
    use std::env;
    use std::fs::File;
    use memmap::MmapOptions;
    
    let filename = env::var("CARGO_HONGGFUZZ_CRASH_FILENAME").unwrap_or_else(|_|{
        eprintln!("error: Environment variable CARGO_HONGGFUZZ_CRASH_FILENAME not set. Try launching with \"cargo hfuzz run-debug TARGET CRASH_FILENAME [ ARGS ... ]\"");
        std::process::exit(1);
    });

    let file = File::open(&filename).unwrap_or_else(|_|{
        eprintln!("error: failed to open \"{}\"", &filename);
        std::process::exit(1);
    });

    let mmap = unsafe {MmapOptions::new().map(&file)}.unwrap_or_else(|_|{
        eprintln!("error: failed to mmap file \"{}\"", &filename);
        std::process::exit(1);
    });

    closure(&mmap);

    eprintln!("This crashfile didn't trigger any panics...");
    eprintln!("Are you sure that you selected the correct crashfile and that your program's behavior is entirely deterministic and only dependent on the fuzzing input?");
    std::process::exit(2);
}

/// Fuzz a closure-like block of code by passing it an object of arbitrary type.
///
/// You can choose the type of the argument using the syntax as in the example below.
/// Please check out the `arbitrary` crate to see which types are available.
///
/// For performance reasons, it is recommended that you use the native type `&[u8]` when possible.
///
/// For perstistent fuzzing to work, you have to call it ad vita aeternam in an infinite loop.
///
/// ```rust,should_panic
/// # use honggfuzz::fuzz;
/// # fn main() {
/// loop {
///     fuzz!(|data: &[u8]| {
///         if data.len() != 3 {return}
///         if data[0] != b'h' {return}
///         if data[1] != b'e' {return}
///         if data[2] != b'y' {return}
///         panic!("BOOM")
///     });
/// }
/// # }
/// ```

#[macro_export]
macro_rules! fuzz {
    (|$buf:ident| $body:block) => {
        $crate::fuzz(|$buf| $body);
    };
    (|$buf:ident: &[u8]| $body:block) => {
        $crate::fuzz(|$buf| $body);
    };
    (|$buf:ident: $dty:ty| $body:block) => {
        $crate::fuzz(|$buf| {
            let $buf: $dty = {
                use $crate::arbitrary::{Arbitrary, Unstructured};

                let mut buf = Unstructured::new($buf); 
                if let Ok(buf) = Arbitrary::arbitrary(&mut buf) {
                    buf
                } else {
                    return
                }
            };

            $body
        });
    };
}