SerdeAnyMap: add unsafe_stable_anymap feature that uses type_name instead of TypeId::of (#1952)

* Test: Use type_name instead of type_id in AnyMap

* Hide behind 'unsafe_stable_anymap' feature

* nostd?

* rename fn

* cleanup a bit
This commit is contained in:
Dominik Maier 2024-03-19 20:15:31 +01:00 committed by GitHub
parent 5eab4fb78b
commit 2efa747292
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 144 additions and 122 deletions

View File

@ -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"]

View File

@ -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<T>() -> TypeRepr
where
T: 'static,
{
unpack_type_id(TypeId::of::<T>())
}
#[cfg(not(feature = "unsafe_stable_anymap"))]
fn type_repr_owned<T>() -> TypeRepr
where
T: 'static,
{
unpack_type_id(TypeId::of::<T>())
}
#[cfg(feature = "unsafe_stable_anymap")]
fn type_repr_owned<T>() -> TypeRepr {
type_name::<T>().to_string()
}
#[cfg(feature = "unsafe_stable_anymap")]
fn type_repr<T>() -> &'static str {
type_name::<T>()
}
/// 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<HashMap<u128, DeserializeCallback<dyn crate::serdeany::SerdeAny>>>,
deserializers:
Option<HashMap<TypeRepr, DeserializeCallback<dyn crate::serdeany::SerdeAny>>>,
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::<T>()), |de| {
deserializers.insert(type_repr_owned::<T>(), |de| {
Ok(Box::new(erased_serde::deserialize::<T>(de)?))
});
}
@ -181,7 +224,7 @@ pub mod serdeany_registry {
#[allow(clippy::unsafe_derive_deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub struct SerdeAnyMap {
map: HashMap<u128, Box<dyn SerdeAny>>,
map: HashMap<TypeRepr, Box<dyn SerdeAny>>,
}
// 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::<T>();
#[cfg(not(feature = "unsafe_stable_anymap"))]
let type_repr = &type_repr;
self.map
.get(&unpack_type_id(TypeId::of::<T>()))
.get(type_repr)
.map(|x| x.as_ref().as_any().downcast_ref::<T>().unwrap())
}
@ -230,8 +277,12 @@ pub mod serdeany_registry {
where
T: crate::serdeany::SerdeAny,
{
let type_repr = type_repr::<T>();
#[cfg(not(feature = "unsafe_stable_anymap"))]
let type_repr = &type_repr;
self.map
.get_mut(&unpack_type_id(TypeId::of::<T>()))
.get_mut(type_repr)
.map(|x| x.as_mut().as_any_mut().downcast_mut::<T>().unwrap())
}
@ -242,8 +293,12 @@ pub mod serdeany_registry {
where
T: crate::serdeany::SerdeAny,
{
let type_repr = type_repr::<T>();
#[cfg(not(feature = "unsafe_stable_anymap"))]
let type_repr = &type_repr;
self.map
.remove(&unpack_type_id(TypeId::of::<T>()))
.remove(type_repr)
.map(|x| x.as_any_boxed().downcast::<T>().unwrap())
}
@ -262,38 +317,42 @@ pub mod serdeany_registry {
where
T: crate::serdeany::SerdeAny,
{
self.entry::<T>().insert(value);
self.raw_entry_mut::<T>()
.insert(type_repr_owned::<T>(), value);
}
/// Get an entry to an element in this map.
#[inline]
#[allow(unused_qualifications)]
pub fn entry<T>(
pub fn raw_entry_mut<T>(
&mut self,
) -> hashbrown::hash_map::Entry<
) -> hashbrown::hash_map::RawEntryMut<
'_,
u128,
TypeRepr,
Box<dyn SerdeAny + 'static>,
BuildHasherDefault<ahash::AHasher>,
>
where
T: crate::serdeany::SerdeAny,
{
let id = unpack_type_id(TypeId::of::<T>());
let type_repr = type_repr::<T>();
#[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::<T>(),
core::any::type_name::<T>()
);
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::<T>().or_insert_with(|| default());
ret.as_mut().as_any_mut().downcast_mut::<T>().unwrap()
let ret = self
.raw_entry_mut::<T>()
.or_insert_with(|| (type_repr_owned::<T>(), default()));
ret.1.as_any_mut().downcast_mut::<T>().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::<T>()))
let type_repr = type_repr::<T>();
#[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<u128, HashMap<u64, Box<dyn crate::serdeany::SerdeAny>>>,
map: HashMap<TypeRepr, HashMap<u64, Box<dyn crate::serdeany::SerdeAny>>>,
}
// 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::<T>())) {
let type_repr = type_repr::<T>();
#[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::<T>())) {
let type_repr = type_repr::<T>();
#[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<T>(&mut self, name: &str) -> Option<&mut T>
where
T: crate::serdeany::SerdeAny,
{
match self.map.get_mut(&unpack_type_id(TypeId::of::<T>())) {
let type_repr = type_repr::<T>();
#[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::<T>();
#[cfg(not(feature = "unsafe_stable_anymap"))]
let type_repr = &type_repr;
#[allow(clippy::manual_map)]
match self.map.get(&unpack_type_id(TypeId::of::<T>())) {
match self.map.get(type_repr) {
None => None,
Some(h) => Some(h.values().map(|x| x.as_any().downcast_ref::<T>().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<dyn crate::serdeany::SerdeAny>>,
fn(&Box<dyn crate::serdeany::SerdeAny>) -> &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::<T>();
#[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::<T>())) {
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<dyn crate::serdeany::SerdeAny>>,
fn(&mut Box<dyn crate::serdeany::SerdeAny>) -> &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<u64, Box<dyn crate::serdeany::SerdeAny>>>,
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<dyn crate::serdeany::SerdeAny>) -> Result<(), Error>,
F: FnMut(&TypeRepr, &Box<dyn crate::serdeany::SerdeAny>) -> 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<dyn crate::serdeany::SerdeAny>) -> Result<(), Error>,
F: FnMut(&TypeRepr, &mut Box<dyn crate::serdeany::SerdeAny>) -> 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::<T>());
let type_repr = type_repr::<T>();
#[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::<T>()
);
self.map
.entry(id)
.or_default()
.raw_entry_mut()
.from_key(type_repr)
.or_insert_with(|| (type_repr_owned::<T>(), 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::<T>()))
let type_repr = type_repr::<T>();
#[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::<T>())) {
let type_repr = type_repr::<T>();
#[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())),
}