diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index 21c66ec974..388e2bda9e 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -32,7 +32,7 @@ use crate::{ events::EventFirer, executors::ExitKind, inputs::Input, - observers::{ObserversTuple, TimeObserver}, + observers::{ListObserver, ObserversTuple, TimeObserver}, state::HasClientPerfMonitor, Error, }; @@ -913,3 +913,74 @@ impl TimeFeedback { } } } + +/// Consider interesting a testcase if the list in ListObserver is not empty. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ListFeedback +where + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + name: String, + phantom: PhantomData, +} + +impl Feedback for ListFeedback +where + I: Input, + S: HasClientPerfMonitor, + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // TODO Replace with match_name_type when stable + let observer = observers + .match_name::>(self.name()) + .unwrap(); + // TODO register the list content in a testcase metadata + Ok(!observer.list().is_empty()) + } +} + +impl Named for ListFeedback +where + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl ListFeedback +where + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + /// Creates a new [`ListFeedback`], deciding if the value of a [`ListObserver`] with the given `name` of a run is interesting. + #[must_use] + pub fn new(name: &'static str) -> Self { + Self { + name: name.to_string(), + phantom: PhantomData, + } + } + + /// Creates a new [`TimeFeedback`], deciding if the given [`ListObserver`] value of a run is interesting. + #[must_use] + pub fn new_with_observer(observer: &ListObserver) -> Self { + Self { + name: observer.name().to_string(), + phantom: PhantomData, + } + } +} diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 1bf41b041d..59f336ddad 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -30,6 +30,7 @@ use serde::{Deserialize, Serialize}; use crate::{ bolts::{ current_time, + ownedref::OwnedRefMut, tuples::{MatchName, Named}, }, executors::ExitKind, @@ -229,6 +230,61 @@ impl Named for TimeObserver { } } +/// A simple observer with a list of things. +#[derive(Serialize, Deserialize, Debug)] +#[serde(bound = "T: serde::de::DeserializeOwned")] +pub struct ListObserver<'a, T> +where + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + name: String, + /// The list + list: OwnedRefMut<'a, Vec>, +} + +impl<'a, T> ListObserver<'a, T> +where + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + /// Creates a new [`ListObserver`] with the given name. + #[must_use] + pub fn new(name: &'static str, list: &'a mut Vec) -> Self { + Self { + name: name.to_string(), + list: OwnedRefMut::Ref(list), + } + } + + /// Get a list ref + pub fn list(&self) -> &Vec { + self.list.as_ref() + } + + /// Get a list mut + pub fn list_mut(&mut self) -> &mut Vec { + self.list.as_mut() + } +} + +impl<'a, I, S, T> Observer for ListObserver<'a, T> +where + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.list.as_mut().clear(); + Ok(()) + } +} + +impl<'a, T> Named for ListObserver<'a, T> +where + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + fn name(&self) -> &str { + &self.name + } +} + #[cfg(feature = "std")] #[cfg(test)] mod tests {