Merge pull request #771 from AFLplusplus/stable

v3.10c
This commit is contained in:
van Hauser 2021-03-01 10:13:31 +01:00 committed by GitHub
commit 41788950cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 1070 additions and 82 deletions

View File

@ -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

View File

@ -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.; \
}

10
custom_mutators/rust/.gitignore vendored Normal file
View File

@ -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

View File

@ -0,0 +1,8 @@
[workspace]
members = [
"custom_mutator-sys",
"custom_mutator",
"example",
# Lain needs a nightly toolchain
# "example_lain",
]

View File

@ -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.

View File

@ -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"

View File

@ -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!");
}

View File

@ -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"));

View File

@ -0,0 +1,4 @@
#include "../../../include/afl-fuzz.h"
#include "../../../include/common.h"
#include "../../../include/config.h"
#include "../../../include/debug.h"

View File

@ -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 }

View File

@ -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
}
}
}
}

View File

@ -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"]

View File

@ -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);

View File

@ -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"]

View File

@ -0,0 +1 @@
nightly

View File

@ -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);

View File

@ -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)

View File

@ -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>)

View File

@ -63,6 +63,31 @@ The project does not require writing new documentation or tutorials beside the
cheat sheet. The technical information for the cheat sheet will be provided by
us.
## Metrics
afl++ is a the highest performant fuzzer publicly available - but is also the
most feature rich and complex. With the publicity of afl++' success and
deployment in Google projects internally and externally and availability as
a package on most Linux distributions we see more and more issues being
created and help requests on our Discord channel that would not be
necessary if people would have read through all our documentation - which
is unrealistic.
We expect the the new documenation after this project to be cleaner, easier
accessible and lighter to digest by our users, resulting in much less
help requests. On the other hand the amount of users using afl++ should
increase as well as it will be more accessible which would also increase
questions again - but overall resulting in a reduction of help requests.
In numbers: we currently have per week on average 5 issues on Github,
10 questions on discord and 1 on mailing lists that would not be necessary
with perfect documentation and perfect people.
We would consider this project a success if afterwards we only have
2 issues on Github and 3 questions on discord anymore that would be answered
by reading the documentation. The mailing list is usually used by the most
novice users and we don't expect any less questions there.
## Project Budget
We have zero experience with technical writers, so this is very hard for us
@ -70,13 +95,19 @@ to calculate. We expect it to be a lot of work though because of the amount
of documentation we have that needs to be restructured and partially rewritten
(44 documents with 13k total lines of content).
We assume the daily rate of a very good and experienced technical writer in
times of a pandemic to be ~500$ (according to web research), and calculate
the overall amout of work to be around 20 days for everything incl. the
graphics (but again - this is basically just guessing).
Technical Writer 10000$
Volunteer stipends 0$ (waved)
T-Shirts for the top 10 contributors and helpers to this documentation project:
10 afl++ logo t-shirts 20$ each 200$
10 shipping cost of t-shirts 10$ each 100$
Total: 10.300$
(in the submission form 10.280$ was entered)
## Additional Information

View File

@ -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 */

View File

@ -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) {

View File

@ -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");

View File

@ -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 ..."

View File

@ -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;

@ -1 +1 @@
Subproject commit 213f3b27dd099ef352181c48cd75c0f20a73e3f0
Subproject commit e36a30ebca57ca433a5d6e20b1a32975aabb761b

View File

@ -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); }
}

View File

@ -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);
}

View File

@ -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.

View File

@ -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.

View File

@ -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

View File

@ -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;