Skip to content

Commit 3f86aaf

Browse files
committed
initial broken attempt at deriving mapping mutators
1 parent 7344fdf commit 3f86aaf

File tree

3 files changed

+175
-3
lines changed

3 files changed

+175
-3
lines changed

libafl/src/mutators/havoc_mutations.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! [`crate::mutators::Mutator`] collection equivalent to AFL++'s havoc mutations
22
3-
use libafl_bolts::tuples::{Map, Merge};
3+
use libafl_bolts::tuples::{Map, Merge, NamedTuple};
44
use tuple_list::{tuple_list, tuple_list_type};
55

66
use crate::mutators::{
@@ -271,3 +271,29 @@ where
271271
current_input_mapper,
272272
))
273273
}
274+
275+
/// TODO
276+
pub trait HasHavocMutators {
277+
/// TODO
278+
fn havoc_mutators<MT: NamedTuple>() -> MT;
279+
}
280+
281+
#[cfg(test)]
282+
mod tests {
283+
use libafl_derive::HasHavocMutators;
284+
285+
use super::HasHavocMutators;
286+
use crate::mutators::{StdScheduledMutator, Vec};
287+
288+
#[derive(HasHavocMutators)]
289+
struct CustomInput {
290+
vec: Vec<u8>,
291+
}
292+
293+
#[test]
294+
fn test_derive_has_havoc_mutators() {
295+
let input = CustomInput { vec: vec![] };
296+
let mutations = CustomInput::havoc_mutators();
297+
let scheduler = StdScheduledMutator::new(mutations);
298+
}
299+
}

libafl_derive/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,4 @@ proc-macro = true
2424
syn = { version = "2.0.77", features = ["full", "extra-traits"] }
2525
quote = "1.0.37"
2626
proc-macro2 = "1.0.86"
27+
proc-macro-crate = "3.2"

libafl_derive/src/lib.rs

Lines changed: 147 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,8 +58,17 @@
5858
)]
5959

6060
use proc_macro::TokenStream;
61-
use quote::quote;
62-
use syn::{parse_macro_input, Data::Struct, DeriveInput, Field, Fields::Named, Type};
61+
use proc_macro_crate::{crate_name, FoundCrate};
62+
use quote::{format_ident, quote};
63+
use syn::{
64+
parse_macro_input,
65+
punctuated::Punctuated,
66+
token::Comma,
67+
Data::Struct,
68+
DeriveInput, Error, Field,
69+
Fields::{Named, Unit, Unnamed},
70+
GenericArgument, Ident, PathArguments, PathSegment, Type,
71+
};
6372

