From a5da9ce42cab1eab93cf80ca744944ae26e6ab58 Mon Sep 17 00:00:00 2001 From: julihoh Date: Sat, 27 Feb 2021 15:05:13 +0100 Subject: custom mutator rust support (#752) * custom mutator rust support * clarify how to view documentation for rust mutators * remove `FuzzResult` hack and clarify lifetimes of CustomMutator::fuzz * rename TErr associated tyep to Error to be more idiomatic * fix warnings * add example for fallible custom mutator * make Fallible Custom Mutator the default and implement it's handle_err method by default * rename CustomMutator::handle_err to handle_error * add example mutator using lain --- custom_mutators/rust/example/Cargo.toml | 13 +++++++++ custom_mutators/rust/example/src/lib.rs | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 custom_mutators/rust/example/Cargo.toml create mode 100644 custom_mutators/rust/example/src/lib.rs (limited to 'custom_mutators/rust/example') 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 "] +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 { + Ok(Self) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result, ()> { + buffer.reverse(); + Ok(Some(buffer)) + } +} + +struct OwnBufferExampleMutator { + own_buffer: Vec, +} + +impl CustomMutator for OwnBufferExampleMutator { + type Error = (); + + fn init(seed: c_uint) -> Result { + 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, ()> { + self.own_buffer.reverse(); + Ok(Some(self.own_buffer.as_slice())) + } +} + +export_mutator!(ExampleMutator); -- cgit 1.4.1 From c219502f0fe654927c3f74f9791f786f0740f344 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Sat, 27 Feb 2021 15:52:36 +0100 Subject: some rust cleanup --- custom_mutators/rust/Cargo.toml | 3 +- custom_mutators/rust/README.md | 6 ++- custom_mutators/rust/custom_mutator/src/lib.rs | 39 +++++++------- custom_mutators/rust/example/Cargo.toml | 6 ++- .../rust/example/src/example_mutator.rs | 49 ++++++++++++++++++ custom_mutators/rust/example/src/lib.rs | 50 ------------------ custom_mutators/rust/example_lain/Cargo.toml | 4 +- .../rust/example_lain/src/lain_mutator.rs | 59 +++++++++++++++++++++ custom_mutators/rust/example_lain/src/lib.rs | 60 ---------------------- 9 files changed, 139 insertions(+), 137 deletions(-) create mode 100644 custom_mutators/rust/example/src/example_mutator.rs delete mode 100644 custom_mutators/rust/example/src/lib.rs create mode 100644 custom_mutators/rust/example_lain/src/lain_mutator.rs delete mode 100644 custom_mutators/rust/example_lain/src/lib.rs (limited to 'custom_mutators/rust/example') diff --git a/custom_mutators/rust/Cargo.toml b/custom_mutators/rust/Cargo.toml index 003bc58a..e36d24b5 100644 --- a/custom_mutators/rust/Cargo.toml +++ b/custom_mutators/rust/Cargo.toml @@ -3,5 +3,6 @@ members = [ "custom_mutator-sys", "custom_mutator", "example", - "example_lain", + # 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 index 119ac790..e2cc38b4 100644 --- a/custom_mutators/rust/README.md +++ b/custom_mutators/rust/README.md @@ -1,9 +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`. +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) can be found in `example_lain`. +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/src/lib.rs b/custom_mutators/rust/custom_mutator/src/lib.rs index 0fb007ab..b82af250 100644 --- a/custom_mutators/rust/custom_mutator/src/lib.rs +++ b/custom_mutators/rust/custom_mutator/src/lib.rs @@ -23,7 +23,7 @@ //! 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}; +use std::{ffi::CStr, fmt::Debug}; #[cfg(feature = "afl_internals")] #[doc(hidden)] @@ -37,7 +37,7 @@ pub trait RawCustomMutator { where Self: Sized; #[cfg(not(feature = "afl_internals"))] - fn init(seed: c_uint) -> Self + fn init(seed: u32) -> Self where Self: Sized; @@ -87,7 +87,7 @@ pub mod wrappers { convert::TryInto, ffi::{c_void, CStr}, mem::ManuallyDrop, - os::raw::{c_char, c_uint}, + os::raw::c_char, panic::catch_unwind, process::abort, ptr::null, @@ -112,13 +112,13 @@ pub mod wrappers { } #[cfg(feature = "afl_internals")] - fn new(afl: &'static afl_state, seed: c_uint) -> Box { + fn new(afl: &'static afl_state, seed: u32) -> Box { Box::new(Self { mutator: M::init(afl, seed), }) } #[cfg(not(feature = "afl_internals"))] - fn new(seed: c_uint) -> Box { + fn new(seed: u32) -> Box { Box::new(Self { mutator: M::init(seed), }) @@ -143,7 +143,7 @@ pub mod wrappers { /// Internal function used in the macro #[cfg(not(feature = "afl_internals"))] - pub fn afl_custom_init_(seed: c_uint) -> *const c_void { + pub fn afl_custom_init_(seed: u32) -> *const c_void { match catch_unwind(|| FFIContext::::new(seed).into_ptr()) { Ok(ret) => ret, Err(err) => panic_handler("afl_custom_init", err), @@ -154,7 +154,7 @@ pub mod wrappers { #[cfg(feature = "afl_internals")] pub fn afl_custom_init_( afl: Option<&'static afl_state>, - seed: c_uint, + seed: u32, ) -> *const c_void { match catch_unwind(|| { let afl = afl.expect("mutator func called with NULL afl"); @@ -328,16 +328,15 @@ pub mod wrappers { /// # #[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 {unimplemented!()} +/// # fn init(_afl_state: &afl_state, _seed: u32) -> Result {unimplemented!()} /// # #[cfg(not(feature = "afl_internals"))] -/// # fn init(_seed: c_uint) -> Result {unimplemented!()} -/// # fn fuzz<'b,'s:'b>(&'s mut self, _buffer: &'b mut [u8], _add_buff: Option<&[u8]>, _max_size: usize) -> Result,()> {unimplemented!()} +/// # fn init(_seed: u32) -> Result {unimplemented!()} +/// # fn fuzz<'b,'s:'b>(&'s mut self, _buffer: &'b mut [u8], _add_buff: Option<&[u8]>, _max_size: usize) -> Result, Self::Error> {unimplemented!()} /// } /// export_mutator!(MyMutator); /// ``` @@ -350,7 +349,7 @@ macro_rules! export_mutator { 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) + $crate::wrappers::afl_custom_init_::<$mutator_type>(afl, seed as u32) } #[cfg(not(feature = "afl_internals"))] @@ -359,7 +358,7 @@ macro_rules! export_mutator { _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) + $crate::wrappers::afl_custom_init_::<$mutator_type>(seed as u32) } #[no_mangle] @@ -442,8 +441,6 @@ macro_rules! export_mutator { #[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; @@ -453,12 +450,12 @@ mod sanity_test { impl RawCustomMutator for ExampleMutator { #[cfg(feature = "afl_internals")] - fn init(_afl: &afl_state, _seed: c_uint) -> Self { + fn init(_afl: &afl_state, _seed: u32) -> Self { unimplemented!() } #[cfg(not(feature = "afl_internals"))] - fn init(_seed: c_uint) -> Self { + fn init(_seed: u32) -> Self { unimplemented!() } @@ -497,12 +494,12 @@ pub trait CustomMutator { } #[cfg(feature = "afl_internals")] - fn init(afl: &'static afl_state, seed: c_uint) -> Result + fn init(afl: &'static afl_state, seed: u32) -> Result where Self: Sized; #[cfg(not(feature = "afl_internals"))] - fn init(seed: c_uint) -> Result + fn init(seed: u32) -> Result where Self: Sized; @@ -544,7 +541,7 @@ where M::Error: Debug, { #[cfg(feature = "afl_internals")] - fn init(afl: &'static afl_state, seed: c_uint) -> Self + fn init(afl: &'static afl_state, seed: u32) -> Self where Self: Sized, { @@ -558,7 +555,7 @@ where } #[cfg(not(feature = "afl_internals"))] - fn init(seed: c_uint) -> Self + fn init(seed: u32) -> Self where Self: Sized, { diff --git a/custom_mutators/rust/example/Cargo.toml b/custom_mutators/rust/example/Cargo.toml index 0c89b200..070d23b1 100644 --- a/custom_mutators/rust/example/Cargo.toml +++ b/custom_mutators/rust/example/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "example" +name = "example_mutator" version = "0.1.0" authors = ["Julius Hohnerlein "] edition = "2018" @@ -9,5 +9,7 @@ edition = "2018" [dependencies] custom_mutator = { path = "../custom_mutator" } -[lib] +[[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 { + Ok(Self) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + buffer: &'b mut [u8], + add_buff: Option<&[u8]>, + max_size: usize, + ) -> Result, Self::Error> { + buffer.reverse(); + Ok(Some(buffer)) + } +} + +struct OwnBufferExampleMutator { + own_buffer: Vec, +} + +impl CustomMutator for OwnBufferExampleMutator { + type Error = (); + + fn init(seed: u32) -> Result { + 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, ()> { + self.own_buffer.reverse(); + Ok(Some(self.own_buffer.as_slice())) + } +} + +export_mutator!(ExampleMutator); diff --git a/custom_mutators/rust/example/src/lib.rs b/custom_mutators/rust/example/src/lib.rs deleted file mode 100644 index 4f9345c0..00000000 --- a/custom_mutators/rust/example/src/lib.rs +++ /dev/null @@ -1,50 +0,0 @@ -#![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 { - Ok(Self) - } - - fn fuzz<'b, 's: 'b>( - &'s mut self, - buffer: &'b mut [u8], - add_buff: Option<&[u8]>, - max_size: usize, - ) -> Result, ()> { - buffer.reverse(); - Ok(Some(buffer)) - } -} - -struct OwnBufferExampleMutator { - own_buffer: Vec, -} - -impl CustomMutator for OwnBufferExampleMutator { - type Error = (); - - fn init(seed: c_uint) -> Result { - 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, ()> { - 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 index 1f68c7e0..29d606a4 100644 --- a/custom_mutators/rust/example_lain/Cargo.toml +++ b/custom_mutators/rust/example_lain/Cargo.toml @@ -10,5 +10,7 @@ edition = "2018" custom_mutator = { path = "../custom_mutator" } lain="0.5" -[lib] +[[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/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, + buffer: Vec, +} + +impl CustomMutator for LainMutator { + type Error = (); + + fn init(seed: u32) -> Result { + 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, ()> { + // 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/custom_mutators/rust/example_lain/src/lib.rs b/custom_mutators/rust/example_lain/src/lib.rs deleted file mode 100644 index 3336e861..00000000 --- a/custom_mutators/rust/example_lain/src/lib.rs +++ /dev/null @@ -1,60 +0,0 @@ -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, - buffer: Vec, -} - -impl CustomMutator for LainMutator { - type Error = (); - - fn init(seed: c_uint) -> Result { - 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, ()> { - // 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); -- cgit 1.4.1 From 2dd5a020615842e51dcf8cfaa7b01ad6bef4cda8 Mon Sep 17 00:00:00 2001 From: julihoh Date: Fri, 5 Mar 2021 19:40:59 +0100 Subject: Ergonomic Improvements for Rust Custom Mutators (#772) * allow returning of str instead of CStr in rust custom mutator * use OsStr instead of CStr for file paths * fix cfg and compiler errors * custom mutator default descriptions * fix usage of afl_internal feature flag * fix example mutator cfg * fix lain mutator * Revert "fix lain mutator" This reverts commit adf700180888d86e8331939cea62d0b39fc699a7. * actually fix lain mutator * resolve question around utf-8 null bytes * change from OsStr to Path to be even more ergonomic * add rust custom mutator ci * fix github action * again * separate compilation check --- .github/workflows/rust_custom_mutator.yml | 30 ++++ custom_mutators/rust/custom_mutator/src/lib.rs | 164 +++++++++++++++++---- .../rust/example/src/example_mutator.rs | 1 + .../rust/example_lain/src/lain_mutator.rs | 2 + 4 files changed, 168 insertions(+), 29 deletions(-) create mode 100644 .github/workflows/rust_custom_mutator.yml (limited to 'custom_mutators/rust/example') diff --git a/.github/workflows/rust_custom_mutator.yml b/.github/workflows/rust_custom_mutator.yml new file mode 100644 index 00000000..de2b184a --- /dev/null +++ b/.github/workflows/rust_custom_mutator.yml @@ -0,0 +1,30 @@ +name: Rust Custom Mutators + +on: + push: + branches: [ stable, dev ] + pull_request: + branches: [ stable, dev ] + +jobs: + test: + name: Test Rust Custom Mutator Support + runs-on: '${{ matrix.os }}' + defaults: + run: + working-directory: custom_mutators/rust + strategy: + matrix: + os: [ubuntu-20.04] + steps: + - uses: actions/checkout@v2 + - name: Install Rust Toolchain + uses: actions-rs/toolchain@v1 + with: + toolchain: stable + - name: Check Code Compiles + run: cargo check + - name: Run General Tests + run: cargo test + - name: Run Tests for afl_internals feature flag + run: cd custom_mutator && cargo test --features=afl_internals \ No newline at end of file diff --git a/custom_mutators/rust/custom_mutator/src/lib.rs b/custom_mutators/rust/custom_mutator/src/lib.rs index b82af250..9444e4d1 100644 --- a/custom_mutators/rust/custom_mutator/src/lib.rs +++ b/custom_mutators/rust/custom_mutator/src/lib.rs @@ -1,3 +1,4 @@ +#![cfg(unix)] //! 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 @@ -23,7 +24,7 @@ //! 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}; +use std::{fmt::Debug, path::Path}; #[cfg(feature = "afl_internals")] #[doc(hidden)] @@ -33,7 +34,7 @@ pub use custom_mutator_sys::afl_state; #[doc(hidden)] pub trait RawCustomMutator { #[cfg(feature = "afl_internals")] - fn init(afl: &'static afl_state, seed: c_uint) -> Self + fn init(afl: &'static afl_state, seed: u32) -> Self where Self: Sized; #[cfg(not(feature = "afl_internals"))] @@ -52,17 +53,17 @@ pub trait RawCustomMutator { 1 } - fn queue_new_entry(&mut self, filename_new_queue: &CStr, _filename_orig_queue: Option<&CStr>) {} + fn queue_new_entry(&mut self, filename_new_queue: &Path, _filename_orig_queue: Option<&Path>) {} - fn queue_get(&mut self, filename: &CStr) -> bool { + fn queue_get(&mut self, filename: &Path) -> bool { true } - fn describe(&mut self, max_description: usize) -> Option<&CStr> { - None + fn describe(&mut self, max_description: usize) -> Option<&str> { + Some(default_mutator_describe::(max_description)) } - fn introspection(&mut self) -> Option<&CStr> { + fn introspection(&mut self) -> Option<&str> { None } @@ -81,16 +82,17 @@ 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}, + ffi::{c_void, CStr, OsStr}, mem::ManuallyDrop, - os::raw::c_char, + os::{raw::c_char, unix::ffi::OsStrExt}, panic::catch_unwind, + path::Path, process::abort, ptr::null, + slice, }; use crate::RawCustomMutator; @@ -99,6 +101,10 @@ pub mod wrappers { /// Also has some convenience functions for FFI conversions (from and to ptr) and tries to make misuse hard (see [`FFIContext::from`]). struct FFIContext { mutator: M, + /// buffer for storing the description returned by [`RawCustomMutator::describe`] as a CString + description_buffer: Vec, + /// buffer for storing the introspection returned by [`RawCustomMutator::introspect`] as a CString + introspection_buffer: Vec, } impl FFIContext { @@ -115,12 +121,16 @@ pub mod wrappers { fn new(afl: &'static afl_state, seed: u32) -> Box { Box::new(Self { mutator: M::init(afl, seed), + description_buffer: Vec::new(), + introspection_buffer: Vec::new(), }) } #[cfg(not(feature = "afl_internals"))] fn new(seed: u32) -> Box { Box::new(Self { mutator: M::init(seed), + description_buffer: Vec::new(), + introspection_buffer: Vec::new(), }) } } @@ -242,9 +252,13 @@ pub mod wrappers { 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_new_queue = Path::new(OsStr::from_bytes( + unsafe { CStr::from_ptr(filename_new_queue) }.to_bytes(), + )); let filename_orig_queue = if !filename_orig_queue.is_null() { - Some(unsafe { CStr::from_ptr(filename_orig_queue) }) + Some(Path::new(OsStr::from_bytes( + unsafe { CStr::from_ptr(filename_orig_queue) }.to_bytes(), + ))) } else { None }; @@ -271,9 +285,14 @@ pub mod wrappers { /// Internal function used in the macro pub fn afl_custom_introspection_(data: *mut c_void) -> *const c_char { match catch_unwind(|| { - let mut context = FFIContext::::from(data); + let context = &mut *FFIContext::::from(data); if let Some(res) = context.mutator.introspection() { - res.as_ptr() + let buf = &mut context.introspection_buffer; + buf.clear(); + buf.extend_from_slice(res.as_bytes()); + buf.push(0); + // unwrapping here, as the error case should be extremely rare + CStr::from_bytes_with_nul(&buf).unwrap().as_ptr() } else { null() } @@ -289,9 +308,14 @@ pub mod wrappers { max_description_len: usize, ) -> *const c_char { match catch_unwind(|| { - let mut context = FFIContext::::from(data); + let context = &mut *FFIContext::::from(data); if let Some(res) = context.mutator.describe(max_description_len) { - res.as_ptr() + let buf = &mut context.description_buffer; + buf.clear(); + buf.extend_from_slice(res.as_bytes()); + buf.push(0); + // unwrapping here, as the error case should be extremely rare + CStr::from_bytes_with_nul(&buf).unwrap().as_ptr() } else { null() } @@ -310,9 +334,9 @@ pub mod wrappers { let mut context = FFIContext::::from(data); assert!(!filename.is_null()); - context - .mutator - .queue_get(unsafe { CStr::from_ptr(filename) }) as u8 + context.mutator.queue_get(Path::new(OsStr::from_bytes( + unsafe { CStr::from_ptr(filename) }.to_bytes(), + ))) as u8 }) { Ok(ret) => ret, Err(err) => panic_handler("afl_custom_queue_get", err), @@ -516,21 +540,21 @@ pub trait CustomMutator { fn queue_new_entry( &mut self, - filename_new_queue: &CStr, - filename_orig_queue: Option<&CStr>, + filename_new_queue: &Path, + filename_orig_queue: Option<&Path>, ) -> Result<(), Self::Error> { Ok(()) } - fn queue_get(&mut self, filename: &CStr) -> Result { + fn queue_get(&mut self, filename: &Path) -> Result { Ok(true) } - fn describe(&mut self, max_description: usize) -> Result, Self::Error> { - Ok(None) + fn describe(&mut self, max_description: usize) -> Result, Self::Error> { + Ok(Some(default_mutator_describe::(max_description))) } - fn introspection(&mut self) -> Result, Self::Error> { + fn introspection(&mut self) -> Result, Self::Error> { Ok(None) } } @@ -593,7 +617,7 @@ where } } - fn queue_new_entry(&mut self, filename_new_queue: &CStr, filename_orig_queue: Option<&CStr>) { + fn queue_new_entry(&mut self, filename_new_queue: &Path, filename_orig_queue: Option<&Path>) { match self.queue_new_entry(filename_new_queue, filename_orig_queue) { Ok(r) => r, Err(e) => { @@ -602,7 +626,7 @@ where } } - fn queue_get(&mut self, filename: &CStr) -> bool { + fn queue_get(&mut self, filename: &Path) -> bool { match self.queue_get(filename) { Ok(r) => r, Err(e) => { @@ -612,7 +636,7 @@ where } } - fn describe(&mut self, max_description: usize) -> Option<&CStr> { + fn describe(&mut self, max_description: usize) -> Option<&str> { match self.describe(max_description) { Ok(r) => r, Err(e) => { @@ -622,7 +646,7 @@ where } } - fn introspection(&mut self) -> Option<&CStr> { + fn introspection(&mut self) -> Option<&str> { match self.introspection() { Ok(r) => r, Err(e) => { @@ -632,3 +656,85 @@ where } } } + +/// the default value to return from [`CustomMutator::describe`]. +fn default_mutator_describe(max_len: usize) -> &'static str { + truncate_str_unicode_safe(std::any::type_name::(), max_len) +} + +#[cfg(all(test, not(feature = "afl_internals")))] +mod default_mutator_describe { + struct MyMutator; + use super::CustomMutator; + impl CustomMutator for MyMutator { + type Error = (); + + fn init(_: u32) -> Result { + Ok(Self) + } + + fn fuzz<'b, 's: 'b>( + &'s mut self, + _: &'b mut [u8], + _: Option<&[u8]>, + _: usize, + ) -> Result, Self::Error> { + unimplemented!() + } + } + + #[test] + fn test_default_describe() { + assert_eq!( + MyMutator::init(0).unwrap().describe(64).unwrap().unwrap(), + "custom_mutator::default_mutator_describe::MyMutator" + ); + } +} + +/// little helper function to truncate a `str` to a maximum of bytes while retaining unicode safety +fn truncate_str_unicode_safe(s: &str, max_len: usize) -> &str { + if s.len() <= max_len { + s + } else { + if let Some((last_index, _)) = s + .char_indices() + .take_while(|(index, _)| *index <= max_len) + .last() + { + &s[..last_index] + } else { + "" + } + } +} + +#[cfg(test)] +mod truncate_test { + use super::truncate_str_unicode_safe; + + #[test] + fn test_truncate() { + for (max_len, input, expected_output) in &[ + (0usize, "a", ""), + (1, "a", "a"), + (1, "ä", ""), + (2, "ä", "ä"), + (3, "äa", "äa"), + (4, "äa", "äa"), + (1, "👎", ""), + (2, "👎", ""), + (3, "👎", ""), + (4, "👎", "👎"), + (1, "abc", "a"), + (2, "abc", "ab"), + ] { + let actual_output = truncate_str_unicode_safe(input, *max_len); + assert_eq!( + &actual_output, expected_output, + "{:#?} truncated to {} bytes should be {:#?}, but is {:#?}", + input, max_len, expected_output, actual_output + ); + } + } +} diff --git a/custom_mutators/rust/example/src/example_mutator.rs b/custom_mutators/rust/example/src/example_mutator.rs index 9b9d4997..c4711dd1 100644 --- a/custom_mutators/rust/example/src/example_mutator.rs +++ b/custom_mutators/rust/example/src/example_mutator.rs @@ -1,3 +1,4 @@ +#![cfg(unix)] #![allow(unused_variables)] use custom_mutator::{export_mutator, CustomMutator}; diff --git a/custom_mutators/rust/example_lain/src/lain_mutator.rs b/custom_mutators/rust/example_lain/src/lain_mutator.rs index 22e5fe73..7099aeae 100644 --- a/custom_mutators/rust/example_lain/src/lain_mutator.rs +++ b/custom_mutators/rust/example_lain/src/lain_mutator.rs @@ -1,3 +1,5 @@ +#![cfg(unix)] + use custom_mutator::{export_mutator, CustomMutator}; use lain::{ mutator::Mutator, -- cgit 1.4.1