diff --git a/TODO.md b/TODO.md deleted file mode 100644 index 9d2d0b3690..0000000000 --- a/TODO.md +++ /dev/null @@ -1,26 +0,0 @@ -# TODOs - -- [ ] Objective-Specific Corpuses (named per objective) -- [ ] Good documentation -- [ ] More informative outputs, deeper introspection (monitor, what mutation did x, etc.) -- [ ] Timeout handling for llmp clients (no ping for n seconds -> treat as disconnected) -- [ ] Heap for signal handling (bumpallo or llmp directly?) -- [x] Frida support for Windows -- [x] LAIN / structured fuzzing example -- [x] LLMP compression -- [x] AFL-Style Forkserver Executor -- [x] "Launcher" example that spawns broker + n clients -- [x] QEMU based instrumentation -- [x] AFL++ LLVM passes in libafl_cc -- [x] LLMP Cross Machine Link (2 brokers connected via TCP) -- [x] Conditional composition of feedbacks (issue #24) -- [x] Other objectives examples (e.g. execution of a given program point) -- [x] Restart Count in Fuzzing Loop -- [x] Minset corpus scheduler -- [x] Win32 shared mem and crash handler to have Windows in-process executor -- [x] Other feedbacks examples (e.g. maximize allocations to spot OOMs) -- [x] A macro crate with derive directives (e.g. for SerdeAny impl). -- [x] Restarting EventMgr could use forks on Unix -- [x] Android Ashmem support -- [x] Errors in the Fuzzer should exit the fuzz run -- [x] Timeouts for executors (WIP on Windows) diff --git a/libafl/src/bolts/anymap.rs b/libafl/src/bolts/anymap.rs new file mode 100644 index 0000000000..d5a3f07aa3 --- /dev/null +++ b/libafl/src/bolts/anymap.rs @@ -0,0 +1,436 @@ +//! Poor-rust-man's downcasts to have `AnyMap` + +use alloc::boxed::Box; +use core::{ + any::{Any, TypeId}, + ptr::addr_of, +}; + +/// Convert to an Any trait object +pub trait AsAny: Any { + /// returns this as Any trait + fn as_any(&self) -> &dyn Any; + /// returns this as mutable Any trait + fn as_any_mut(&mut self) -> &mut dyn Any; + /// returns this as boxed Any trait + fn as_any_boxed(self: Box) -> Box; +} + +/// Implement `AsAny` for a type +#[macro_export] +macro_rules! impl_asany { + ($struct_name:ident $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)?) => { + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? $crate::bolts::anymap::AsAny for $struct_name $(< $( $lt ),+ >)? { + fn as_any(&self) -> &dyn ::core::any::Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any { + self + } + + fn as_any_boxed( + self: ::alloc::boxed::Box, + ) -> ::alloc::boxed::Box { + self + } + } + }; +} + +// yolo + +/// Get a `type_id` from its previously unpacked `u64`. +/// Opposite of [`unpack_type_id(id)`]. +/// +/// # Safety +/// Probably not safe for future compilers, fine for now. +#[must_use] +pub fn pack_type_id(id: u64) -> TypeId { + assert_eq_size!(TypeId, u64); + unsafe { *(addr_of!(id) as *const TypeId) } +} + +/// Unpack a `type_id` to an `u64` +/// Opposite of [`pack_type_id(id)`]. +/// +/// # Safety +/// Probably not safe for future compilers, fine for now. +#[must_use] +pub fn unpack_type_id(id: TypeId) -> u64 { + assert_eq_size!(TypeId, u64); + unsafe { *(addr_of!(id) as *const u64) } +} + +/// Create `AnyMap` and `NamedAnyMap` for a given trait +#[macro_export] +macro_rules! create_anymap_for_trait { + ( $mod_name:ident, $parent_mod:path, $trait_name:ident $(< $( $lt:tt $( : $clt:tt $(+ $dlt:tt )* )? ),+ >)? $(, $attrs:meta)*) => { + mod $mod_name { + use alloc::boxed::Box; + use core::any::TypeId; + + use hashbrown::hash_map::{Keys, Values, ValuesMut}; + use hashbrown::HashMap; + + use $crate::bolts::anymap::{pack_type_id, unpack_type_id}; + use $crate::Error; + + use super::*; + #[allow(unused_import_braces)] + use $parent_mod::{$trait_name}; + + /// An anymap containing trait objects + #[derive(Default)] + $(#[$attrs])* + pub struct AnyMap $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? { + map: HashMap)?>>, + } + + #[allow(unused_qualifications)] + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? AnyMap $(< $( $lt ),+ >)? { + /// Get an element from the map. + #[must_use] + #[inline] + pub fn get(&self) -> Option<&T> + where + T: $trait_name $(< $( $lt ),+ >)?, + { + self.map + .get(&unpack_type_id(TypeId::of::())) + .map(|x| x.as_ref().as_any().downcast_ref::().unwrap()) + } + + /// Get a mutable borrow for an element in the map. + #[must_use] + #[inline] + pub fn get_mut(&mut self) -> Option<&mut T> + where + T: $trait_name $(< $( $lt ),+ >)?, + { + self.map + .get_mut(&unpack_type_id(TypeId::of::())) + .map(|x| x.as_mut().as_any_mut().downcast_mut::().unwrap()) + } + + /// Remove an element in the map. Returns the removed element. + #[must_use] + #[inline] + pub fn remove(&mut self) -> Option> + where + T: $trait_name $(< $( $lt ),+ >)?, + { + self.map + .remove(&unpack_type_id(TypeId::of::())) + .map(|x| x.as_any_boxed().downcast::().unwrap()) + } + + /// Insert an element into the map. + #[inline] + pub fn insert(&mut self, t: T) + where + T: $trait_name $(< $( $lt ),+ >)?, + { + self.map + .insert(unpack_type_id(TypeId::of::()), Box::new(t)); + } + + /// Insert a boxed element into the map. + #[inline] + pub fn insert_boxed(&mut self, t: Box) + where + T: $trait_name $(< $( $lt ),+ >)?, + { + self.map.insert(unpack_type_id(TypeId::of::()), t); + } + + /// Returns the count of elements in this map. + #[must_use] + #[inline] + pub fn len(&self) -> usize { + self.map.len() + } + + /// Returns `true` if this map is empty. + #[must_use] + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + /// Returns if the map contains the given type. + #[must_use] + #[inline] + pub fn contains(&self) -> bool + where + T: $trait_name $(< $( $lt ),+ >)?, + { + self.map.contains_key(&unpack_type_id(TypeId::of::())) + } + + /// Create a new [`AnyMap`]. + #[must_use] + pub fn new() -> Self { + AnyMap { + map: HashMap::default(), + } + } + } + + /// An anymap, addressable by name and type, containing trait objects + #[allow(unused_qualifications)] + #[derive(Default)] + $(#[$attrs])* + pub struct NamedAnyMap $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? { + map: HashMap)?>>>, + } + + #[allow(unused_qualifications)] + impl $(< $( $lt $( : $clt $(+ $dlt )* )? ),+ >)? NamedAnyMap $(< $( $lt ),+ >)? { + /// Get an element by name + #[must_use] + #[inline] + pub fn get(&self, name: &str) -> Option<&T> + where + T: $trait_name $(< $( $lt ),+ >)?, + { + match self.map.get(&unpack_type_id(TypeId::of::())) { + None => None, + Some(h) => h + .get(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())) + .map(|x| x.as_any().downcast_ref::().unwrap()), + } + } + + /// 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 $trait_name $(< $( $lt ),+ >)?> { + match self.map.get(&unpack_type_id(*typeid)) { + None => None, + Some(h) => h + .get(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())) + .map(AsRef::as_ref), + } + } + + /// Get an element of a given type contained in this map by [`TypeId`], as mut. + #[must_use] + #[inline] + pub fn get_mut(&mut self, name: &str) -> Option<&mut T> + where + T: $trait_name $(< $( $lt ),+ >)?, + { + match self.map.get_mut(&unpack_type_id(TypeId::of::())) { + None => None, + Some(h) => h + .get_mut(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())) + .map(|x| x.as_any_mut().downcast_mut::().unwrap()), + } + } + + /// 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 $trait_name $(< $( $lt ),+ >)?> { + match self.map.get_mut(&unpack_type_id(*typeid)) { + None => None, + Some(h) => h + .get_mut(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())) + .map(AsMut::as_mut), + } + } + + /// Get all elements of a type contained in this map. + #[must_use] + #[allow(unused_qualifications)] + #[inline] + pub fn get_all( + &self, + ) -> Option< + core::iter::Map< + Values<'_, u64, Box)?>>, + fn(&Box)?>) -> &T, + >, + > + where + T: $trait_name $(< $( $lt ),+ >)?, + { + #[allow(clippy::manual_map)] + match self.map.get(&unpack_type_id(TypeId::of::())) { + 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] + pub fn all_by_typeid( + &self, + typeid: &TypeId, + ) -> Option< + core::iter::Map< + Values<'_, u64, Box)?>>, + fn(&Box)?>) -> &dyn $trait_name $(< $( $lt ),+ >)?, + >, + > { + #[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)] + pub fn get_all_mut( + &mut self, + ) -> Option< + core::iter::Map< + ValuesMut<'_, u64, Box)?>>, + fn(&mut Box)?>) -> &mut T, + >, + > + where + T: $trait_name $(< $( $lt ),+ >)?, + { + #[allow(clippy::manual_map)] + match self.map.get_mut(&unpack_type_id(TypeId::of::())) { + None => None, + Some(h) => Some( + h.values_mut() + .map(|x| x.as_any_mut().downcast_mut::().unwrap()), + ), + } + } + + /// Get all [`TypeId`]`s` contained in this map, as mut. + #[inline] + #[allow(unused_qualifications)] + pub fn all_by_typeid_mut( + &mut self, + typeid: &TypeId, + ) -> Option< + core::iter::Map< + ValuesMut<'_, u64, Box)?>>, + fn(&mut Box)?>) -> &mut dyn $trait_name $(< $( $lt ),+ >)?, + >, + > { + #[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)] + pub fn all_typeids( + &self, + ) -> core::iter::Map< + Keys<'_, u64, HashMap)?>>>, + fn(&u64) -> 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)?>) -> Result<(), Error>>( + &self, + func: &mut F, + ) -> Result<(), Error> { + for (id, h) in self.map.iter() { + for x in h.values() { + func(&pack_type_id(*id), x)?; + } + } + Ok(()) + } + + /// Run `func` for each element in this map, getting a mutable borrow. + #[inline] + pub fn for_each_mut)?>) -> Result<(), Error>>( + &mut self, + func: &mut F, + ) -> Result<(), Error> { + for (id, h) in self.map.iter_mut() { + for x in h.values_mut() { + func(&pack_type_id(*id), x)?; + } + } + Ok(()) + } + + /// Insert an element into this map. + #[inline] + #[allow(unused_qualifications)] + pub fn insert(&mut self, val: Box)?>, name: &str) { + let id = unpack_type_id((*val).type_id()); + if !self.map.contains_key(&id) { + self.map.insert(id, HashMap::default()); + } + self.map + .get_mut(&id) + .unwrap() + .insert(xxhash_rust::xxh3::xxh3_64(name.as_bytes()), val); + } + + /// Returns the `len` of this map. + #[must_use] + #[inline] + pub fn len(&self) -> usize { + self.map.len() + } + + /// Returns `true` if this map is empty. + #[must_use] + pub fn is_empty(&self) -> bool { + self.map.is_empty() + } + + /// Returns if the element with a given type is contained in this map. + #[must_use] + #[inline] + pub fn contains_type(&self) -> bool + where + T: $trait_name $(< $( $lt ),+ >)?, + { + self.map.contains_key(&unpack_type_id(TypeId::of::())) + } + + /// Returns if the element by a given `name` is contained in this map. + #[must_use] + #[inline] + pub fn contains(&self, name: &str) -> bool + where + T: $trait_name $(< $( $lt ),+ >)?, + { + match self.map.get(&unpack_type_id(TypeId::of::())) { + None => false, + Some(h) => h.contains_key(&xxhash_rust::xxh3::xxh3_64(name.as_bytes())), + } + } + + /// Create a new `SerdeAny` map. + #[must_use] + pub fn new() -> Self { + Self { + map: HashMap::default(), + } + } + } + } + } +} diff --git a/libafl/src/bolts/mod.rs b/libafl/src/bolts/mod.rs index e86ef6bef0..281c870da7 100644 --- a/libafl/src/bolts/mod.rs +++ b/libafl/src/bolts/mod.rs @@ -1,5 +1,6 @@ //! Bolts are no conceptual fuzzing elements, but they keep libafl-based fuzzers together. +pub mod anymap; #[cfg(feature = "llmp_compression")] pub mod compress; pub mod cpu; diff --git a/libafl/src/bolts/serdeany.rs b/libafl/src/bolts/serdeany.rs index 8cea851276..303c36c80a 100644 --- a/libafl/src/bolts/serdeany.rs +++ b/libafl/src/bolts/serdeany.rs @@ -3,35 +3,7 @@ use serde::{de::DeserializeSeed, Deserialize, Deserializer, Serialize, Serializer}; use alloc::boxed::Box; -use core::{ - any::{Any, TypeId}, - fmt::Debug, - ptr::addr_of, -}; - -// yolo - -/// Get a `type_id` from its previously unpacked `u64`. -/// Opposite of [`unpack_type_id(id)`]. -/// -/// # Safety -/// Probably not safe for future compilers, fine for now. -#[must_use] -pub fn pack_type_id(id: u64) -> TypeId { - assert_eq_size!(TypeId, u64); - unsafe { *(addr_of!(id) as *const TypeId) } -} - -/// Unpack a `type_id` to an `u64` -/// Opposite of [`pack_type_id(id)`]. -/// -/// # Safety -/// Probably not safe for future compilers, fine for now. -#[must_use] -pub fn unpack_type_id(id: TypeId) -> u64 { - assert_eq_size!(TypeId, u64); - unsafe { *(addr_of!(id) as *const u64) } -} +use core::{any::Any, fmt::Debug}; /// A (de)serializable Any trait pub trait SerdeAny: Any + erased_serde::Serialize + Debug { @@ -97,7 +69,7 @@ macro_rules! create_serde_registry_for_trait { pub mod $mod_name { use alloc::boxed::Box; - use core::any::{Any, TypeId}; + use core::any::TypeId; use core::fmt; use postcard; use serde::{Deserialize, Serialize}; @@ -105,8 +77,9 @@ macro_rules! create_serde_registry_for_trait { use hashbrown::hash_map::{Keys, Values, ValuesMut}; use hashbrown::HashMap; - use $crate::bolts::serdeany::{ - pack_type_id, unpack_type_id, DeserializeCallback, DeserializeCallbackSeed, + use $crate::bolts::{ + anymap::{pack_type_id, unpack_type_id}, + serdeany::{DeserializeCallback, DeserializeCallbackSeed}, }; use $crate::Error; @@ -336,7 +309,7 @@ macro_rules! create_serde_registry_for_trait { #[inline] pub fn get(&self, name: &str) -> Option<&T> where - T: Any, + T: $trait_name, { match self.map.get(&unpack_type_id(TypeId::of::())) { None => None, @@ -364,7 +337,7 @@ macro_rules! create_serde_registry_for_trait { #[inline] pub fn get_mut(&mut self, name: &str) -> Option<&mut T> where - T: Any, + T: $trait_name, { match self.map.get_mut(&unpack_type_id(TypeId::of::())) { None => None, @@ -403,7 +376,7 @@ macro_rules! create_serde_registry_for_trait { >, > where - T: Any, + T: $trait_name, { #[allow(clippy::manual_map)] match self.map.get(&unpack_type_id(TypeId::of::())) { @@ -446,7 +419,7 @@ macro_rules! create_serde_registry_for_trait { >, > where - T: Any, + T: $trait_name, { #[allow(clippy::manual_map)] match self.map.get_mut(&unpack_type_id(TypeId::of::())) { @@ -492,9 +465,9 @@ macro_rules! create_serde_registry_for_trait { /// Run `func` for each element in this map. #[inline] #[allow(unused_qualifications)] - pub fn for_each( + pub fn for_each) -> Result<(), Error>>( &self, - func: fn(&TypeId, &Box) -> Result<(), Error>, + func: &mut F, ) -> Result<(), Error> { for (id, h) in self.map.iter() { for x in h.values() { @@ -506,9 +479,11 @@ macro_rules! create_serde_registry_for_trait { /// Run `func` for each element in this map, getting a mutable borrow. #[inline] - pub fn for_each_mut( + pub fn for_each_mut< + F: FnMut(&TypeId, &mut Box) -> Result<(), Error>, + >( &mut self, - func: fn(&TypeId, &mut Box) -> Result<(), Error>, + func: &mut F, ) -> Result<(), Error> { for (id, h) in self.map.iter_mut() { for x in h.values_mut() { @@ -550,7 +525,7 @@ macro_rules! create_serde_registry_for_trait { #[inline] pub fn contains_type(&self) -> bool where - T: Any, + T: $trait_name, { self.map.contains_key(&unpack_type_id(TypeId::of::())) } @@ -560,7 +535,7 @@ macro_rules! create_serde_registry_for_trait { #[inline] pub fn contains(&self, name: &str) -> bool where - T: Any, + T: $trait_name, { match self.map.get(&unpack_type_id(TypeId::of::())) { None => false, @@ -592,7 +567,7 @@ macro_rules! create_serde_registry_for_trait { { use serde::ser::SerializeSeq; - let id = $crate::bolts::serdeany::unpack_type_id(self.type_id()); + let id = $crate::bolts::anymap::unpack_type_id(self.type_id()); let mut seq = se.serialize_seq(Some(2))?; seq.serialize_element(&id)?; seq.serialize_element(&$crate::bolts::serdeany::Wrap(self))?; diff --git a/libafl/src/lib.rs b/libafl/src/lib.rs index b59bf65ed8..7fec940b4e 100644 --- a/libafl/src/lib.rs +++ b/libafl/src/lib.rs @@ -3,7 +3,10 @@ Welcome to `LibAFL` */ #![cfg_attr(not(feature = "std"), no_std)] +// For `type_eq` #![cfg_attr(unstable_feature, feature(specialization))] +// For `type_id` and owned things +#![cfg_attr(unstable_feature, feature(intrinsics))] #![deny(rustdoc::broken_intra_doc_links)] #![deny(clippy::pedantic)] #![allow( diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index f1f32a8d13..6df2c65e1a 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -8,6 +8,11 @@ pub use cmp::*; pub mod concolic; +#[cfg(unstable_feature)] +pub mod owned; +#[cfg(unstable_feature)] +pub use owned::*; + use alloc::string::{String, ToString}; use core::{fmt::Debug, time::Duration}; use serde::{Deserialize, Serialize}; diff --git a/libafl/src/observers/owned.rs b/libafl/src/observers/owned.rs new file mode 100644 index 0000000000..ef6b6705d6 --- /dev/null +++ b/libafl/src/observers/owned.rs @@ -0,0 +1,97 @@ +//! A dynamic collection of owned observers, working only with unstable rust + +use core::{any::Any, fmt::Debug}; +use serde::{Deserialize, Deserializer, Serialize, Serializer}; + +use crate::{ + bolts::{ + anymap::{pack_type_id, AsAny}, + tuples::MatchName, + }, + observers::{Observer, ObserversTuple}, + Error, +}; + +////////// Warning, unsafe as hell, this bypass the standard library /////////// + +extern "rust-intrinsic" { + fn type_id() -> u64; +} + +unsafe fn downcast_ref_unsafe(any: &dyn Any) -> &T { + &*(any as *const dyn Any as *const T) +} + +unsafe fn downcast_mut_unsafe(any: &mut dyn Any) -> &mut T { + &mut *(any as *mut dyn Any as *mut T) +} + +//////////////////////////////////////////////////////////////////////////////// + +/// Combine `Observer` and `AsAny` +pub trait AnyObserver: Observer + AsAny {} + +crate::create_anymap_for_trait!( + observers_anymap, + super, + AnyObserver, + derive(Debug) +); +pub use observers_anymap::{AnyMap as ObserversAnyMap, NamedAnyMap as NamedObserversAnyMap}; + +/// An owned list of `Observer` trait objects +/// This is not really serializable, using this struct needs [`EventConfig::AlwaysUnique`] as configuration +#[derive(Debug, Default)] +pub struct ObserversOwnedMap { + /// The named trait objects map + pub map: NamedObserversAnyMap, +} + +impl Serialize for ObserversOwnedMap { + fn serialize(&self, _serializer: T) -> Result + where + T: Serializer, + { + panic!("Cannot serialize ObserversOwnedMap, use EventConfig::AlwaysUnique as event manager configuration"); + } +} + +impl<'de, I: 'static + Debug, S: 'static + Debug> Deserialize<'de> for ObserversOwnedMap { + fn deserialize(_deserializer: D) -> Result + where + D: Deserializer<'de>, + { + panic!("Cannot deserialize ObserversOwnedMap, use EventConfig::AlwaysUnique as event manager configuration"); + } +} + +impl ObserversTuple for ObserversOwnedMap { + fn pre_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> { + self.map + .for_each_mut(&mut |_, ob| ob.pre_exec(state, input)) + } + + fn post_exec_all(&mut self, state: &mut S, input: &I) -> Result<(), Error> { + self.map + .for_each_mut(&mut |_, ob| ob.post_exec(state, input)) + } +} + +impl MatchName for ObserversOwnedMap { + fn match_name(&self, name: &str) -> Option<&T> { + unsafe { + let t = pack_type_id(type_id::()); + self.map + .by_typeid(name, &t) + .map(|x| downcast_ref_unsafe(x.as_any())) + } + } + fn match_name_mut(&mut self, name: &str) -> Option<&mut T> { + unsafe { + let t = pack_type_id(type_id::()); + self.map + .by_typeid_mut(name, &t) + .map(|x| downcast_mut_unsafe(x.as_any_mut())) + } + } +} diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index b09f81bafe..9d703db006 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -19,6 +19,9 @@ pub use calibrate::{CalibrationStage, PowerScheduleMetadata}; pub mod power; pub use power::PowerMutationalStage; +pub mod owned; +pub use owned::StagesOwnedList; + #[cfg(feature = "std")] pub mod concolic; #[cfg(feature = "std")] diff --git a/libafl/src/stages/owned.rs b/libafl/src/stages/owned.rs new file mode 100644 index 0000000000..efa4f5841b --- /dev/null +++ b/libafl/src/stages/owned.rs @@ -0,0 +1,44 @@ +//! A dynamic collection of owned Stages + +use alloc::{boxed::Box, vec::Vec}; + +use crate::{ + bolts::anymap::AsAny, + stages::{Stage, StagesTuple}, + Error, +}; + +/// Combine `Stage` and `AsAny` +pub trait AnyStage: Stage + AsAny {} + +/// An owned list of `Observer` trait objects +#[derive(Default)] +#[allow(missing_debug_implementations)] +pub struct StagesOwnedList { + /// The named trait objects map + pub list: Vec>>, +} + +impl StagesTuple for StagesOwnedList { + fn perform_all( + &mut self, + fuzzer: &mut Z, + executor: &mut E, + state: &mut S, + manager: &mut EM, + corpus_idx: usize, + ) -> Result<(), Error> { + for s in &mut self.list { + s.perform(fuzzer, executor, state, manager, corpus_idx)?; + } + Ok(()) + } +} + +impl StagesOwnedList { + /// Create a new instance + #[must_use] + pub fn new(list: Vec>>) -> Self { + Self { list } + } +}