From f4fa7e7b8bb1c204a25e011aea9ee503cd3a41ff Mon Sep 17 00:00:00 2001 From: Andrea Fioraldi Date: Sat, 5 Dec 2020 16:27:28 +0100 Subject: [PATCH] serde anymap, todo remove testcase clone() in event --- afl/Cargo.toml | 4 +- afl/src/corpus/testcase.rs | 30 +++---- afl/src/feedbacks/mod.rs | 16 ++-- afl/src/inputs/bytes.rs | 2 +- afl/src/lib.rs | 1 + afl/src/serde_anymap.rs | 165 +++++++++++++++++++++++++++++++++++++ 6 files changed, 191 insertions(+), 27 deletions(-) create mode 100644 afl/src/serde_anymap.rs diff --git a/afl/Cargo.toml b/afl/Cargo.toml index c336e28efc..e598a8f3e0 100644 --- a/afl/Cargo.toml +++ b/afl/Cargo.toml @@ -32,5 +32,5 @@ libc = "0.2" # For (*nix) libc num = "*" xxhash-rust = { version = "0.8.0-beta.5", features = ["xxh3"] } # xxh3 hashing for rust serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib -typetag = "0.1" -postcard = "0.5.1" # no_std compatible serde serialization fromat \ No newline at end of file +erased-serde = "0.3.12" +postcard = "0.5.1" # no_std compatible serde serialization fromat diff --git a/afl/src/corpus/testcase.rs b/afl/src/corpus/testcase.rs index bf509ac270..852be0e544 100644 --- a/afl/src/corpus/testcase.rs +++ b/afl/src/corpus/testcase.rs @@ -1,7 +1,5 @@ -use alloc::boxed::Box; use alloc::rc::Rc; use alloc::string::String; -use core::any::Any; use core::cell::RefCell; use core::convert::Into; use core::default::Default; @@ -10,6 +8,7 @@ use hashbrown::HashMap; use serde::{Deserialize, Serialize}; use crate::inputs::Input; +use crate::serde_anymap::{SerdeAny, SerdeAnyMap}; use crate::AflError; // TODO PathBuf for no_std and change filename to PathBuf @@ -18,14 +17,12 @@ use crate::AflError; // TODO: Give example /// Metadata for a testcase -#[typetag::serde(tag = "type")] -pub trait TestcaseMetadata: Any { +pub trait TestcaseMetadata: SerdeAny { /// The name of this metadata - used to find it in the list of avaliable metadatas fn name(&self) -> &'static str; - - fn clone(&self) -> Box; } +/* /// Just a wrapper of Boxed TestcaseMetadata trait object for Clone #[derive(Serialize, Deserialize)] pub struct TestcaseMetadataContainer { @@ -45,10 +42,10 @@ impl TestcaseMetadataContainer { pub fn meta_mut(&mut self) -> &mut Box { &mut self.meta } -} +}*/ /// An entry in the Testcase Corpus -#[derive(Default, Clone, Serialize, Deserialize)] +#[derive(Default, Serialize, Deserialize)] pub struct Testcase where I: Input, @@ -61,7 +58,7 @@ where fitness: u32, // TODO find a way to use TypeId /// Map of metadatas associated with this testcase - metadatas: HashMap, + metadatas: SerdeAnyMap, } impl Into>> for Testcase @@ -124,7 +121,7 @@ where } /// Get all the metadatas into an HashMap (mutable) - pub fn metadatas(&mut self) -> &mut HashMap { + pub fn metadatas(&mut self) -> &mut SerdeAnyMap { &mut self.metadatas } /// Add a metadata @@ -132,12 +129,7 @@ where where TM: TestcaseMetadata + 'static, { - self.metadatas.insert( - meta.name().to_string(), - TestcaseMetadataContainer { - meta: Box::new(meta), - }, - ); + self.metadatas.insert(meta); } /// Create a new Testcase instace given an input @@ -149,7 +141,7 @@ where input: Some(input.into()), filename: None, fitness: 0, - metadatas: HashMap::default(), + metadatas: SerdeAnyMap::new(), } } @@ -159,7 +151,7 @@ where input: Some(input), filename: Some(filename), fitness: 0, - metadatas: HashMap::default(), + metadatas: SerdeAnyMap::new(), } } @@ -168,7 +160,7 @@ where input: None, filename: None, fitness: 0, - metadatas: HashMap::default(), + metadatas: SerdeAnyMap::new(), } } } diff --git a/afl/src/feedbacks/mod.rs b/afl/src/feedbacks/mod.rs index e484fa69ba..541a17aaa9 100644 --- a/afl/src/feedbacks/mod.rs +++ b/afl/src/feedbacks/mod.rs @@ -3,12 +3,14 @@ use alloc::rc::Rc; use alloc::vec::Vec; use core::cell::RefCell; use core::marker::PhantomData; +use core::any::Any; use num::Integer; use serde::{Deserialize, Serialize}; use crate::corpus::{Testcase, TestcaseMetadata}; use crate::inputs::Input; use crate::observers::MapObserver; +use crate::serde_anymap::SerdeAny; use crate::AflError; pub trait Feedback @@ -171,15 +173,19 @@ pub struct MapNoveltiesMetadata { novelties: Vec, } -#[typetag::serde] +impl SerdeAny for MapNoveltiesMetadata { + fn as_any(&self) -> &dyn Any { + self + } + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + impl TestcaseMetadata for MapNoveltiesMetadata { fn name(&self) -> &'static str { "MapNoveltiesMetadata" } - - fn clone(&self) -> Box { - Box::new(MapNoveltiesMetadata::new(self.novelties.clone())) - } } impl MapNoveltiesMetadata { pub fn novelties(&self) -> &[usize] { diff --git a/afl/src/inputs/bytes.rs b/afl/src/inputs/bytes.rs index 4dda8c3f33..da8258a6ab 100644 --- a/afl/src/inputs/bytes.rs +++ b/afl/src/inputs/bytes.rs @@ -10,7 +10,7 @@ use crate::AflError; /// A bytes input is the basic input #[derive(Clone, Debug, Default)] pub struct BytesInput { - pub bytes: Vec, + bytes: Vec, } impl Input for BytesInput { diff --git a/afl/src/lib.rs b/afl/src/lib.rs index da4070ae12..1e8c7727af 100644 --- a/afl/src/lib.rs +++ b/afl/src/lib.rs @@ -14,6 +14,7 @@ pub mod mutators; pub mod observers; pub mod stages; pub mod utils; +pub mod serde_anymap; use alloc::string::String; use core::fmt; diff --git a/afl/src/serde_anymap.rs b/afl/src/serde_anymap.rs new file mode 100644 index 0000000000..1b548061e0 --- /dev/null +++ b/afl/src/serde_anymap.rs @@ -0,0 +1,165 @@ +use hashbrown::HashMap; +use serde::{Serialize, Deserialize}; + +use core::default::Default; +use core::any::{TypeId, Any}; +use core::fmt; + +pub fn pack_type_id(id: u64) -> TypeId { + unsafe { + *(&id as *const u64 as *const TypeId) + } +} + +pub fn unpack_type_id(id: TypeId) -> u64 { + unsafe { + *(&id as *const _ as *const u64) + } +} + +pub trait SerdeAny : Any + erased_serde::Serialize { + fn as_any(&self) -> &dyn Any; + fn as_any_mut(&mut self) -> &mut dyn Any; +} + +type DeserializeCallback = fn(&mut dyn erased_serde::Deserializer) -> Result, erased_serde::Error>; + +struct Wrap<'a, T: ?Sized>(pub &'a T); +impl<'a, T> Serialize for Wrap<'a, T> +where + T: ?Sized + erased_serde::Serialize + 'a, +{ + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + erased_serde::serialize(self.0, serializer) + } +} + +impl<'a> serde::Serialize for dyn SerdeAny + 'a { + fn serialize(&self, se: S) -> Result + where S: serde::Serializer + { + use serde::ser::SerializeSeq; + + let id = unpack_type_id(self.type_id()); + let mut seq = se.serialize_seq(Some(2))?; + seq.serialize_element(&id)?; + seq.serialize_element(&Wrap(self))?; + seq.end() + } +} + +struct DeserializeCallbackSeed { + pub cb: DeserializeCallback, +} + +impl<'de> serde::de::DeserializeSeed<'de> for DeserializeCallbackSeed{ + type Value = Box; + + fn deserialize(self, deserializer: D) -> Result + where + D: serde::de::Deserializer<'de>, + { + let mut erased = erased_serde::Deserializer::erase(deserializer); + (self.cb)(&mut erased).map_err(serde::de::Error::custom) + } +} + +struct BoxAnyVisitor {} +impl<'de> serde::de::Visitor<'de> for BoxAnyVisitor { + type Value = Box; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("Expecting a serialized SerdeAny trait object (Box)") + } + + fn visit_seq(self, mut visitor: V) -> Result, V::Error> + where + V: serde::de::SeqAccess<'de>, + { + let id: u64 = visitor.next_element()?.unwrap(); + let cb = unsafe { *REGISTRY.deserializers.as_ref().unwrap().get(&id).expect("Cannot deserialize an unregistered SerdeAny") }; + let seed = DeserializeCallbackSeed { cb: cb }; + let obj: Box = visitor.next_element_seed(seed)?.unwrap(); + Ok(obj) + } +} + +impl<'de> Deserialize<'de> for Box { + fn deserialize(deserializer: D) -> Result, D::Error> + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_seq(BoxAnyVisitor {}) + } +} + +pub struct Registry { + deserializers: Option>, + finalized: bool +} + +impl Registry { + pub fn register(&mut self) where T: SerdeAny + Serialize + serde::de::DeserializeOwned { + if self.finalized { + panic!("Global Registry of SerdeAny types is already finalized!"); + } + + let deserializers = self.deserializers.get_or_insert_with(|| HashMap::default()); + deserializers.insert(unpack_type_id(TypeId::of::()), |de| Ok(Box::new(erased_serde::deserialize::(de)?))); + } + + pub fn finalize(&mut self) { + self.finalized = true; + } +} + +static mut REGISTRY: Registry = Registry { deserializers: None, finalized: false }; + +pub struct RegistryBuilder {} +impl RegistryBuilder { + pub fn register() where T: SerdeAny + Serialize + serde::de::DeserializeOwned { + unsafe { + REGISTRY.register::(); + } + } + + pub fn finalize() { + unsafe { + REGISTRY.finalize(); + } + } +} + +#[derive(Default, Serialize, Deserialize)] +pub struct SerdeAnyMap { + map: HashMap> +} + +impl SerdeAnyMap { + pub fn get(&self) -> Option<&T> where T: SerdeAny { + self.map.get(&unpack_type_id(TypeId::of::())).map(|x| x.as_ref().as_any().downcast_ref::().unwrap()) + } + + pub fn get_mut(&mut self) -> Option<&mut T> where T: SerdeAny { + self.map.get_mut(&unpack_type_id(TypeId::of::())).map(|x| x.as_mut().as_any_mut().downcast_mut::().unwrap()) + } + + pub fn insert(&mut self, t: T) where T: SerdeAny { + self.map.insert(unpack_type_id(TypeId::of::()), Box::new(t)); + } + + pub fn len(&self) -> usize { + self.map.len() + } + + pub fn contains(&self) -> bool where T: SerdeAny { + self.map.contains_key(&unpack_type_id(TypeId::of::())) + } + + pub fn new() -> Self { + SerdeAnyMap { map: HashMap::default() } + } +}