mirror of
https://github.com/AFLplusplus/AFLplusplus.git
synced 2025-06-15 03:18:07 +00:00
Add support for post_process in Rust custom mutator + associated example with lain (#2241)
This commit is contained in:
@ -5,4 +5,5 @@ members = [
|
||||
"example",
|
||||
# Lain needs a nightly toolchain
|
||||
# "example_lain",
|
||||
# "example_lain_post_process",
|
||||
]
|
@ -9,3 +9,11 @@ A minimal example can be found in `example`. Build it using `cargo build --examp
|
||||
|
||||
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.
|
||||
|
||||
An example for the use of the post_process function, using [lain](https://github.com/microsoft/lain) with [serde](https://github.com/serde-rs/serde) and [bincode](https://github.com/bincode-org/bincode) can be found in `example_lain_post_process`.
|
||||
In order for it to work you need to:
|
||||
|
||||
- disable input trimming with `AFL_DISABLE_TRIM=1`
|
||||
- provide an initial instance serialized with `bincode` or use the `AFL_NO_STARTUP_CALIBRATION=1` environment variable.
|
||||
|
||||
Note that `bincode` can also be used to serialize/deserialize the lain-generated structure and mutate it rather than generating a new one at each iteration, but it requires some structure serialized with `bincode` as input seed.
|
||||
|
@ -73,6 +73,8 @@ pub trait RawCustomMutator {
|
||||
None
|
||||
}
|
||||
|
||||
fn post_process<'b, 's: 'b>(&'s mut self, buffer: &'b mut [u8]) -> Option<&'b [u8]>;
|
||||
|
||||
/*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);
|
||||
@ -353,6 +355,33 @@ pub mod wrappers {
|
||||
Err(err) => panic_handler("afl_custom_queue_get", &err),
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal function used in the macro
|
||||
pub unsafe fn afl_custom_post_process<M: RawCustomMutator>(
|
||||
data: *mut c_void,
|
||||
buf: *mut u8,
|
||||
buf_size: usize,
|
||||
out_buf: *mut *const u8,
|
||||
) -> usize {
|
||||
match catch_unwind(|| {
|
||||
let mut context = FFIContext::<M>::from(data);
|
||||
|
||||
assert!(!buf.is_null(), "null buf passed to afl_custom_post_process");
|
||||
assert!(
|
||||
!out_buf.is_null(),
|
||||
"null out_buf passed to afl_custom_post_process"
|
||||
);
|
||||
let buff_slice = slice::from_raw_parts_mut(buf, buf_size);
|
||||
if let Some(buffer) = context.mutator.post_process(buff_slice) {
|
||||
*out_buf = buffer.as_ptr();
|
||||
return buffer.len();
|
||||
}
|
||||
0
|
||||
}) {
|
||||
Ok(ret) => ret,
|
||||
Err(err) => panic_handler("afl_custom_post_process", &err),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An exported macro to defined afl_custom_init meant for insternal usage
|
||||
@ -480,6 +509,16 @@ macro_rules! export_mutator {
|
||||
pub unsafe extern "C" fn afl_custom_deinit(data: *mut ::std::os::raw::c_void) {
|
||||
$crate::wrappers::afl_custom_deinit_::<$mutator_type>(data)
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn afl_custom_post_process(
|
||||
data: *mut ::std::os::raw::c_void,
|
||||
buf: *mut u8,
|
||||
buf_size: usize,
|
||||
out_buf: *mut *const u8,
|
||||
) -> usize {
|
||||
$crate::wrappers::afl_custom_post_process::<$mutator_type>(data, buf, buf_size, out_buf)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -512,6 +551,10 @@ mod sanity_test {
|
||||
) -> Option<&'b [u8]> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn post_process<'b, 's: 'b>(&'s mut self, buffer: &'b mut [u8]) -> Option<&'b [u8]> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
export_mutator!(ExampleMutator);
|
||||
@ -579,6 +622,13 @@ pub trait CustomMutator {
|
||||
fn introspection(&mut self) -> Result<Option<&str>, Self::Error> {
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
fn post_process<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
) -> Result<Option<&'b [u8]>, Self::Error> {
|
||||
Ok(Some(buffer))
|
||||
}
|
||||
}
|
||||
|
||||
impl<M> RawCustomMutator for M
|
||||
@ -682,6 +732,16 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn post_process<'b, 's: 'b>(&'s mut self, buffer: &'b mut [u8]) -> Option<&'b [u8]> {
|
||||
match self.post_process(buffer) {
|
||||
Ok(r) => r,
|
||||
Err(e) => {
|
||||
Self::handle_error(e);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// the default value to return from [`CustomMutator::describe`].
|
||||
|
@ -8,7 +8,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
custom_mutator = { path = "../custom_mutator" }
|
||||
lain="0.5"
|
||||
lain = { git = "https://github.com/AFLplusplus/lain.git" }
|
||||
|
||||
[[example]]
|
||||
name = "example_lain"
|
||||
|
21
custom_mutators/rust/example_lain_post_process/Cargo.toml
Normal file
21
custom_mutators/rust/example_lain_post_process/Cargo.toml
Normal file
@ -0,0 +1,21 @@
|
||||
[package]
|
||||
name = "example_lain_post_process"
|
||||
version = "0.1.0"
|
||||
authors = [
|
||||
"Julius Hohnerlein <julihoh@users.noreply.github.com>",
|
||||
"jma <94166787+jma-qb@users.noreply.github.com>",
|
||||
]
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
custom_mutator = { path = "../custom_mutator" }
|
||||
lain = { git = "https://github.com/AFLplusplus/lain.git" }
|
||||
bincode = "1.3.3"
|
||||
serde = { version = "1.0.214", features = ["derive"] }
|
||||
|
||||
[[example]]
|
||||
name = "example_lain_post_process"
|
||||
path = "./src/lain_mutator.rs"
|
||||
crate-type = ["cdylib"]
|
@ -0,0 +1 @@
|
||||
nightly
|
@ -0,0 +1,70 @@
|
||||
#![cfg(unix)]
|
||||
|
||||
use custom_mutator::{export_mutator, CustomMutator};
|
||||
use lain::{
|
||||
mutator::Mutator,
|
||||
prelude::*,
|
||||
rand::{rngs::StdRng, SeedableRng},
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, Mutatable, NewFuzzed, BinarySerialize)]
|
||||
struct MyStruct {
|
||||
tag: u8,
|
||||
#[lain(ignore)]
|
||||
length: u32,
|
||||
#[lain(min = 0, max = 10)]
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
struct LainMutator {
|
||||
mutator: Mutator<StdRng>,
|
||||
buffer: Vec<u8>,
|
||||
post_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(),
|
||||
post_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 serialized = bincode::serialize(&instance).unwrap();
|
||||
let size = serialized.len();
|
||||
if size > max_size {
|
||||
return Err(());
|
||||
}
|
||||
self.buffer.clear();
|
||||
self.buffer.reserve(size);
|
||||
self.buffer.extend_from_slice(&serialized);
|
||||
Ok(Some(self.buffer.as_slice()))
|
||||
}
|
||||
|
||||
fn post_process<'b, 's: 'b>(
|
||||
&'s mut self,
|
||||
buffer: &'b mut [u8],
|
||||
) -> Result<Option<&'b [u8]>, Self::Error> {
|
||||
let mut instance = bincode::deserialize::<MyStruct>(&buffer).unwrap();
|
||||
instance.length = instance.data.len() as u32;
|
||||
let size = instance.serialized_size();
|
||||
self.post_buffer.clear();
|
||||
self.post_buffer.reserve(size);
|
||||
instance.binary_serialize::<_, BigEndian>(&mut self.post_buffer);
|
||||
Ok(Some(&self.post_buffer))
|
||||
}
|
||||
}
|
||||
|
||||
export_mutator!(LainMutator);
|
Reference in New Issue
Block a user