Skip to content

Commit

Permalink
WIP Use display trait
Browse files Browse the repository at this point in the history
  • Loading branch information
Marcondiro committed Jun 28, 2024
1 parent 44536d8 commit 80bd6a8
Show file tree
Hide file tree
Showing 4 changed files with 324 additions and 126 deletions.
109 changes: 107 additions & 2 deletions libafl_derive/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@
)
)]

use proc_macro::TokenStream;
use proc_macro::{Ident, TokenStream};
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
use syn::{parse_macro_input, Data::Struct, DeriveInput, Fields::Named, PathArguments, Type};

/// Derive macro to implement `SerdeAny`, to use a type in a `SerdeAnyMap`
#[proc_macro_derive(SerdeAny)]
Expand All @@ -69,3 +69,108 @@ pub fn libafl_serdeany_derive(input: TokenStream) -> TokenStream {
libafl_bolts::impl_serdeany!(#name);
})
}

/// Display macro to implement `Display` for a struct where all fields implement `Display`.
/// The result is the concatenation of all fields display.
/// Specifically handled cases:
/// Options: Some => inner type display None => "".
/// Vec: inner type display concatenated with spaces.
/// Generics or other more or less exotic stuff are not supported.
#[proc_macro_derive(Display)]
pub fn libafl_display(input: TokenStream) -> TokenStream {
// TODO a bit of refactoring
let DeriveInput { ident, data, .. } = parse_macro_input!(input as DeriveInput);

if let Struct(s) = data {
if let Named(fields) = s.fields {
let vec_fields = fields
.named
.iter()
.filter(|it| libafl_display_type(&it.ty) == TyVec)
.map(|it| &it.ident);
let options_fields = fields
.named
.iter()
.filter(|it| libafl_display_type(&it.ty) == TyOption)
.map(|it| &it.ident);
let other_fields = fields
.named
.iter()
.filter(|it| libafl_display_type(&it.ty) == TyOther)
.map(|it| &it.ident);

let other_fields_fmt = " {}".repeat(other_fields.clone().count());
return TokenStream::from(quote! {
impl core::fmt::Display for #ident {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
// TyVec
#( for e in &self.#vec_fields {
write!(f, " {}", e)?;
} )*

// TyOption
#( if let Some(opt) = &self.#options_fields {
write!(f, " {}", opt)?;
} )*

// TyOther
write!(f, #other_fields_fmt, #(self.#other_fields), *)?;

Ok(())
}
}
});
}
}
panic!("Only structs are supported");
}

#[derive(Debug, PartialEq, Eq)]
enum LibaflDisplayFieldType {
TyOption,
TyVec,
TyOther,
}
use LibaflDisplayFieldType::{TyOption, TyOther, TyVec};

fn libafl_display_type(ty: &Type) -> LibaflDisplayFieldType {
if let Type::Path(type_path) = ty {
if type_path.qself.is_none() && type_path.path.segments.len() == 1 {
let segment = &type_path.path.segments[0];
if segment.ident == "Option" {
if let PathArguments::AngleBracketed(ref generic_args) = segment.arguments {
if generic_args.args.len() == 1 {
return TyOption;
}
}
} else if segment.ident == "Vec" {
if let PathArguments::AngleBracketed(ref generic_args) = segment.arguments {
if generic_args.args.len() == 1 {
return TyVec;
}
}
}
}
}
TyOther
}

#[cfg(test)]
mod tests {
use syn::parse_quote;
use LibaflDisplayFieldType::{TyOption, TyOther, TyVec};

use super::*;

#[test]
fn is_option_works() {
let ty: Type = parse_quote!(Option<(String, i8)>);
assert!(libafl_display_type(&ty) == TyOption);

let ty: Type = parse_quote!(Vec<u8>);
assert!(libafl_display_type(&ty) == TyVec);

let ty: Type = parse_quote!(Optionsus<u8>);
assert!(libafl_display_type(&ty) == TyOther);
}
}
2 changes: 2 additions & 0 deletions libafl_qemu/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ libafl = { path = "../libafl", version = "0.13.0", default-features = false, fea
libafl_bolts = { path = "../libafl_bolts", version = "0.13.0", default-features = false, features = ["std", "derive"] }
libafl_targets = { path = "../libafl_targets", version = "0.13.0" }
libafl_qemu_sys = { path = "./libafl_qemu_sys", version = "0.13.0" }
libafl_derive = { path = "../libafl_derive", version = "0.13.0" }

serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
hashbrown = { version = "0.14", features = ["serde"] } # A faster hashmap, nostd compatible
Expand Down Expand Up @@ -94,6 +95,7 @@ pyo3 = { version = "0.18", optional = true , features = ["multiple-pymethods"]}
bytes-utils = "0.1"
typed-builder = "0.18"
memmap2 = "0.9"
derive_more = "0.99"
# Document all features of this crate (for `cargo doc`)
document-features = { version = "0.2", optional = true }

Expand Down
14 changes: 8 additions & 6 deletions libafl_qemu/src/qemu/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ use strum::IntoEnumIterator;
use crate::{GuestAddrKind, GuestReg, Regs};

pub mod qemu_opt;
use qemu_opt::QemuOpt;
use qemu_opt::QemuBuilder;

#[cfg(emulation_mode = "usermode")]
mod usermode;
Expand Down Expand Up @@ -572,18 +572,20 @@ impl From<u8> for HookData {

#[allow(clippy::unused_self)]
impl Qemu {
pub fn builder() -> QemuBuilder {
QemuBuilder::new()
}

//TODO move to QemuBuilder
#[allow(clippy::must_use_candidate)]
pub fn with_options(
qemu_opts: &QemuOpt,
env: &[(String, String)],
) -> Result<Self, QemuInitError> {
pub fn build(qemu_opts: &QemuBuilder) -> Result<Self, QemuInitError> {
//TODO: do it properly without this shortcut
let args = qemu_opts
.to_string()
.split(' ')
.map(std::string::ToString::to_string)
.collect::<Vec<String>>();
Self::init(&args, env)
Self::init(&args, &vec![])
}

#[allow(clippy::must_use_candidate, clippy::similar_names)]
Expand Down
Loading

0 comments on commit 80bd6a8

Please sign in to comment.