about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--custom_mutators/rust/.gitignore10
-rw-r--r--custom_mutators/rust/Cargo.toml7
-rw-r--r--custom_mutators/rust/README.md9
-rw-r--r--custom_mutators/rust/custom_mutator-sys/Cargo.toml12
-rw-r--r--custom_mutators/rust/custom_mutator-sys/build.rs42
-rw-r--r--custom_mutators/rust/custom_mutator-sys/src/lib.rs5
-rw-r--r--custom_mutators/rust/custom_mutator-sys/wrapper.h4
-rw-r--r--custom_mutators/rust/custom_mutator/Cargo.toml13
-rw-r--r--custom_mutators/rust/custom_mutator/src/lib.rs637
-rw-r--r--custom_mutators/rust/example/Cargo.toml13
-rw-r--r--custom_mutators/rust/example/src/lib.rs50
-rw-r--r--custom_mutators/rust/example_lain/Cargo.toml14
-rw-r--r--custom_mutators/rust/example_lain/rust-toolchain1
-rw-r--r--custom_mutators/rust/example_lain/src/lib.rs60
-rw-r--r--docs/custom_mutators.md5
15 files changed, 882 insertions, 0 deletions
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..003bc58a
--- /dev/null
+++ b/custom_mutators/rust/Cargo.toml
@@ -0,0 +1,7 @@
+[workspace]
+members = [
+    "custom_mutator-sys",
+    "custom_mutator",
+    "example",
+    "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..119ac790
--- /dev/null
+++ b/custom_mutators/rust/README.md
@@ -0,0 +1,9 @@
+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`.
+
+An example using [lain](https://github.com/microsoft/lain) 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..0fb007ab
--- /dev/null
+++ b/custom_mutators/rust/custom_mutator/src/lib.rs
@@ -0,0 +1,637 @@
+//! 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, os::raw::c_uint};
+
+#[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: c_uint) -> 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, c_uint},
+        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: c_uint) -> Box<Self> {
+            Box::new(Self {
+                mutator: M::init(afl, seed),
+            })
+        }
+        #[cfg(not(feature = "afl_internals"))]
+        fn new(seed: c_uint) -> 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: c_uint) -> *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: c_uint,
+    ) -> *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;
+/// # use std::os::raw::c_uint;
+/// struct MyMutator;
+/// impl CustomMutator for MyMutator {
+///     /// ...
+/// #  type Error = ();
+/// #  #[cfg(feature = "afl_internals")]
+/// #  fn init(_afl_state: &afl_state, _seed: c_uint) -> Result<Self,()> {unimplemented!()}
+/// #  #[cfg(not(feature = "afl_internals"))]
+/// #  fn init(_seed: c_uint) -> Result<Self,()> {unimplemented!()}
+/// #  fn fuzz<'b,'s:'b>(&'s mut self, _buffer: &'b mut [u8], _add_buff: Option<&[u8]>, _max_size: usize) -> Result<Option<&'b [u8]>,()> {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)
+        }
+
+        #[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)
+        }
+
+        #[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 {
+    use std::os::raw::c_uint;
+
+    #[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: c_uint) -> Self {
+            unimplemented!()
+        }
+
+        #[cfg(not(feature = "afl_internals"))]
+        fn init(_seed: c_uint) -> 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: c_uint) -> Result<Self, Self::Error>
+    where
+        Self: Sized;
+
+    #[cfg(not(feature = "afl_internals"))]
+    fn init(seed: c_uint) -> 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: c_uint) -> 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: c_uint) -> 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..0c89b200
--- /dev/null
+++ b/custom_mutators/rust/example/Cargo.toml
@@ -0,0 +1,13 @@
+[package]
+name = "example"
+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" }
+
+[lib]
+crate-type = ["cdylib"]
\ No newline at end of file
diff --git a/custom_mutators/rust/example/src/lib.rs b/custom_mutators/rust/example/src/lib.rs
new file mode 100644
index 00000000..4f9345c0
--- /dev/null
+++ b/custom_mutators/rust/example/src/lib.rs
@@ -0,0 +1,50 @@
+#![allow(unused_variables)]
+
+use custom_mutator::{export_mutator, CustomMutator};
+use std::os::raw::c_uint;
+
+struct ExampleMutator;
+
+impl CustomMutator for ExampleMutator {
+    type Error = ();
+
+    fn init(seed: c_uint) -> Result<Self, ()> {
+        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]>, ()> {
+        buffer.reverse();
+        Ok(Some(buffer))
+    }
+}
+
+struct OwnBufferExampleMutator {
+    own_buffer: Vec<u8>,
+}
+
+impl CustomMutator for OwnBufferExampleMutator {
+    type Error = ();
+
+    fn init(seed: c_uint) -> Result<Self, ()> {
+        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..1f68c7e0
--- /dev/null
+++ b/custom_mutators/rust/example_lain/Cargo.toml
@@ -0,0 +1,14 @@
+[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"
+
+[lib]
+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/lib.rs b/custom_mutators/rust/example_lain/src/lib.rs
new file mode 100644
index 00000000..3336e861
--- /dev/null
+++ b/custom_mutators/rust/example_lain/src/lib.rs
@@ -0,0 +1,60 @@
+use custom_mutator::{export_mutator, CustomMutator};
+use lain::{
+    mutator::Mutator,
+    prelude::*,
+    rand::{rngs::StdRng, SeedableRng},
+};
+use std::os::raw::c_uint;
+
+#[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: c_uint) -> 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/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>)