diff options
30 files changed, 1037 insertions, 80 deletions
diff --git a/README.md b/README.md index 119426f6..800c2121 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ <img align="right" src="https://raw.githubusercontent.com/andreafioraldi/AFLplusplus-website/master/static/logo_256x256.png" alt="AFL++ Logo"> - Release Version: [3.00c](https://github.com/AFLplusplus/AFLplusplus/releases) + Release Version: [3.10c](https://github.com/AFLplusplus/AFLplusplus/releases) - Github Version: 3.01a + Github Version: 3.11a Repository: [https://github.com/AFLplusplus/AFLplusplus](https://github.com/AFLplusplus/AFLplusplus) @@ -25,14 +25,14 @@ For comparisons use the fuzzbench `aflplusplus` setup, or use `afl-clang-fast` with `AFL_LLVM_CMPLOG=1`. -## Major changes in afl++ 3.0 + 3.1 +## Major changes in afl++ 3.00 + 3.10 -With afl++ 3.1 we introduced the following changes from previous behaviours: +With afl++ 3.10 we introduced the following changes from previous behaviours: * The '+' feature of the '-t' option now means to auto-calculate the timeout with the value given being the maximum timeout. The original meaning of "skipping timeouts instead of abort" is now inherent to the -t option. -With afl++ 3.0 we introduced changes that break some previous afl and afl++ +With afl++ 3.00 we introduced changes that break some previous afl and afl++ behaviours and defaults: * There are no llvm_mode and gcc_plugin subdirectories anymore and there is diff --git a/afl-system-config b/afl-system-config index d5e5ceae..9905ac81 100755 --- a/afl-system-config +++ b/afl-system-config @@ -49,6 +49,12 @@ if [ "$PLATFORM" = "FreeBSD" ] ; then sysctl kern.elf64.aslr.enable=0 } > /dev/null echo Settings applied. + cat <<EOF +In order to suppress core file generation during fuzzing it is recommended to set +me:\\ + :coredumpsize=0: +in the ~/.login_conf file for the user used for fuzzing. +EOF echo It is recommended to boot the kernel with lots of security off - if you are running a machine that is in a secured network - so set this: echo ' sysctl hw.ibrs_disable=1' echo 'Setting kern.pmap.pg_ps_enabled=0 into /boot/loader.conf might be helpful too.' @@ -60,8 +66,14 @@ if [ "$PLATFORM" = "OpenBSD" ] ; then DONE=1 fi if [ "$PLATFORM" = "DragonFly" ] ; then - echo - echo 'System security features cannot be disabled on DragonFly.' + #/sbin/sysctl kern.corefile=/dev/null + #echo Settings applied. + cat <<EOF +In order to suppress core file generation during fuzzing it is recommended to set +me:\\ + :coredumpsize=0: +in the ~/.login_conf file for the user used for fuzzing. +EOF DONE=1 fi if [ "$PLATFORM" = "NetBSD" ] ; then @@ -88,7 +100,7 @@ fi if [ "$PLATFORM" = "Haiku" ] ; then SETTINGS=~/config/settings/system/debug_server/settings [ -r ${SETTINGS} ] && grep -qE "default_action\s+kill" ${SETTINGS} && { echo "Nothing to do"; } || { \ - echo We change the debug_server default_action from user to silenty kill; \ + echo We change the debug_server default_action from user to silently kill; \ [ ! -r ${SETTINGS} ] && echo "default_action kill" >${SETTINGS} || { mv ${SETTINGS} s.tmp; sed -e "s/default_action\s\s*user/default_action kill/" s.tmp > ${SETTINGS}; rm s.tmp; }; \ echo Settings applied.; \ } diff --git a/custom_mutators/rust/.gitignore b/custom_mutators/rust/.gitignore new file mode 100644 index 00000000..088ba6ba --- /dev/null +++ b/custom_mutators/rust/.gitignore @@ -0,0 +1,10 @@ +# Generated by Cargo +# will have compiled files and executables +/target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk diff --git a/custom_mutators/rust/Cargo.toml b/custom_mutators/rust/Cargo.toml new file mode 100644 index 00000000..e36d24b5 --- /dev/null +++ b/custom_mutators/rust/Cargo.toml @@ -0,0 +1,8 @@ +[workspace] +members = [ + "custom_mutator-sys", + "custom_mutator", + "example", + # Lain needs a nightly toolchain + # "example_lain", +] \ No newline at end of file diff --git a/custom_mutators/rust/README.md b/custom_mutators/rust/README.md new file mode 100644 index 00000000..e2cc38b4 --- /dev/null +++ b/custom_mutators/rust/README.md @@ -0,0 +1,11 @@ +# Rust Custom Mutators + +Bindings to create custom mutators in Rust. + +These bindings are documented with rustdoc. To view the documentation run +```cargo doc -p custom_mutator --open```. + +A minimal example can be found in `example`. Build it using `cargo build --example example_mutator`. + +An example using [lain](https://github.com/microsoft/lain) for structured fuzzing can be found in `example_lain`. +Since lain requires a nightly rust toolchain, you need to set one up before you can play with it. diff --git a/custom_mutators/rust/custom_mutator-sys/Cargo.toml b/custom_mutators/rust/custom_mutator-sys/Cargo.toml new file mode 100644 index 00000000..104f7df0 --- /dev/null +++ b/custom_mutators/rust/custom_mutator-sys/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "custom_mutator-sys" +version = "0.1.0" +authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[build-dependencies] +bindgen = "0.56" diff --git a/custom_mutators/rust/custom_mutator-sys/build.rs b/custom_mutators/rust/custom_mutator-sys/build.rs new file mode 100644 index 00000000..3c88a90d --- /dev/null +++ b/custom_mutators/rust/custom_mutator-sys/build.rs @@ -0,0 +1,42 @@ +extern crate bindgen; + +use std::env; +use std::path::PathBuf; + +// this code is largely taken straight from the handbook: https://github.com/fitzgen/bindgen-tutorial-bzip2-sys +fn main() { + // Tell cargo to invalidate the built crate whenever the wrapper changes + println!("cargo:rerun-if-changed=wrapper.h"); + + // The bindgen::Builder is the main entry point + // to bindgen, and lets you build up options for + // the resulting bindings. + let bindings = bindgen::Builder::default() + // The input header we would like to generate + // bindings for. + .header("wrapper.h") + .whitelist_type("afl_state_t") + .blacklist_type(r"u\d+") + .opaque_type(r"_.*") + .opaque_type("FILE") + .opaque_type("in_addr(_t)?") + .opaque_type("in_port(_t)?") + .opaque_type("sa_family(_t)?") + .opaque_type("sockaddr_in(_t)?") + .opaque_type("time_t") + .rustfmt_bindings(true) + .size_t_is_usize(true) + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + .parse_callbacks(Box::new(bindgen::CargoCallbacks)) + // Finish the builder and generate the bindings. + .generate() + // Unwrap the Result and panic on failure. + .expect("Unable to generate bindings"); + + // Write the bindings to the $OUT_DIR/bindings.rs file. + let out_path = PathBuf::from(env::var("OUT_DIR").unwrap()); + bindings + .write_to_file(out_path.join("bindings.rs")) + .expect("Couldn't write bindings!"); +} diff --git a/custom_mutators/rust/custom_mutator-sys/src/lib.rs b/custom_mutators/rust/custom_mutator-sys/src/lib.rs new file mode 100644 index 00000000..a38a13a8 --- /dev/null +++ b/custom_mutators/rust/custom_mutator-sys/src/lib.rs @@ -0,0 +1,5 @@ +#![allow(non_upper_case_globals)] +#![allow(non_camel_case_types)] +#![allow(non_snake_case)] + +include!(concat!(env!("OUT_DIR"), "/bindings.rs")); diff --git a/custom_mutators/rust/custom_mutator-sys/wrapper.h b/custom_mutators/rust/custom_mutator-sys/wrapper.h new file mode 100644 index 00000000..81cdb90f --- /dev/null +++ b/custom_mutators/rust/custom_mutator-sys/wrapper.h @@ -0,0 +1,4 @@ +#include "../../../include/afl-fuzz.h" +#include "../../../include/common.h" +#include "../../../include/config.h" +#include "../../../include/debug.h" diff --git a/custom_mutators/rust/custom_mutator/Cargo.toml b/custom_mutators/rust/custom_mutator/Cargo.toml new file mode 100644 index 00000000..2d3cdbfa --- /dev/null +++ b/custom_mutators/rust/custom_mutator/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "custom_mutator" +version = "0.1.0" +authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[features] +afl_internals = ["custom_mutator-sys"] + +[dependencies] +custom_mutator-sys = { path = "../custom_mutator-sys", optional=true } diff --git a/custom_mutators/rust/custom_mutator/src/lib.rs b/custom_mutators/rust/custom_mutator/src/lib.rs new file mode 100644 index 00000000..b82af250 --- /dev/null +++ b/custom_mutators/rust/custom_mutator/src/lib.rs @@ -0,0 +1,634 @@ +//! Somewhat safe and somewhat ergonomic bindings for creating [AFL++](https://github.com/AFLplusplus/AFLplusplus) [custom mutators](https://github.com/AFLplusplus/AFLplusplus/blob/stable/docs/custom_mutators.md) in Rust. +//! +//! # Usage +//! AFL++ custom mutators are expected to be dynamic libraries which expose a set of symbols. +//! Check out [`CustomMutator`] to see which functions of the API are supported. +//! Then use [`export_mutator`] to export the correct symbols for your mutator. +//! In order to use the mutator, your crate needs to be a library crate and have a `crate-type` of `cdylib`. +//! Putting +//! ```yaml +//! [lib] +//! crate-type = ["cdylib"] +//! ``` +//! into your `Cargo.toml` should do the trick. +//! The final executable can be found in `target/(debug|release)/your_crate_name.so`. +//! # Example +//! See [`export_mutator`] for an example. +//! +//! # On `panic`s +//! This binding is panic-safe in that it will prevent panics from unwinding into AFL++. Any panic will `abort` at the boundary between the custom mutator and AFL++. +//! +//! # Access to AFL++ internals +//! This crate has an optional feature "afl_internals", which gives access to AFL++'s internal state. +//! The state is passed to [`CustomMutator::init`], when the feature is activated. +//! +//! _This is completely unsafe and uses automatically generated types extracted from the AFL++ source._ +use std::{ffi::CStr, fmt::Debug}; + +#[cfg(feature = "afl_internals")] +#[doc(hidden)] +pub use custom_mutator_sys::afl_state; + +#[allow(unused_variables)] +#[doc(hidden)] +pub trait RawCustomMutator { + #[cfg(feature = "afl_internals")] + fn init(afl: &'static afl_state, seed: c_uint) -> Self + where + Self: Sized; + #[cfg(not(feature = "afl_internals"))] + fn init(seed: u32) -> Self + where + Self: Sized; + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Option<&'b [u8]>; + + fn fuzz_count(&mut self, buffer: &[u8]) -> u32 { + 1 + } + + fn queue_new_entry(&mut self, filename_new_queue: &CStr, _filename_orig_queue: Option<&CStr>) {} + + fn queue_get(&mut self, filename: &CStr) -> bool { + true + } + + fn describe(&mut self, max_description: usize) -> Option<&CStr> { + None + } + + fn introspection(&mut self) -> Option<&CStr> { + None + } + + /*fn post_process(&self, buffer: &[u8], unsigned char **out_buf)-> usize; + int afl_custom_init_trim(&self, buffer: &[u8]); + size_t afl_custom_trim(&self, unsigned char **out_buf); + int afl_custom_post_trim(&self, unsigned char success); + size_t afl_custom_havoc_mutation(&self, buffer: &[u8], unsigned char **out_buf, size_t max_size); + unsigned char afl_custom_havoc_mutation_probability(&self);*/ +} + +/// Wrappers for the custom mutator which provide the bridging between the C API and CustomMutator. +/// These wrappers are not intended to be used directly, rather export_mutator will use them to publish the custom mutator C API. +#[doc(hidden)] +pub mod wrappers { + #[cfg(feature = "afl_internals")] + use custom_mutator_sys::afl_state; + + use core::slice; + use std::{ + any::Any, + convert::TryInto, + ffi::{c_void, CStr}, + mem::ManuallyDrop, + os::raw::c_char, + panic::catch_unwind, + process::abort, + ptr::null, + }; + + use crate::RawCustomMutator; + + /// A structure to be used as the data pointer for our custom mutator. This was used as additional storage and is kept for now in case its needed later. + /// Also has some convenience functions for FFI conversions (from and to ptr) and tries to make misuse hard (see [`FFIContext::from`]). + struct FFIContext<M: RawCustomMutator> { + mutator: M, + } + + impl<M: RawCustomMutator> FFIContext<M> { + fn from(ptr: *mut c_void) -> ManuallyDrop<Box<Self>> { + assert!(!ptr.is_null()); + ManuallyDrop::new(unsafe { Box::from_raw(ptr as *mut Self) }) + } + + fn into_ptr(self: Box<Self>) -> *const c_void { + Box::into_raw(self) as *const c_void + } + + #[cfg(feature = "afl_internals")] + fn new(afl: &'static afl_state, seed: u32) -> Box<Self> { + Box::new(Self { + mutator: M::init(afl, seed), + }) + } + #[cfg(not(feature = "afl_internals"))] + fn new(seed: u32) -> Box<Self> { + Box::new(Self { + mutator: M::init(seed), + }) + } + } + + /// panic handler called for every panic + fn panic_handler(method: &str, panic_info: Box<dyn Any + Send + 'static>) -> ! { + use std::ops::Deref; + let cause = panic_info + .downcast_ref::<String>() + .map(String::deref) + .unwrap_or_else(|| { + panic_info + .downcast_ref::<&str>() + .copied() + .unwrap_or("<cause unknown>") + }); + eprintln!("A panic occurred at {}: {}", method, cause); + abort() + } + + /// Internal function used in the macro + #[cfg(not(feature = "afl_internals"))] + pub fn afl_custom_init_<M: RawCustomMutator>(seed: u32) -> *const c_void { + match catch_unwind(|| FFIContext::<M>::new(seed).into_ptr()) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_init", err), + } + } + + /// Internal function used in the macro + #[cfg(feature = "afl_internals")] + pub fn afl_custom_init_<M: RawCustomMutator>( + afl: Option<&'static afl_state>, + seed: u32, + ) -> *const c_void { + match catch_unwind(|| { + let afl = afl.expect("mutator func called with NULL afl"); + FFIContext::<M>::new(afl, seed).into_ptr() + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_init", err), + } + } + + /// Internal function used in the macro + pub unsafe fn afl_custom_fuzz_<M: RawCustomMutator>( + data: *mut c_void, + buf: *mut u8, + buf_size: usize, + out_buf: *mut *const u8, + add_buf: *mut u8, + add_buf_size: usize, + max_size: usize, + ) -> usize { + match catch_unwind(|| { + let mut context = FFIContext::<M>::from(data); + if buf.is_null() { + panic!("null buf passed to afl_custom_fuzz") + } + if out_buf.is_null() { + panic!("null out_buf passed to afl_custom_fuzz") + } + let buff_slice = slice::from_raw_parts_mut(buf, buf_size); + let add_buff_slice = if add_buf.is_null() { + None + } else { + Some(slice::from_raw_parts(add_buf, add_buf_size)) + }; + match context + .mutator + .fuzz(buff_slice, add_buff_slice, max_size.try_into().unwrap()) + { + Some(buffer) => { + *out_buf = buffer.as_ptr(); + buffer.len().try_into().unwrap() + } + None => { + // return the input buffer with 0-length to let AFL skip this mutation attempt + *out_buf = buf; + 0 + } + } + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_fuzz", err), + } + } + + /// Internal function used in the macro + pub unsafe fn afl_custom_fuzz_count_<M: RawCustomMutator>( + data: *mut c_void, + buf: *const u8, + buf_size: usize, + ) -> u32 { + match catch_unwind(|| { + let mut context = FFIContext::<M>::from(data); + if buf.is_null() { + panic!("null buf passed to afl_custom_fuzz") + } + let buf_slice = slice::from_raw_parts(buf, buf_size); + // see https://doc.rust-lang.org/nomicon/borrow-splitting.html + let ctx = &mut **context; + let mutator = &mut ctx.mutator; + mutator.fuzz_count(buf_slice) + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_fuzz_count", err), + } + } + + /// Internal function used in the macro + pub fn afl_custom_queue_new_entry_<M: RawCustomMutator>( + data: *mut c_void, + filename_new_queue: *const c_char, + filename_orig_queue: *const c_char, + ) { + match catch_unwind(|| { + let mut context = FFIContext::<M>::from(data); + if filename_new_queue.is_null() { + panic!("received null filename_new_queue in afl_custom_queue_new_entry"); + } + let filename_new_queue = unsafe { CStr::from_ptr(filename_new_queue) }; + let filename_orig_queue = if !filename_orig_queue.is_null() { + Some(unsafe { CStr::from_ptr(filename_orig_queue) }) + } else { + None + }; + context + .mutator + .queue_new_entry(filename_new_queue, filename_orig_queue); + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_queue_new_entry", err), + } + } + + /// Internal function used in the macro + pub unsafe fn afl_custom_deinit_<M: RawCustomMutator>(data: *mut c_void) { + match catch_unwind(|| { + // drop the context + ManuallyDrop::into_inner(FFIContext::<M>::from(data)); + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_deinit", err), + } + } + + /// Internal function used in the macro + pub fn afl_custom_introspection_<M: RawCustomMutator>(data: *mut c_void) -> *const c_char { + match catch_unwind(|| { + let mut context = FFIContext::<M>::from(data); + if let Some(res) = context.mutator.introspection() { + res.as_ptr() + } else { + null() + } + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_introspection", err), + } + } + + /// Internal function used in the macro + pub fn afl_custom_describe_<M: RawCustomMutator>( + data: *mut c_void, + max_description_len: usize, + ) -> *const c_char { + match catch_unwind(|| { + let mut context = FFIContext::<M>::from(data); + if let Some(res) = context.mutator.describe(max_description_len) { + res.as_ptr() + } else { + null() + } + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_describe", err), + } + } + + /// Internal function used in the macro + pub fn afl_custom_queue_get_<M: RawCustomMutator>( + data: *mut c_void, + filename: *const c_char, + ) -> u8 { + match catch_unwind(|| { + let mut context = FFIContext::<M>::from(data); + assert!(!filename.is_null()); + + context + .mutator + .queue_get(unsafe { CStr::from_ptr(filename) }) as u8 + }) { + Ok(ret) => ret, + Err(err) => panic_handler("afl_custom_queue_get", err), + } + } +} + +/// exports the given Mutator as a custom mutator as the C interface that AFL++ expects. +/// It is not possible to call this macro multiple times, because it would define the custom mutator symbols multiple times. +/// # Example +/// ``` +/// # #[macro_use] extern crate custom_mutator; +/// # #[cfg(feature = "afl_internals")] +/// # use custom_mutator::afl_state; +/// # use custom_mutator::CustomMutator; +/// struct MyMutator; +/// impl CustomMutator for MyMutator { +/// /// ... +/// # type Error = (); +/// # #[cfg(feature = "afl_internals")] +/// # fn init(_afl_state: &afl_state, _seed: u32) -> Result<Self,()> {unimplemented!()} +/// # #[cfg(not(feature = "afl_internals"))] +/// # fn init(_seed: u32) -> Result<Self, Self::Error> {unimplemented!()} +/// # fn fuzz<'b,'s:'b>(&'s mut self, _buffer: &'b mut [u8], _add_buff: Option<&[u8]>, _max_size: usize) -> Result<Option<&'b [u8]>, Self::Error> {unimplemented!()} +/// } +/// export_mutator!(MyMutator); +/// ``` +#[macro_export] +macro_rules! export_mutator { + ($mutator_type:ty) => { + #[cfg(feature = "afl_internals")] + #[no_mangle] + pub extern "C" fn afl_custom_init( + afl: ::std::option::Option<&'static $crate::afl_state>, + seed: ::std::os::raw::c_uint, + ) -> *const ::std::os::raw::c_void { + $crate::wrappers::afl_custom_init_::<$mutator_type>(afl, seed as u32) + } + + #[cfg(not(feature = "afl_internals"))] + #[no_mangle] + pub extern "C" fn afl_custom_init( + _afl: *const ::std::os::raw::c_void, + seed: ::std::os::raw::c_uint, + ) -> *const ::std::os::raw::c_void { + $crate::wrappers::afl_custom_init_::<$mutator_type>(seed as u32) + } + + #[no_mangle] + pub extern "C" fn afl_custom_fuzz_count( + data: *mut ::std::os::raw::c_void, + buf: *const u8, + buf_size: usize, + ) -> u32 { + unsafe { + $crate::wrappers::afl_custom_fuzz_count_::<$mutator_type>(data, buf, buf_size) + } + } + + #[no_mangle] + pub extern "C" fn afl_custom_fuzz( + data: *mut ::std::os::raw::c_void, + buf: *mut u8, + buf_size: usize, + out_buf: *mut *const u8, + add_buf: *mut u8, + add_buf_size: usize, + max_size: usize, + ) -> usize { + unsafe { + $crate::wrappers::afl_custom_fuzz_::<$mutator_type>( + data, + buf, + buf_size, + out_buf, + add_buf, + add_buf_size, + max_size, + ) + } + } + + #[no_mangle] + pub extern "C" fn afl_custom_queue_new_entry( + data: *mut ::std::os::raw::c_void, + filename_new_queue: *const ::std::os::raw::c_char, + filename_orig_queue: *const ::std::os::raw::c_char, + ) { + $crate::wrappers::afl_custom_queue_new_entry_::<$mutator_type>( + data, + filename_new_queue, + filename_orig_queue, + ) + } + + #[no_mangle] + pub extern "C" fn afl_custom_queue_get( + data: *mut ::std::os::raw::c_void, + filename: *const ::std::os::raw::c_char, + ) -> u8 { + $crate::wrappers::afl_custom_queue_get_::<$mutator_type>(data, filename) + } + + #[no_mangle] + pub extern "C" fn afl_custom_introspection( + data: *mut ::std::os::raw::c_void, + ) -> *const ::std::os::raw::c_char { + $crate::wrappers::afl_custom_introspection_::<$mutator_type>(data) + } + + #[no_mangle] + pub extern "C" fn afl_custom_describe( + data: *mut ::std::os::raw::c_void, + max_description_len: usize, + ) -> *const ::std::os::raw::c_char { + $crate::wrappers::afl_custom_describe_::<$mutator_type>(data, max_description_len) + } + + #[no_mangle] + pub extern "C" fn afl_custom_deinit(data: *mut ::std::os::raw::c_void) { + unsafe { $crate::wrappers::afl_custom_deinit_::<$mutator_type>(data) } + } + }; +} + +#[cfg(test)] +/// this sanity test is supposed to just find out whether an empty mutator being exported by the macro compiles +mod sanity_test { + #[cfg(feature = "afl_internals")] + use super::afl_state; + + use super::{export_mutator, RawCustomMutator}; + + struct ExampleMutator; + + impl RawCustomMutator for ExampleMutator { + #[cfg(feature = "afl_internals")] + fn init(_afl: &afl_state, _seed: u32) -> Self { + unimplemented!() + } + + #[cfg(not(feature = "afl_internals"))] + fn init(_seed: u32) -> Self { + unimplemented!() + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + _buffer: &'b mut [u8], + _add_buff: Option<&[u8]>, + _max_size: usize, + ) -> Option<&'b [u8]> { + unimplemented!() + } + } + + export_mutator!(ExampleMutator); +} + +#[allow(unused_variables)] +/// A custom mutator. +/// [`CustomMutator::handle_error`] will be called in case any method returns an [`Result::Err`]. +pub trait CustomMutator { + /// The error type. All methods must return the same error type. + type Error: Debug; + + /// The method which handles errors. + /// By default, this method will log the error to stderr if the environment variable "`AFL_CUSTOM_MUTATOR_DEBUG`" is set and non-empty. + /// After logging the error, execution will continue on a best-effort basis. + /// + /// This default behaviour can be customized by implementing this method. + fn handle_error(err: Self::Error) { + if std::env::var("AFL_CUSTOM_MUTATOR_DEBUG") + .map(|v| !v.is_empty()) + .unwrap_or(false) + { + eprintln!("Error in custom mutator: {:?}", err) + } + } + + #[cfg(feature = "afl_internals")] + fn init(afl: &'static afl_state, seed: u32) -> Result<Self, Self::Error> + where + Self: Sized; + + #[cfg(not(feature = "afl_internals"))] + fn init(seed: u32) -> Result<Self, Self::Error> + where + Self: Sized; + + fn fuzz_count(&mut self, buffer: &[u8]) -> Result<u32, Self::Error> { + Ok(1) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result<Option<&'b [u8]>, Self::Error>; + + fn queue_new_entry( + &mut self, + filename_new_queue: &CStr, + filename_orig_queue: Option<&CStr>, + ) -> Result<(), Self::Error> { + Ok(()) + } + + fn queue_get(&mut self, filename: &CStr) -> Result<bool, Self::Error> { + Ok(true) + } + + fn describe(&mut self, max_description: usize) -> Result<Option<&CStr>, Self::Error> { + Ok(None) + } + + fn introspection(&mut self) -> Result<Option<&CStr>, Self::Error> { + Ok(None) + } +} + +impl<M> RawCustomMutator for M +where + M: CustomMutator, + M::Error: Debug, +{ + #[cfg(feature = "afl_internals")] + fn init(afl: &'static afl_state, seed: u32) -> Self + where + Self: Sized, + { + match Self::init(afl, seed) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + panic!("Error in afl_custom_init") + } + } + } + + #[cfg(not(feature = "afl_internals"))] + fn init(seed: u32) -> Self + where + Self: Sized, + { + match Self::init(seed) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + panic!("Error in afl_custom_init") + } + } + } + + fn fuzz_count(&mut self, buffer: &[u8]) -> u32 { + match self.fuzz_count(buffer) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + 0 + } + } + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Option<&'b [u8]> { + match self.fuzz(buffer, add_buff, max_size) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + None + } + } + } + + fn queue_new_entry(&mut self, filename_new_queue: &CStr, filename_orig_queue: Option<&CStr>) { + match self.queue_new_entry(filename_new_queue, filename_orig_queue) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + } + } + } + + fn queue_get(&mut self, filename: &CStr) -> bool { + match self.queue_get(filename) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + false + } + } + } + + fn describe(&mut self, max_description: usize) -> Option<&CStr> { + match self.describe(max_description) { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + None + } + } + } + + fn introspection(&mut self) -> Option<&CStr> { + match self.introspection() { + Ok(r) => r, + Err(e) => { + Self::handle_error(e); + None + } + } + } +} diff --git a/custom_mutators/rust/example/Cargo.toml b/custom_mutators/rust/example/Cargo.toml new file mode 100644 index 00000000..070d23b1 --- /dev/null +++ b/custom_mutators/rust/example/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "example_mutator" +version = "0.1.0" +authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +custom_mutator = { path = "../custom_mutator" } + +[[example]] +name = "example_mutator" +path = "./src/example_mutator.rs" +crate-type = ["cdylib"] \ No newline at end of file diff --git a/custom_mutators/rust/example/src/example_mutator.rs b/custom_mutators/rust/example/src/example_mutator.rs new file mode 100644 index 00000000..9b9d4997 --- /dev/null +++ b/custom_mutators/rust/example/src/example_mutator.rs @@ -0,0 +1,49 @@ +#![allow(unused_variables)] + +use custom_mutator::{export_mutator, CustomMutator}; + +struct ExampleMutator; + +impl CustomMutator for ExampleMutator { + type Error = (); + + fn init(seed: u32) -> Result<Self, Self::Error> { + Ok(Self) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result<Option<&'b [u8]>, Self::Error> { + buffer.reverse(); + Ok(Some(buffer)) + } +} + +struct OwnBufferExampleMutator { + own_buffer: Vec<u8>, +} + +impl CustomMutator for OwnBufferExampleMutator { + type Error = (); + + fn init(seed: u32) -> Result<Self, Self::Error> { + Ok(Self { + own_buffer: Vec::new(), + }) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result<Option<&'b [u8]>, ()> { + self.own_buffer.reverse(); + Ok(Some(self.own_buffer.as_slice())) + } +} + +export_mutator!(ExampleMutator); diff --git a/custom_mutators/rust/example_lain/Cargo.toml b/custom_mutators/rust/example_lain/Cargo.toml new file mode 100644 index 00000000..29d606a4 --- /dev/null +++ b/custom_mutators/rust/example_lain/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "example_lain" +version = "0.1.0" +authors = ["Julius Hohnerlein <julihoh@users.noreply.github.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +custom_mutator = { path = "../custom_mutator" } +lain="0.5" + +[[example]] +name = "example_lain" +path = "./src/lain_mutator.rs" +crate-type = ["cdylib"] \ No newline at end of file diff --git a/custom_mutators/rust/example_lain/rust-toolchain b/custom_mutators/rust/example_lain/rust-toolchain new file mode 100644 index 00000000..07ade694 --- /dev/null +++ b/custom_mutators/rust/example_lain/rust-toolchain @@ -0,0 +1 @@ +nightly \ No newline at end of file diff --git a/custom_mutators/rust/example_lain/src/lain_mutator.rs b/custom_mutators/rust/example_lain/src/lain_mutator.rs new file mode 100644 index 00000000..22e5fe73 --- /dev/null +++ b/custom_mutators/rust/example_lain/src/lain_mutator.rs @@ -0,0 +1,59 @@ +use custom_mutator::{export_mutator, CustomMutator}; +use lain::{ + mutator::Mutator, + prelude::*, + rand::{rngs::StdRng, SeedableRng}, +}; + +#[derive(Debug, Mutatable, NewFuzzed, BinarySerialize)] +struct MyStruct { + field_1: u8, + + #[lain(bits = 3)] + field_2: u8, + + #[lain(bits = 5)] + field_3: u8, + + #[lain(min = 5, max = 10000)] + field_4: u32, + + #[lain(ignore)] + ignored_field: u64, +} + +struct LainMutator { + mutator: Mutator<StdRng>, + buffer: Vec<u8>, +} + +impl CustomMutator for LainMutator { + type Error = (); + + fn init(seed: u32) -> Result<Self, ()> { + Ok(Self { + mutator: Mutator::new(StdRng::seed_from_u64(seed as u64)), + buffer: Vec::new(), + }) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + _buffer: &'b mut [u8], + _add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result<Option<&'b [u8]>, ()> { + // we just sample an instance of MyStruct, ignoring the current input + let instance = MyStruct::new_fuzzed(&mut self.mutator, None); + let size = instance.serialized_size(); + if size > max_size { + return Err(()); + } + self.buffer.clear(); + self.buffer.reserve(size); + instance.binary_serialize::<_, BigEndian>(&mut self.buffer); + Ok(Some(self.buffer.as_slice())) + } +} + +export_mutator!(LainMutator); diff --git a/docs/Changelog.md b/docs/Changelog.md index c4347baf..f3e15b6a 100644 --- a/docs/Changelog.md +++ b/docs/Changelog.md @@ -9,7 +9,7 @@ Want to stay in the loop on major new features? Join our mailing list by sending a mail to <afl-users+subscribe@googlegroups.com>. -### Version ++3.01a (dev) +### Version ++3.10c (release) - Mac OS ARM64 support - Android support fixed and updated by Joey Jiaojg - thanks! - New selective instrumentation option with __AFL_COVERAGE_* commands @@ -42,16 +42,17 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. - switched to an even faster RNG - added hghwng's patch for faster trace map analysis - printing suggestions for mistyped `AFL_` env variables + - added Rust bindings for custom mutators (thanks @julihoh) - afl-cc - allow instrumenting LLVMFuzzerTestOneInput - fixed endless loop for allow/blocklist lines starting with a comment (thanks to Zherya for reporting) - cmplog/redqueen now also tracks floating point, _ExtInt() + 128bit - cmplog/redqueen can now process basic libc++ and libstdc++ - std::string comparisons (though no position or length type variants) - - added support for __afl_coverage_interesting() for LTO and - and our own PCGUARD (llvm 10.0.1+), read more about this function - and selective coverage in instrumentation/README.instrument_list.md + std::string comparisons (no position or length type variants) + - added support for __afl_coverage_interesting() for LTO and our + own PCGUARD (llvm 10.0.1+), read more about this function and + selective coverage in instrumentation/README.instrument_list.md - added AFL_LLVM_INSTRUMENT option NATIVE for native clang pc-guard support (less performant than our own), GCC for old afl-gcc and CLANG for old afl-clang @@ -67,12 +68,12 @@ sending a mail to <afl-users+subscribe@googlegroups.com>. - unicornafl - Substantial speed gains in python bindings for certain use cases - Improved rust bindings - - Added a new example harness to compare python, c, and rust bindings + - Added a new example harness to compare python, c and rust bindings - afl-cmin and afl-showmap now support the -f option - afl_plot now also generates a graph on the discovered edges - changed default: no memory limit for afl-cmin and afl-cmin.bash - warn on any _AFL and __AFL env vars. - - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars. + - set AFL_IGNORE_UNKNOWN_ENVS to not warn on unknown AFL_... env vars - added dummy Makefile to instrumentation/ - Updated utils/afl_frida to be 5% faster, 7% on x86_x64 - Added `AFL_KILL_SIGNAL` env variable (thanks @v-p-b) diff --git a/docs/custom_mutators.md b/docs/custom_mutators.md index a2c544e3..61d711e4 100644 --- a/docs/custom_mutators.md +++ b/docs/custom_mutators.md @@ -4,6 +4,11 @@ This file describes how you can implement custom mutations to be used in AFL. For now, we support C/C++ library and Python module, collectivelly named as the custom mutator. +There is also experimental support for Rust in `custom_mutators/rust`. +Please refer to that directory for documentation. +Run ```cargo doc -p custom_mutator --open``` in that directory to view the +documentation in your web browser. + Implemented by - C/C++ library (`*.so`): Khaled Yakdan from Code Intelligence (<yakdan@code-intelligence.de>) - Python module: Christian Holler from Mozilla (<choller@mozilla.com>) diff --git a/include/config.h b/include/config.h index 439f4d2f..c583f23b 100644 --- a/include/config.h +++ b/include/config.h @@ -25,8 +25,8 @@ /* Version string: */ -// c = release, d = volatile github dev, e = experimental branch -#define VERSION "++3.01a" +// c = release, a = volatile github dev, e = experimental branch +#define VERSION "++3.10c" /****************************************************** * * @@ -50,7 +50,7 @@ #define CMPLOG_COMBINE /* Minimum % of the corpus to perform cmplog on. Default: 10% */ -#define CMPLOG_CORPUS_PERCENT 10U +#define CMPLOG_CORPUS_PERCENT 5U /* Number of potential positions from which we decide if cmplog becomes useless, default 8096 */ diff --git a/instrumentation/afl-compiler-rt.o.c b/instrumentation/afl-compiler-rt.o.c index e4aeadfa..ecb94cab 100644 --- a/instrumentation/afl-compiler-rt.o.c +++ b/instrumentation/afl-compiler-rt.o.c @@ -244,8 +244,12 @@ static void __afl_map_shm(void) { if (__afl_final_loc) { - if (__afl_final_loc % 32) - __afl_final_loc = (((__afl_final_loc + 31) >> 5) << 5); + if (__afl_final_loc % 64) { + + __afl_final_loc = (((__afl_final_loc + 63) >> 6) << 6); + + } + __afl_map_size = __afl_final_loc; if (__afl_final_loc > MAP_SIZE) { diff --git a/instrumentation/afl-llvm-lto-instrumentation.so.cc b/instrumentation/afl-llvm-lto-instrumentation.so.cc index f5c24e41..137bae2c 100644 --- a/instrumentation/afl-llvm-lto-instrumentation.so.cc +++ b/instrumentation/afl-llvm-lto-instrumentation.so.cc @@ -924,9 +924,7 @@ bool AFLLTOPass::runOnModule(Module &M) { if (getenv("AFL_LLVM_LTO_DONTWRITEID") == NULL) { - uint32_t write_loc = afl_global_id; - - if (afl_global_id % 32) write_loc = (((afl_global_id + 32) >> 4) << 4); + uint32_t write_loc = (((afl_global_id + 63) >> 6) << 6); GlobalVariable *AFLFinalLoc = new GlobalVariable( M, Int32Ty, true, GlobalValue::ExternalLinkage, 0, "__afl_final_loc"); diff --git a/qemu_mode/build_qemu_support.sh b/qemu_mode/build_qemu_support.sh index 50e5d4e8..815e77d6 100755 --- a/qemu_mode/build_qemu_support.sh +++ b/qemu_mode/build_qemu_support.sh @@ -374,10 +374,20 @@ if [ "$ORIG_CROSS" = "" ]; then fi fi -if ! command -v "$CROSS" > /dev/null -then +if ! command -v "$CROSS" > /dev/null ; then + if [ "$CPU_TARGET" = "$(uname -m)" ] ; then + echo "[+] Building afl++ qemu support libraries with CC=$CC" + echo "[+] Building libcompcov ..." + make -C libcompcov && echo "[+] libcompcov ready" + echo "[+] Building unsigaction ..." + make -C unsigaction && echo "[+] unsigaction ready" + echo "[+] Building libqasan ..." + make -C libqasan && echo "[+] unsigaction ready" + else echo "[!] Cross compiler $CROSS could not be found, cannot compile libcompcov libqasan and unsigaction" + fi else + echo "[+] Building afl++ qemu support libraries with CC=$CROSS" echo "[+] Building libcompcov ..." make -C libcompcov CC=$CROSS && echo "[+] libcompcov ready" echo "[+] Building unsigaction ..." diff --git a/qemu_mode/libqasan/hooks.c b/qemu_mode/libqasan/hooks.c index 405dddae..9c406c74 100644 --- a/qemu_mode/libqasan/hooks.c +++ b/qemu_mode/libqasan/hooks.c @@ -26,6 +26,8 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include "libqasan.h" #include "map_macro.h" +ssize_t (*__lq_libc_write)(int, const void *, size_t); +ssize_t (*__lq_libc_read)(int, void *, size_t); char *(*__lq_libc_fgets)(char *, int, FILE *); int (*__lq_libc_atoi)(const char *); long (*__lq_libc_atol)(const char *); @@ -35,6 +37,8 @@ void __libqasan_init_hooks(void) { __libqasan_init_malloc(); + __lq_libc_write = ASSERT_DLSYM(write); + __lq_libc_read = ASSERT_DLSYM(read); __lq_libc_fgets = ASSERT_DLSYM(fgets); __lq_libc_atoi = ASSERT_DLSYM(atoi); __lq_libc_atol = ASSERT_DLSYM(atol); @@ -42,6 +46,30 @@ void __libqasan_init_hooks(void) { } +ssize_t write(int fd, const void *buf, size_t count) { + + void *rtv = __builtin_return_address(0); + + QASAN_DEBUG("%14p: write(%d, %p, %zu)\n", rtv, fd, buf, count); + ssize_t r = __lq_libc_write(fd, buf, count); + QASAN_DEBUG("\t\t = %zd\n", r); + + return r; + +} + +ssize_t read(int fd, void *buf, size_t count) { + + void *rtv = __builtin_return_address(0); + + QASAN_DEBUG("%14p: read(%d, %p, %zu)\n", rtv, fd, buf, count); + ssize_t r = __lq_libc_read(fd, buf, count); + QASAN_DEBUG("\t\t = %zd\n", r); + + return r; + +} + #ifdef __ANDROID__ size_t malloc_usable_size(const void *ptr) { @@ -54,7 +82,7 @@ size_t malloc_usable_size(void *ptr) { QASAN_DEBUG("%14p: malloc_usable_size(%p)\n", rtv, ptr); size_t r = __libqasan_malloc_usable_size((void *)ptr); - QASAN_DEBUG("\t\t = %ld\n", r); + QASAN_DEBUG("\t\t = %zu\n", r); return r; @@ -64,7 +92,7 @@ void *malloc(size_t size) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: malloc(%ld)\n", rtv, size); + QASAN_DEBUG("%14p: malloc(%zu)\n", rtv, size); void *r = __libqasan_malloc(size); QASAN_DEBUG("\t\t = %p\n", r); @@ -76,7 +104,7 @@ void *calloc(size_t nmemb, size_t size) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: calloc(%ld, %ld)\n", rtv, nmemb, size); + QASAN_DEBUG("%14p: calloc(%zu, %zu)\n", rtv, nmemb, size); void *r = __libqasan_calloc(nmemb, size); QASAN_DEBUG("\t\t = %p\n", r); @@ -88,7 +116,7 @@ void *realloc(void *ptr, size_t size) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: realloc(%p, %ld)\n", rtv, ptr, size); + QASAN_DEBUG("%14p: realloc(%p, %zu)\n", rtv, ptr, size); void *r = __libqasan_realloc(ptr, size); QASAN_DEBUG("\t\t = %p\n", r); @@ -100,7 +128,7 @@ int posix_memalign(void **memptr, size_t alignment, size_t size) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: posix_memalign(%p, %ld, %ld)\n", rtv, memptr, alignment, + QASAN_DEBUG("%14p: posix_memalign(%p, %zu, %zu)\n", rtv, memptr, alignment, size); int r = __libqasan_posix_memalign(memptr, alignment, size); QASAN_DEBUG("\t\t = %d [*memptr = %p]\n", r, *memptr); @@ -113,7 +141,7 @@ void *memalign(size_t alignment, size_t size) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: memalign(%ld, %ld)\n", rtv, alignment, size); + QASAN_DEBUG("%14p: memalign(%zu, %zu)\n", rtv, alignment, size); void *r = __libqasan_memalign(alignment, size); QASAN_DEBUG("\t\t = %p\n", r); @@ -125,7 +153,7 @@ void *aligned_alloc(size_t alignment, size_t size) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: aligned_alloc(%ld, %ld)\n", rtv, alignment, size); + QASAN_DEBUG("%14p: aligned_alloc(%zu, %zu)\n", rtv, alignment, size); void *r = __libqasan_aligned_alloc(alignment, size); QASAN_DEBUG("\t\t = %p\n", r); @@ -137,7 +165,7 @@ void *valloc(size_t size) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: valloc(%ld)\n", rtv, size); + QASAN_DEBUG("%14p: valloc(%zu)\n", rtv, size); void *r = __libqasan_memalign(sysconf(_SC_PAGESIZE), size); QASAN_DEBUG("\t\t = %p\n", r); @@ -149,7 +177,7 @@ void *pvalloc(size_t size) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: pvalloc(%ld)\n", rtv, size); + QASAN_DEBUG("%14p: pvalloc(%zu)\n", rtv, size); size_t page_size = sysconf(_SC_PAGESIZE); size = (size & (page_size - 1)) + page_size; void *r = __libqasan_memalign(page_size, size); @@ -188,7 +216,7 @@ int memcmp(const void *s1, const void *s2, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: memcmp(%p, %p, %ld)\n", rtv, s1, s2, n); + QASAN_DEBUG("%14p: memcmp(%p, %p, %zu)\n", rtv, s1, s2, n); QASAN_LOAD(s1, n); QASAN_LOAD(s2, n); int r = __libqasan_memcmp(s1, s2, n); @@ -202,7 +230,7 @@ void *memcpy(void *dest, const void *src, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: memcpy(%p, %p, %ld)\n", rtv, dest, src, n); + QASAN_DEBUG("%14p: memcpy(%p, %p, %zu)\n", rtv, dest, src, n); QASAN_LOAD(src, n); QASAN_STORE(dest, n); void *r = __libqasan_memcpy(dest, src, n); @@ -216,7 +244,7 @@ void *mempcpy(void *dest, const void *src, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: mempcpy(%p, %p, %ld)\n", rtv, dest, src, n); + QASAN_DEBUG("%14p: mempcpy(%p, %p, %zu)\n", rtv, dest, src, n); QASAN_LOAD(src, n); QASAN_STORE(dest, n); void *r = (uint8_t *)__libqasan_memcpy(dest, src, n) + n; @@ -230,7 +258,7 @@ void *memmove(void *dest, const void *src, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: memmove(%p, %p, %ld)\n", rtv, dest, src, n); + QASAN_DEBUG("%14p: memmove(%p, %p, %zu)\n", rtv, dest, src, n); QASAN_LOAD(src, n); QASAN_STORE(dest, n); void *r = __libqasan_memmove(dest, src, n); @@ -244,7 +272,7 @@ void *memset(void *s, int c, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: memset(%p, %d, %ld)\n", rtv, s, c, n); + QASAN_DEBUG("%14p: memset(%p, %d, %zu)\n", rtv, s, c, n); QASAN_STORE(s, n); void *r = __libqasan_memset(s, c, n); QASAN_DEBUG("\t\t = %p\n", r); @@ -257,7 +285,7 @@ void *memchr(const void *s, int c, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: memchr(%p, %d, %ld)\n", rtv, s, c, n); + QASAN_DEBUG("%14p: memchr(%p, %d, %zu)\n", rtv, s, c, n); void *r = __libqasan_memchr(s, c, n); if (r == NULL) QASAN_LOAD(s, n); @@ -273,7 +301,7 @@ void *memrchr(const void *s, int c, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: memrchr(%p, %d, %ld)\n", rtv, s, c, n); + QASAN_DEBUG("%14p: memrchr(%p, %d, %zu)\n", rtv, s, c, n); QASAN_LOAD(s, n); void *r = __libqasan_memrchr(s, c, n); QASAN_DEBUG("\t\t = %p\n", r); @@ -287,7 +315,7 @@ void *memmem(const void *haystack, size_t haystacklen, const void *needle, void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: memmem(%p, %ld, %p, %ld)\n", rtv, haystack, haystacklen, + QASAN_DEBUG("%14p: memmem(%p, %zu, %p, %zu)\n", rtv, haystack, haystacklen, needle, needlelen); QASAN_LOAD(haystack, haystacklen); QASAN_LOAD(needle, needlelen); @@ -303,7 +331,7 @@ void bzero(void *s, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: bzero(%p, %ld)\n", rtv, s, n); + QASAN_DEBUG("%14p: bzero(%p, %zu)\n", rtv, s, n); QASAN_STORE(s, n); __libqasan_memset(s, 0, n); @@ -315,7 +343,7 @@ void explicit_bzero(void *s, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: bzero(%p, %ld)\n", rtv, s, n); + QASAN_DEBUG("%14p: bzero(%p, %zu)\n", rtv, s, n); QASAN_STORE(s, n); __libqasan_memset(s, 0, n); @@ -325,7 +353,7 @@ int bcmp(const void *s1, const void *s2, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: bcmp(%p, %p, %ld)\n", rtv, s1, s2, n); + QASAN_DEBUG("%14p: bcmp(%p, %p, %zu)\n", rtv, s1, s2, n); QASAN_LOAD(s1, n); QASAN_LOAD(s2, n); int r = __libqasan_bcmp(s1, s2, n); @@ -383,7 +411,7 @@ int strncasecmp(const char *s1, const char *s2, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: strncasecmp(%p, %p, %ld)\n", rtv, s1, s2, n); + QASAN_DEBUG("%14p: strncasecmp(%p, %p, %zu)\n", rtv, s1, s2, n); size_t l1 = __libqasan_strnlen(s1, n); QASAN_LOAD(s1, l1); size_t l2 = __libqasan_strnlen(s2, n); @@ -433,7 +461,7 @@ int strncmp(const char *s1, const char *s2, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: strncmp(%p, %p, %ld)\n", rtv, s1, s2, n); + QASAN_DEBUG("%14p: strncmp(%p, %p, %zu)\n", rtv, s1, s2, n); size_t l1 = __libqasan_strnlen(s1, n); QASAN_LOAD(s1, l1); size_t l2 = __libqasan_strnlen(s2, n); @@ -464,7 +492,7 @@ char *strncpy(char *dest, const char *src, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: strncpy(%p, %p, %ld)\n", rtv, dest, src, n); + QASAN_DEBUG("%14p: strncpy(%p, %p, %zu)\n", rtv, dest, src, n); size_t l = __libqasan_strnlen(src, n); QASAN_STORE(dest, n); void *r; @@ -523,7 +551,7 @@ size_t strlen(const char *s) { QASAN_DEBUG("%14p: strlen(%p)\n", rtv, s); size_t r = __libqasan_strlen(s); QASAN_LOAD(s, r + 1); - QASAN_DEBUG("\t\t = %ld\n", r); + QASAN_DEBUG("\t\t = %zu\n", r); return r; @@ -533,10 +561,10 @@ size_t strnlen(const char *s, size_t n) { void *rtv = __builtin_return_address(0); - QASAN_DEBUG("%14p: strnlen(%p, %ld)\n", rtv, s, n); + QASAN_DEBUG("%14p: strnlen(%p, %zu)\n", rtv, s, n); size_t r = __libqasan_strnlen(s, n); QASAN_LOAD(s, r); - QASAN_DEBUG("\t\t = %ld\n", r); + QASAN_DEBUG("\t\t = %zu\n", r); return r; @@ -623,7 +651,7 @@ size_t wcslen(const wchar_t *s) { QASAN_DEBUG("%14p: wcslen(%p)\n", rtv, s); size_t r = __libqasan_wcslen(s); QASAN_LOAD(s, sizeof(wchar_t) * (r + 1)); - QASAN_DEBUG("\t\t = %ld\n", r); + QASAN_DEBUG("\t\t = %zu\n", r); return r; diff --git a/qemu_mode/qemuafl b/qemu_mode/qemuafl -Subproject 213f3b27dd099ef352181c48cd75c0f20a73e3f +Subproject e36a30ebca57ca433a5d6e20b1a32975aabb761 diff --git a/src/afl-common.c b/src/afl-common.c index 078ffb9d..a306fe5e 100644 --- a/src/afl-common.c +++ b/src/afl-common.c @@ -47,6 +47,10 @@ u8 be_quiet = 0; u8 *doc_path = ""; u8 last_intr = 0; +#ifndef AFL_PATH + #define AFL_PATH "/usr/local/lib/afl/" +#endif + void detect_file_args(char **argv, u8 *prog_in, bool *use_stdin) { u32 i = 0; @@ -372,11 +376,11 @@ u8 *get_libqasan_path(u8 *own_loc) { } - if (!access(BIN_PATH "/libqasan.so", X_OK)) { + if (!access(AFL_PATH "/libqasan.so", X_OK)) { if (cp) { ck_free(cp); } - return ck_strdup(BIN_PATH "/libqasan.so"); + return ck_strdup(AFL_PATH "/libqasan.so"); } @@ -1131,7 +1135,7 @@ u32 get_map_size(void) { } - if (map_size % 32) { map_size = (((map_size >> 5) + 1) << 5); } + if (map_size % 64) { map_size = (((map_size >> 6) + 1) << 6); } } diff --git a/src/afl-forkserver.c b/src/afl-forkserver.c index 9ee59822..fd5edc98 100644 --- a/src/afl-forkserver.c +++ b/src/afl-forkserver.c @@ -656,11 +656,11 @@ void afl_fsrv_start(afl_forkserver_t *fsrv, char **argv, if (!fsrv->map_size) { fsrv->map_size = MAP_SIZE; } - if (unlikely(tmp_map_size % 32)) { + if (unlikely(tmp_map_size % 64)) { // should not happen WARNF("Target reported non-aligned map size of %u", tmp_map_size); - tmp_map_size = (((tmp_map_size + 31) >> 5) << 5); + tmp_map_size = (((tmp_map_size + 63) >> 6) << 6); } diff --git a/unicorn_mode/README.md b/unicorn_mode/README.md index f6bd4d12..b3df44fa 100644 --- a/unicorn_mode/README.md +++ b/unicorn_mode/README.md @@ -8,19 +8,19 @@ The CompareCoverage and NeverZero counters features are by Andrea Fioraldi <andr ## 1) Introduction -The code in ./unicorn_mode allows you to build a standalone feature that -leverages the Unicorn Engine and allows callers to obtain instrumentation +The code in ./unicorn_mode allows you to build the (Unicorn Engine)[https://github.com/unicorn-engine/unicorn] with afl support. +This means, you can run anything that can be emulated in unicorn and obtain instrumentation output for black-box, closed-source binary code snippets. This mechanism can be then used by afl-fuzz to stress-test targets that couldn't be built -with afl-gcc or used in QEMU mode, or with other extensions such as -TriforceAFL. +with afl-cc or used in QEMU mode. There is a significant performance penalty compared to native AFL, but at least we're able to use AFL++ on these binaries, right? ## 2) How to use -Requirements: you need an installed python environment. +First, you will need a working harness for your target in unicorn, using Python, C, or Rust. +For some pointers for more advanced emulation, take a look at [BaseSAFE](https://github.com/fgsect/BaseSAFE) and [Qiling](https://github.com/qilingframework/qiling). ### Building AFL++'s Unicorn Mode @@ -34,23 +34,23 @@ cd unicorn_mode ``` NOTE: This script checks out a Unicorn Engine fork as submodule that has been tested -and is stable-ish, based on the unicorn engine master. +and is stable-ish, based on the unicorn engine `next` branch. Building Unicorn will take a little bit (~5-10 minutes). Once it completes it automatically compiles a sample application and verifies that it works. ### Fuzzing with Unicorn Mode -To really use unicorn-mode effectively you need to prepare the following: +To use unicorn-mode effectively you need to prepare the following: * Relevant binary code to be fuzzed * Knowledge of the memory map and good starting state * Folder containing sample inputs to start fuzzing with + Same ideas as any other AFL inputs - + Quality/speed of results will depend greatly on quality of starting + + Quality/speed of results will depend greatly on the quality of starting samples + See AFL's guidance on how to create a sample corpus - * Unicornafl-based test harness which: + * Unicornafl-based test harness in Rust, C, or Python, which: + Adds memory map regions + Loads binary code into memory + Calls uc.afl_fuzz() / uc.afl_start_forkserver @@ -59,13 +59,13 @@ To really use unicorn-mode effectively you need to prepare the following: the test harness + Presumably the data to be fuzzed is at a fixed buffer address + If input constraints (size, invalid bytes, etc.) are known they - should be checked after the file is loaded. If a constraint - fails, just exit the test harness. AFL will treat the input as + should be checked in the place_input handler. If a constraint + fails, just return false from the handler. AFL will treat the input as 'uninteresting' and move on. + Sets up registers and memory state for beginning of test - + Emulates the interested code from beginning to end + + Emulates the interesting code from beginning to end + If a crash is detected, the test harness must 'crash' by - throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.) + throwing a signal (SIGSEGV, SIGKILL, SIGABORT, etc.), or indicate a crash in the crash validation callback. Once you have all those things ready to go you just need to run afl-fuzz in 'unicorn-mode' by passing in the '-U' flag: @@ -79,11 +79,12 @@ AFL's main documentation for more info about how to use afl-fuzz effectively. For a much clearer vision of what all of this looks like, please refer to the sample provided in the 'unicorn_mode/samples' directory. There is also a blog -post that goes over the basics at: +post that uses slightly older concepts, but describes the general ideas, at: [https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf](https://medium.com/@njvoss299/afl-unicorn-fuzzing-arbitrary-binary-code-563ca28936bf) -The 'helper_scripts' directory also contains several helper scripts that allow you + +The ['helper_scripts'](./helper_scripts) directory also contains several helper scripts that allow you to dump context from a running process, load it, and hook heap allocations. For details on how to use this check out the follow-up blog post to the one linked above. @@ -92,10 +93,10 @@ A example use of AFL-Unicorn mode is discussed in the paper Unicorefuzz: ## 3) Options -As for the QEMU-based instrumentation, the afl-unicorn twist of afl++ -comes with a sub-instruction based instrumentation similar in purpose to laf-intel. +As for the QEMU-based instrumentation, unicornafl comes with a sub-instruction based instrumentation similar in purpose to laf-intel. The options that enable Unicorn CompareCoverage are the same used for QEMU. +This will split up each multi-byte compare to give feedback for each correct byte. AFL_COMPCOV_LEVEL=1 is to instrument comparisons with only immediate values. AFL_COMPCOV_LEVEL=2 instruments all comparison instructions. @@ -119,6 +120,20 @@ unicornafl.monkeypatch() This will replace all unicorn imports with unicornafl inputs. -Refer to the [samples/arm_example/arm_tester.c](samples/arm_example/arm_tester.c) for an example -of how to do this properly! If you don't get this right, AFL will not -load any mutated inputs and your fuzzing will be useless! +5) Examples + +Apart from reading the documentation in `afl.c` and the python bindings of unicornafl, the best documentation are the [samples/](./samples). +The following examples exist at the time of writing: + +- c: A simple example how to use the c bindings +- compcov_x64: A python example that uses compcov to traverse hard-to-reach blocks +- persistent: A c example using persistent mode for maximum speed, and resetting the target state between each iteration +- simple: A simple python example +- speedtest/c: The c harness for an example target, used to compare c, python, and rust bindings and fix speed issues +- speedtest/python: Fuzzing the same target in python +- speedtest/rust: Fuzzing the same target using a rust harness + +Usually, the place to look at is the `harness` in each folder. The source code in each harness is pretty well documented. +Most harnesses also have the `afl-fuzz` commandline, or even offer a `make fuzz` Makefile target. +Targets in these folders, if x86, can usually be made using `make target` in each folder or get shipped pre-built (plus their source). +Especially take a look at the [speedtest documentation](./samples/speedtest/README.md) to see how the languages compare. \ No newline at end of file diff --git a/unicorn_mode/samples/c/COMPILE.md b/unicorn_mode/samples/c/COMPILE.md index 7857e5bf..7da140f7 100644 --- a/unicorn_mode/samples/c/COMPILE.md +++ b/unicorn_mode/samples/c/COMPILE.md @@ -17,6 +17,6 @@ You shouldn't need to compile simple_target.c since a X86_64 binary version is pre-built and shipped in this sample folder. This file documents how the binary was built in case you want to rebuild it or recompile it for any reason. -The pre-built binary (simple_target_x86_64.bin) was built using -g -O0 in gcc. +The pre-built binary (persistent_target_x86_64) was built using -g -O0 in gcc. We then load the binary and execute the main function directly. diff --git a/utils/afl_network_proxy/GNUmakefile b/utils/afl_network_proxy/GNUmakefile index 25a3df82..0b55dc2c 100644 --- a/utils/afl_network_proxy/GNUmakefile +++ b/utils/afl_network_proxy/GNUmakefile @@ -1,5 +1,6 @@ PREFIX ?= /usr/local BIN_PATH = $(PREFIX)/bin +HELPER_PATH = $(PREFIX)/lib/afl DOC_PATH = $(PREFIX)/share/doc/afl PROGRAMS = afl-network-client afl-network-server @@ -31,7 +32,7 @@ afl-network-client: afl-network-client.c $(CC) $(CFLAGS) -I../../include -o afl-network-client afl-network-client.c $(LDFLAGS) afl-network-server: afl-network-server.c - $(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS) + $(CC) $(CFLAGS) -I../../include -o afl-network-server afl-network-server.c ../../src/afl-forkserver.c ../../src/afl-sharedmem.c ../../src/afl-common.c -DAFL_PATH=\"$(HELPER_PATH)\" -DBIN_PATH=\"$(BIN_PATH)\" $(LDFLAGS) clean: rm -f $(PROGRAMS) *~ core diff --git a/utils/persistent_mode/persistent_demo_new.c b/utils/persistent_mode/persistent_demo_new.c index 7e694696..285f50aa 100644 --- a/utils/persistent_mode/persistent_demo_new.c +++ b/utils/persistent_mode/persistent_demo_new.c @@ -70,7 +70,7 @@ int main(int argc, char **argv) { len = __AFL_FUZZ_TESTCASE_LEN; // do not use the macro directly in a call! - fprintf(stderr, "input: %zd \"%s\"\n", len, buf); + // fprintf(stderr, "input: %zd \"%s\"\n", len, buf); /* do we have enough data? */ if (len < 8) continue; |