From 02348dcd92eec76c483f349974830e6f23be6b71 Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 14:54:02 +0200 Subject: [PATCH 01/11] Implement persistent mode with new function fuzz() Performance goes from ~2200/s to 17000/s on my i7-7700HQ while fuzzing the `url_read` target. closes #131 partially addresses #31 --- src/lib.rs | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 8a327c119..510a06b83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,6 +83,41 @@ where } } +// this function is provided by the afl-llvm-rt static library +extern "C" { + fn __afl_persistent_loop(counter: usize) -> isize; +} + +pub fn fuzz(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe { + // this marker strings needs to be in the produced executable for + // afl-fuzz to detect `persistent mode` + static PERSIST_MARKER: &'static str = "##SIG_AFL_PERSISTENT##\0"; + + // we now need a fake instruction to prevent the compiler from optimizing out + // this marker string + unsafe{std::ptr::read_volatile(&PERSIST_MARKER)}; // hack used in https://github.com/bluss/bencher for black_box() + // unsafe { asm!("" : : "r"(&PERSIST_MARKER)) }; // hack used in nightly's back_box(), requires feature asm + + let mut input = vec![]; + + while unsafe{__afl_persistent_loop(1000)} != 0 { + // get buffer from AFL through stdin + let result = io::stdin().read_to_end(&mut input); + if result.is_err() { + return; + } + + let did_panic = std::panic::catch_unwind(|| { + closure(&input); + }).is_err(); + + if did_panic { + std::process::abort(); + } + input.clear(); + } +} + #[cfg(test)] mod test { /* From 201d80b918af22a975f17f8f692ebe7769b797f7 Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 15:01:26 +0200 Subject: [PATCH 02/11] Use panic::set_hook() to abort process See rationnal here: https://github.com/rust-fuzz/honggfuzz-rs/commit/abe2b4c93121e6fdca498e7e1645f63c9d41369d closes #134 --- src/lib.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 510a06b83..1bbb27921 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,11 @@ pub fn fuzz(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe { unsafe{std::ptr::read_volatile(&PERSIST_MARKER)}; // hack used in https://github.com/bluss/bencher for black_box() // unsafe { asm!("" : : "r"(&PERSIST_MARKER)) }; // hack used in nightly's back_box(), requires feature asm + // sets panic hook to abort + std::panic::set_hook(Box::new(|_| { + std::process::abort(); + })); + let mut input = vec![]; while unsafe{__afl_persistent_loop(1000)} != 0 { @@ -107,11 +112,17 @@ pub fn fuzz(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe { return; } + // 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. let did_panic = std::panic::catch_unwind(|| { closure(&input); }).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(); } input.clear(); From f42084697a97d6a27d7378eb6155da505dac280e Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 15:43:50 +0200 Subject: [PATCH 03/11] Implement defered forkserver mode It doesn't change much performance but why not --- src/lib.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 1bbb27921..9fb3d1638 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -83,20 +83,24 @@ where } } -// this function is provided by the afl-llvm-rt static library +// those functions are provided by the afl-llvm-rt static library extern "C" { fn __afl_persistent_loop(counter: usize) -> isize; + fn __afl_manual_init(); } pub fn fuzz(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe { // this marker strings needs to be in the produced executable for - // afl-fuzz to detect `persistent mode` + // afl-fuzz to detect `persistent mode` and `defered mode` static PERSIST_MARKER: &'static str = "##SIG_AFL_PERSISTENT##\0"; + static DEFERED_MARKER: &'static str = "##SIG_AFL_DEFER_FORKSRV##\0"; // we now need a fake instruction to prevent the compiler from optimizing out - // this marker string + // those marker strings unsafe{std::ptr::read_volatile(&PERSIST_MARKER)}; // hack used in https://github.com/bluss/bencher for black_box() + unsafe{std::ptr::read_volatile(&DEFERED_MARKER)}; // unsafe { asm!("" : : "r"(&PERSIST_MARKER)) }; // hack used in nightly's back_box(), requires feature asm + // unsafe { asm!("" : : "r"(&DEFERED_MARKER)) }; // sets panic hook to abort std::panic::set_hook(Box::new(|_| { @@ -105,6 +109,9 @@ pub fn fuzz(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe { let mut input = vec![]; + // initialize forkserver there + unsafe{__afl_manual_init()}; + while unsafe{__afl_persistent_loop(1000)} != 0 { // get buffer from AFL through stdin let result = io::stdin().read_to_end(&mut input); From b2b306a953c069d9780498af9ea37b5794a4a4b3 Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 15:48:21 +0200 Subject: [PATCH 04/11] Update RUSTFLAGS --- src/bin/cargo-afl.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/bin/cargo-afl.rs b/src/bin/cargo-afl.rs index dd723a05e..81a659dbd 100644 --- a/src/bin/cargo-afl.rs +++ b/src/bin/cargo-afl.rs @@ -150,9 +150,16 @@ where let cargo_path = env!("CARGO"); let rustflags = &format!( - "-C llvm-args=-sanitizer-coverage-level=3 \ - -C llvm-args=-sanitizer-coverage-trace-pc-guard \ + "--cfg fuzzing \ + -C debug-assertions \ + -C overflow_checks \ -C passes=sancov \ + -C llvm-args=-sanitizer-coverage-level=3 \ + -C llvm-args=-sanitizer-coverage-trace-pc-guard \ + -C llvm-args=-sanitizer-coverage-prune-blocks=0 \ + -C opt-level=3 \ + -C target-cpu=native \ + -C debuginfo=0 \ -l afl-llvm-rt \ -L {}", common::afl_llvm_rt_dir().display() From 68176b887e0c3d63e32023ea7878cf8630b45f12 Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 15:52:01 +0200 Subject: [PATCH 05/11] Make RUSTFLAGS extensible by user --- src/bin/cargo-afl.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/bin/cargo-afl.rs b/src/bin/cargo-afl.rs index 81a659dbd..3b80e6867 100644 --- a/src/bin/cargo-afl.rs +++ b/src/bin/cargo-afl.rs @@ -149,7 +149,7 @@ where { let cargo_path = env!("CARGO"); - let rustflags = &format!( + let mut rustflags = format!( "--cfg fuzzing \ -C debug-assertions \ -C overflow_checks \ @@ -161,12 +161,16 @@ where -C target-cpu=native \ -C debuginfo=0 \ -l afl-llvm-rt \ - -L {}", + -L {} ", common::afl_llvm_rt_dir().display() ); + + // add user provided flags + rustflags.push_str(&env::var("RUSTFLAGS").unwrap_or_default()); + let status = Command::new(cargo_path) .args(args) // skip `cargo` and `afl` - .env("RUSTFLAGS", rustflags) + .env("RUSTFLAGS", &rustflags) .status() .unwrap(); process::exit(status.code().unwrap_or(1)); From 0df4ca870f29d6ba2d2e78943e42bf05c6fdd111 Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 15:56:18 +0200 Subject: [PATCH 06/11] Add ASAN and TSAN flags for Rust --- src/bin/cargo-afl.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/bin/cargo-afl.rs b/src/bin/cargo-afl.rs index 3b80e6867..fa85ec56c 100644 --- a/src/bin/cargo-afl.rs +++ b/src/bin/cargo-afl.rs @@ -149,6 +149,13 @@ where { let cargo_path = env!("CARGO"); + // add some flags to sanitizers to make them work with Rust code + let asan_options = env::var("ASAN_OPTIONS").unwrap_or_default(); + let asan_options = format!("detect_odr_violation=0:{}", asan_options); + + let tsan_options = env::var("TSAN_OPTIONS").unwrap_or_default(); + let tsan_options = format!("report_signal_unsafe=0:{}", tsan_options); + let mut rustflags = format!( "--cfg fuzzing \ -C debug-assertions \ @@ -171,6 +178,8 @@ where let status = Command::new(cargo_path) .args(args) // skip `cargo` and `afl` .env("RUSTFLAGS", &rustflags) + .env("ASAN_OPTIONS", asan_options) + .env("TSAN_OPTIONS", tsan_options) .status() .unwrap(); process::exit(status.code().unwrap_or(1)); From e893d3cef2eec9bdfaeaf7ec3733bbaafcc7750e Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 16:04:15 +0200 Subject: [PATCH 07/11] Document fuzz function --- src/lib.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 9fb3d1638..05f0b7dfe 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -89,6 +89,26 @@ extern "C" { fn __afl_manual_init(); } +/// Fuzz a closure by passing it a `&[u8]` +/// +/// This slice contains a "random" quantity of "random" data. +/// +/// ```rust,should_panic +/// # extern crate afl; +/// # use afl::fuzz; +/// # fn main() { +/// fuzz(|data|{ +/// if data.len() != 6 {return} +/// if data[0] != b'q' {return} +/// if data[1] != b'w' {return} +/// if data[2] != b'e' {return} +/// if data[3] != b'r' {return} +/// if data[4] != b't' {return} +/// if data[5] != b'y' {return} +/// panic!("BOOM") +/// }); +/// # } +/// ``` pub fn fuzz(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe { // this marker strings needs to be in the produced executable for // afl-fuzz to detect `persistent mode` and `defered mode` From e8022463ba9db776553fdfd63c698eac57a1b5e6 Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 16:04:25 +0200 Subject: [PATCH 08/11] Add fuzz! macro --- Cargo.lock | 2 +- src/lib.rs | 50 +++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e36e544de..25fb8c25c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,6 @@ [[package]] name = "afl" -version = "0.3.1" +version = "0.3.2" dependencies = [ "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.30.0 (registry+https://github.com/rust-lang/crates.io-index)", diff --git a/src/lib.rs b/src/lib.rs index 05f0b7dfe..dbb1fdb73 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,7 +93,7 @@ extern "C" { /// /// This slice contains a "random" quantity of "random" data. /// -/// ```rust,should_panic +/// ```rust,ignore /// # extern crate afl; /// # use afl::fuzz; /// # fn main() { @@ -156,6 +156,54 @@ pub fn fuzz(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe { } } +/// 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. +/// +/// ```rust,ignore +/// # #[macro_use] extern crate afl; +/// # fn main() { +/// fuzz!(|data: &[u8]| { +/// if data.len() != 6 {return} +/// if data[0] != b'q' {return} +/// if data[1] != b'w' {return} +/// if data[2] != b'e' {return} +/// if data[3] != b'r' {return} +/// if data[4] != b't' {return} +/// if data[5] != b'y' {return} +/// panic!("BOOM") +/// }); +/// # } +/// ``` +#[macro_export] +macro_rules! fuzz { + (|$buf:ident| $body:block) => { + afl::fuzz(|$buf| $body); + }; + (|$buf:ident: &[u8]| $body:block) => { + afl::fuzz(|$buf| $body); + }; + (|$buf:ident: $dty: ty| $body:block) => { + afl::fuzz(|$buf| { + let $buf: $dty = { + use arbitrary::{Arbitrary, RingBuffer}; + if let Ok(d) = RingBuffer::new($buf, $buf.len()).and_then(|mut b|{ + Arbitrary::arbitrary(&mut b).map_err(|_| "") + }) { + d + } else { + return + } + }; + + $body + }); + }; +} + #[cfg(test)] mod test { /* From c4b4a5d2919e8bad3d043682fb2f426144694b18 Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 16:07:19 +0200 Subject: [PATCH 09/11] travis: Add macOS --- .travis.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.travis.yml b/.travis.yml index b4f863c87..554f1d478 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,6 @@ rust: - stable - beta - nightly +os: + - linux + - osx From d8c43ebd226f634771242231f7a19a1891706fe1 Mon Sep 17 00:00:00 2001 From: Paul Grandperrin Date: Fri, 27 Apr 2018 17:10:47 +0200 Subject: [PATCH 10/11] Deprecate non-persistent functions --- src/lib.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index dbb1fdb73..29067f5f8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,9 @@ use std::{panic, process}; /// }) /// } /// ``` +#[deprecated(since = "0.3.3", +note="This function does not use the `persistent mode` and `defered forkserver mode` and is therefore very slow. +Please use fuzz() or fuzz!() instead.")] pub fn read_stdio_bytes(closure: F) where F: Fn(Vec) + panic::RefUnwindSafe, @@ -66,6 +69,9 @@ where /// }) /// } /// ``` +#[deprecated(since = "0.3.3", +note="This function does not use the `persistent mode` and `defered forkserver mode` and is therefore very slow. +Please use fuzz() or fuzz!() instead.")] pub fn read_stdio_string(closure: F) where F: Fn(String) + panic::RefUnwindSafe, From 3bf1c0924041afdca72c20003f3996f378017d8d Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 13 May 2018 14:31:44 -0400 Subject: [PATCH 11/11] Update lib.rs --- src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 29067f5f8..0c06b7648 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -99,7 +99,7 @@ extern "C" { /// /// This slice contains a "random" quantity of "random" data. /// -/// ```rust,ignore +/// ```rust,no_run /// # extern crate afl; /// # use afl::fuzz; /// # fn main() { @@ -169,7 +169,7 @@ pub fn fuzz(closure: F) where F: Fn(&[u8]) + std::panic::RefUnwindSafe { /// /// For performance reasons, it is recommended that you use the native type `&[u8]` when possible. /// -/// ```rust,ignore +/// ```rust,no_run /// # #[macro_use] extern crate afl; /// # fn main() { /// fuzz!(|data: &[u8]| {