|
58 | 58 | )]
|
59 | 59 |
|
60 | 60 | 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 | +}; |
63 | 72 |
|
64 | 73 | /// Derive macro to implement `SerdeAny`, to use a type in a `SerdeAnyMap`
|
65 | 74 | #[proc_macro_derive(SerdeAny)]
|
@@ -157,3 +166,139 @@ fn libafl_display_field_by_type(it: &Field) -> proc_macro2::TokenStream {
|
157 | 166 | write!(f, #fmt, self.#ident)?;
|
158 | 167 | }
|
159 | 168 | }
|
| 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