diff --git a/libafl_bolts/Cargo.toml b/libafl_bolts/Cargo.toml index 741d69f8e9..8e24ea3a8d 100644 --- a/libafl_bolts/Cargo.toml +++ b/libafl_bolts/Cargo.toml @@ -63,6 +63,13 @@ xxh3 = ["xxhash-rust"] #! ### SerdeAny features +## With this feature, the AnyMap uses [`type_name`](https://doc.rust-lang.org/std/any/fn.type_name.html) +## instead of [`TypeId::of`](https://doc.rust-lang.org/std/any/struct.TypeId.html#method.of) for deserialization. +## With this feature, stored state may remain deserializable across multiple compilations of LibAFL. +## This is **unsafe** and may lead to type confusions. Only use when you know what you are doing/ you have tests in place. +## The rust doc specifically states that "multiple types may map to the same type name"! +unsafe_stable_anymap = [] + ## Automatically register all `#[derive(SerdeAny)]` types at startup. serdeany_autoreg = ["ctor"] diff --git a/libafl_bolts/src/serdeany.rs b/libafl_bolts/src/serdeany.rs index e8b3d13083..cda620e727 100644 --- a/libafl_bolts/src/serdeany.rs +++ b/libafl_bolts/src/serdeany.rs @@ -1,11 +1,54 @@ //! Poor-rust-man's downcasts for stuff we send over the wire (or shared maps) use alloc::boxed::Box; +#[cfg(feature = "unsafe_stable_anymap")] +use alloc::string::{String, ToString}; +#[cfg(feature = "unsafe_stable_anymap")] +use core::any::type_name; +#[cfg(not(feature = "unsafe_stable_anymap"))] +use core::any::TypeId; use core::{any::Any, fmt::Debug}; use serde::{de::DeserializeSeed, Deserialize, Deserializer, Serialize, Serializer}; pub use serdeany_registry::*; +#[cfg(not(feature = "unsafe_stable_anymap"))] +use crate::anymap::unpack_type_id; + +/// The type of a stored type in this anymap (`u128`) +#[cfg(not(feature = "unsafe_stable_anymap"))] +pub type TypeRepr = u128; + +/// The type of a stored type in this anymap (`String`) +#[cfg(feature = "unsafe_stable_anymap")] +pub type TypeRepr = String; + +#[cfg(not(feature = "unsafe_stable_anymap"))] +fn type_repr() -> TypeRepr +where + T: 'static, +{ + unpack_type_id(TypeId::of::()) +} + +#[cfg(not(feature = "unsafe_stable_anymap"))] +fn type_repr_owned() -> TypeRepr +where + T: 'static, +{ + unpack_type_id(TypeId::of::()) +} + +#[cfg(feature = "unsafe_stable_anymap")] +fn type_repr_owned() -> TypeRepr { + type_name::().to_string() +} + +#[cfg(feature = "unsafe_stable_anymap")] +fn type_repr() -> &'static str { + type_name::() +} + /// A (de)serializable Any trait pub trait SerdeAny: Any + erased_serde::Serialize + Debug { /// returns this as Any trait @@ -67,19 +110,18 @@ where pub mod serdeany_registry { use alloc::boxed::Box; - use core::{any::TypeId, fmt, hash::BuildHasherDefault}; + use core::{fmt, hash::BuildHasherDefault}; use hashbrown::{ - hash_map::{Keys, Values, ValuesMut}, + hash_map::{Values, ValuesMut}, HashMap, }; use serde::{Deserialize, Serialize}; - use super::SerdeAny; + use super::{SerdeAny, TypeRepr}; use crate::{ - anymap::{pack_type_id, unpack_type_id}, hash_std, - serdeany::{DeserializeCallback, DeserializeCallbackSeed}, + serdeany::{type_repr, type_repr_owned, DeserializeCallback, DeserializeCallbackSeed}, Error, }; @@ -98,7 +140,7 @@ pub mod serdeany_registry { where V: serde::de::SeqAccess<'de>, { - let id: u128 = visitor.next_element()?.unwrap(); + let id: TypeRepr = visitor.next_element()?.unwrap(); let cb = unsafe { *REGISTRY .deserializers @@ -115,7 +157,8 @@ pub mod serdeany_registry { #[allow(unused_qualifications)] struct Registry { - deserializers: Option>>, + deserializers: + Option>>, finalized: bool, } @@ -128,7 +171,7 @@ pub mod serdeany_registry { assert!(!self.finalized, "Registry is already finalized!"); let deserializers = self.deserializers.get_or_insert_with(HashMap::default); - deserializers.insert(unpack_type_id(TypeId::of::()), |de| { + deserializers.insert(type_repr_owned::(), |de| { Ok(Box::new(erased_serde::deserialize::(de)?)) }); } @@ -181,7 +224,7 @@ pub mod serdeany_registry { #[allow(clippy::unsafe_derive_deserialize)] #[derive(Debug, Serialize, Deserialize)] pub struct SerdeAnyMap { - map: HashMap>, + map: HashMap>, } // Cloning by serializing and deserializing. It ain't fast, but it's honest work. @@ -218,8 +261,12 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + self.map - .get(&unpack_type_id(TypeId::of::())) + .get(type_repr) .map(|x| x.as_ref().as_any().downcast_ref::().unwrap()) } @@ -230,8 +277,12 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + self.map - .get_mut(&unpack_type_id(TypeId::of::())) + .get_mut(type_repr) .map(|x| x.as_mut().as_any_mut().downcast_mut::().unwrap()) } @@ -242,8 +293,12 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + self.map - .remove(&unpack_type_id(TypeId::of::())) + .remove(type_repr) .map(|x| x.as_any_boxed().downcast::().unwrap()) } @@ -262,38 +317,42 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { - self.entry::().insert(value); + self.raw_entry_mut::() + .insert(type_repr_owned::(), value); } /// Get an entry to an element in this map. #[inline] #[allow(unused_qualifications)] - pub fn entry( + pub fn raw_entry_mut( &mut self, - ) -> hashbrown::hash_map::Entry< + ) -> hashbrown::hash_map::RawEntryMut< '_, - u128, + TypeRepr, Box, BuildHasherDefault, > where T: crate::serdeany::SerdeAny, { - let id = unpack_type_id(TypeId::of::()); + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + assert!( unsafe { REGISTRY .deserializers .as_ref() .expect("Empty types registry") - .get(&id) + .get(type_repr) .is_some() }, "Type {} was inserted without registration! Call RegistryBuilder::register::<{}>() or use serde_autoreg.", core::any::type_name::(), core::any::type_name::() ); - self.map.entry(id) + self.map.raw_entry_mut().from_key(type_repr) } /// Gets a value by type, or inserts it using the given construction function `default` @@ -309,8 +368,10 @@ pub mod serdeany_registry { where T: SerdeAny + 'static, { - let ret = self.entry::().or_insert_with(|| default()); - ret.as_mut().as_any_mut().downcast_mut::().unwrap() + let ret = self + .raw_entry_mut::() + .or_insert_with(|| (type_repr_owned::(), default())); + ret.1.as_any_mut().downcast_mut::().unwrap() } /// Returns the count of elements in this map. @@ -333,7 +394,11 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { - self.map.contains_key(&unpack_type_id(TypeId::of::())) + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + + self.map.contains_key(type_repr) } /// Create a new [`SerdeAnyMap`]. @@ -356,7 +421,7 @@ pub mod serdeany_registry { #[allow(unused_qualifications)] #[derive(Debug, Serialize, Deserialize)] pub struct NamedSerdeAnyMap { - map: HashMap>>, + map: HashMap>>, } // Cloning by serializing and deserializing. It ain't fast, but it's honest work. @@ -377,7 +442,11 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { - match self.map.get(&unpack_type_id(TypeId::of::())) { + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + + match self.map.get(type_repr) { None => None, Some(h) => h .get(&hash_std(name.as_bytes())) @@ -392,7 +461,11 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { - match self.map.get_mut(&unpack_type_id(TypeId::of::())) { + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + + match self.map.get_mut(type_repr) { None => None, Some(h) => h .remove(&hash_std(name.as_bytes())) @@ -400,29 +473,18 @@ pub mod serdeany_registry { } } - /// Get an element of a given type contained in this map by [`TypeId`]. - #[must_use] - #[allow(unused_qualifications)] - #[inline] - pub fn by_typeid( - &self, - name: &str, - typeid: &TypeId, - ) -> Option<&dyn crate::serdeany::SerdeAny> { - match self.map.get(&unpack_type_id(*typeid)) { - None => None, - Some(h) => h.get(&hash_std(name.as_bytes())).map(AsRef::as_ref), - } - } - - /// Get an element of a given type contained in this map by [`TypeId`], as mut. + /// Get an element of a given type contained in this map by type `T`, as mut. #[must_use] #[inline] pub fn get_mut(&mut self, name: &str) -> Option<&mut T> where T: crate::serdeany::SerdeAny, { - match self.map.get_mut(&unpack_type_id(TypeId::of::())) { + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + + match self.map.get_mut(type_repr) { None => None, Some(h) => h .get_mut(&hash_std(name.as_bytes())) @@ -430,20 +492,6 @@ pub mod serdeany_registry { } } - /// Get an element of a given type contained in this map by [`TypeId`], as mut. - #[must_use] - #[inline] - pub fn by_typeid_mut( - &mut self, - name: &str, - typeid: &TypeId, - ) -> Option<&mut dyn crate::serdeany::SerdeAny> { - match self.map.get_mut(&unpack_type_id(*typeid)) { - None => None, - Some(h) => h.get_mut(&hash_std(name.as_bytes())).map(AsMut::as_mut), - } - } - /// Get all elements of a type contained in this map. #[must_use] #[allow(unused_qualifications)] @@ -460,34 +508,17 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + #[allow(clippy::manual_map)] - match self.map.get(&unpack_type_id(TypeId::of::())) { + match self.map.get(type_repr) { None => None, Some(h) => Some(h.values().map(|x| x.as_any().downcast_ref::().unwrap())), } } - /// Get all elements of a given type contained in this map by [`TypeId`]. - #[must_use] - #[allow(unused_qualifications)] - #[inline] - #[allow(clippy::type_complexity)] - pub fn all_by_typeid( - &self, - typeid: &TypeId, - ) -> Option< - core::iter::Map< - Values<'_, u64, Box>, - fn(&Box) -> &dyn crate::serdeany::SerdeAny, - >, - > { - #[allow(clippy::manual_map)] - match self.map.get(&unpack_type_id(*typeid)) { - None => None, - Some(h) => Some(h.values().map(|x| x.as_ref())), - } - } - /// Get all elements contained in this map, as mut. #[inline] #[allow(unused_qualifications)] @@ -503,8 +534,12 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + #[allow(clippy::manual_map)] - match self.map.get_mut(&unpack_type_id(TypeId::of::())) { + match self.map.get_mut(type_repr) { None => None, Some(h) => Some( h.values_mut() @@ -513,51 +548,18 @@ pub mod serdeany_registry { } } - /// Get all [`TypeId`]`s` contained in this map, as mut. - #[inline] - #[allow(unused_qualifications)] - #[allow(clippy::type_complexity)] - pub fn all_by_typeid_mut( - &mut self, - typeid: &TypeId, - ) -> Option< - core::iter::Map< - ValuesMut<'_, u64, Box>, - fn(&mut Box) -> &mut dyn crate::serdeany::SerdeAny, - >, - > { - #[allow(clippy::manual_map)] - match self.map.get_mut(&unpack_type_id(*typeid)) { - None => None, - Some(h) => Some(h.values_mut().map(|x| x.as_mut())), - } - } - - /// Get all [`TypeId`]`s` contained in this map. - #[inline] - #[allow(unused_qualifications)] - #[allow(clippy::type_complexity)] - pub fn all_typeids( - &self, - ) -> core::iter::Map< - Keys<'_, u128, HashMap>>, - fn(&u128) -> TypeId, - > { - self.map.keys().map(|x| pack_type_id(*x)) - } - /// Run `func` for each element in this map. #[inline] #[allow(unused_qualifications)] pub fn for_each< - F: FnMut(&TypeId, &Box) -> Result<(), Error>, + F: FnMut(&TypeRepr, &Box) -> Result<(), Error>, >( &self, func: &mut F, ) -> Result<(), Error> { for (id, h) in &self.map { for x in h.values() { - func(&pack_type_id(*id), x)?; + func(id, x)?; } } Ok(()) @@ -566,14 +568,14 @@ pub mod serdeany_registry { /// Run `func` for each element in this map, getting a mutable borrow. #[inline] pub fn for_each_mut< - F: FnMut(&TypeId, &mut Box) -> Result<(), Error>, + F: FnMut(&TypeRepr, &mut Box) -> Result<(), Error>, >( &mut self, func: &mut F, ) -> Result<(), Error> { for (id, h) in &mut self.map { for x in h.values_mut() { - func(&pack_type_id(*id), x)?; + func(id, x)?; } } Ok(()) @@ -604,14 +606,17 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { - let id = unpack_type_id(TypeId::of::()); + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + assert!( unsafe { REGISTRY .deserializers .as_ref() .expect("Empty types registry") - .get(&id) + .get(type_repr) .is_some() }, "Type {} was inserted without registration! Call RegistryBuilder::register::<{}>() or use serde_autoreg.", @@ -619,8 +624,10 @@ pub mod serdeany_registry { core::any::type_name::() ); self.map - .entry(id) - .or_default() + .raw_entry_mut() + .from_key(type_repr) + .or_insert_with(|| (type_repr_owned::(), HashMap::default())) + .1 .entry(hash_std(name.as_bytes())) } @@ -666,7 +673,11 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { - self.map.contains_key(&unpack_type_id(TypeId::of::())) + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + + self.map.contains_key(type_repr) } /// Returns if the element by a given `name` is contained in this map. @@ -676,7 +687,11 @@ pub mod serdeany_registry { where T: crate::serdeany::SerdeAny, { - match self.map.get(&unpack_type_id(TypeId::of::())) { + let type_repr = type_repr::(); + #[cfg(not(feature = "unsafe_stable_anymap"))] + let type_repr = &type_repr; + + match self.map.get(type_repr) { None => false, Some(h) => h.contains_key(&hash_std(name.as_bytes())), }