6473
/// Derive macro to implement `SerdeAny`, to use a type in a `SerdeAnyMap`
6574
#[proc_macro_derive(SerdeAny)]
@@ -157,3 +166,139 @@ fn libafl_display_field_by_type(it: &Field) -> proc_macro2::TokenStream {
157166
write!(f, #fmt, self.#ident)?;
158167
}
159168
}
169+
170+
/// TODO
171+
#[proc_macro_derive(HasHavocMutators)]
172+
pub fn derive_has_mutator_bytes(input: TokenStream) -> TokenStream {
173+
let input_ast = parse_macro_input!(input as DeriveInput);
174+
175+
let struct_name = input_ast.ident.clone();
176+
177+
let fields = match extract_fields(input_ast) {
178+
Ok(f) => f,
179+
Err(e) => return e.into_compile_error().into(),
180+
};
181+
182+
let (getter_methods, mutator_merge_call) = match create_functions_on_fields(&fields) {
183+
Ok(e) => e,
184+
Err(e) => return e.into_compile_error().into(),
185+
};
186+
187+
// required to be able to use it from within libafl — used for testing
188+
let libafl_source = match crate_name("libafl").expect("Could not figure out current crate") {
189+
FoundCrate::Itself => quote! { crate },
190+
FoundCrate::Name(_) => quote! { libafl },
191+
};
192+
193+
// Generate the impl block
194+
let expanded = quote! {
195+
use #libafl_source::{inputs::MutVecInput, mutators::{Mutator, mapped_havoc_mutations}};
196+
use libafl_bolts::tuples::{Merge, NamedTuple, tuple_list};
197+
198+
impl #struct_name {
199+
#getter_methods
200+
}
201+
202+
impl HasHavocMutators for #struct_name {
203+
fn havoc_mutators<MT: NamedTuple>() -> MT {
204+
#mutator_merge_call
205+
}
206+
}
207+
};
208+
209+
TokenStream::from(expanded)
210+
}
211+
212+
fn extract_fields(ast: DeriveInput) -> Result<Punctuated<Field, Comma>, Error> {
213+
match &ast.data {
214+
Struct(data_struct) => match &data_struct.fields {
215+
Named(fields_named) => Ok(fields_named.named.clone()),
216+
Unnamed(fields_unnamed) => Ok(fields_unnamed.unnamed.clone()),
217+
Unit => Err(Error::new_spanned(
218+
ast,
219+
"HasHavocMutators can not be derived for unit structs",
220+
)),
221+
},
222+
_ => Err(Error::new_spanned(
223+
ast,
224+
"HasHavocMutators can only be derived for structs",
225+
)),
226+
}
227+
}
228+
229+
fn create_functions_on_fields(
230+
fields: &Punctuated<Field, Comma>,
231+
) -> Result<(proc_macro2::TokenStream, proc_macro2::TokenStream), Error> {
232+
let functions_res = fields.iter().map(|field| match field.ty.clone() {
233+
Type::Path(type_path) => {
234+
let segment = type_path.path.segments.last().unwrap();
235+
if let Some(tokens) = create_functions_on_type(segment, field.ident.as_ref().unwrap()) {
236+
return Ok(tokens);
237+
}
238+
239+
Err(Error::new_spanned(
240+
segment.ident.clone(),
241+
"HasHavocMutators does not support struct parts of this type",
242+
))
243+
}
244+
_ => Err(Error::new_spanned(
245+
field,
246+
"HasHavocMutators can only be derived for structs",
247+
)),
248+
});
249+
250+
// check if any fields could not be parsed into functions, combine the errors and return them
251+
if let Some(errors) = functions_res
252+
.clone()
253+
.filter(Result::is_err)
254+
.map(Result::unwrap_err)
255+
.reduce(|mut acc, e| {
256+
acc.combine(e);
257+
acc
258+
})
259+
{
260+
return Err(errors);
261+
}
262+
263+
Ok(functions_res.map(Result::unwrap).fold(
264+
(quote! {}, quote! { tuple_list!() }),
265+
|(acc1, acc2), (e1, e2)| {
266+
(
267+
quote! {
268+
#acc1
269+
#e1
270+
},
271+
quote! { #acc2.merge(#e2) },
272+
)
273+
},
274+
))
275+
}
276+
277+
fn create_functions_on_type(
278+
segment: &PathSegment,
279+
field_name: &Ident,
280+
) -> Option<(proc_macro2::TokenStream, proc_macro2::TokenStream)> {
281+
if segment.ident == "Vec" {
282+
if let PathArguments::AngleBracketed(args) = &segment.arguments {
283+
if let Some(GenericArgument::Type(Type::Path(arg_type))) = args.args.first() {
284+
let arg_ident = &arg_type.path.segments.last().unwrap().ident;
285+
if arg_ident == "u8" {
286+
let mutable_method_name = format_ident!("{}_mut", field_name);
287+
let immutable_method_name = field_name;
288+
return Some((
289+
quote! {
290+
pub fn #mutable_method_name(&mut self) -> MutVecInput<'_> {
291+
(&mut self.#field_name).into()
292+
}
293+
pub fn #immutable_method_name(&self) -> &[u8] {
294+
&self.#field_name
295+
}
296+
},
297+
quote! { mapped_havoc_mutations(Self::#mutable_method_name, Self::#immutable_method_name) },
298+
));
299+
}
300+
}
301+
}
302+
}
303+
None
304+
}

0 commit comments

Comments
 (0)