diff --git a/fuzzers/tinyinst_simple/src/main.rs b/fuzzers/tinyinst_simple/src/main.rs index c029423b11..bc50e0d98b 100644 --- a/fuzzers/tinyinst_simple/src/main.rs +++ b/fuzzers/tinyinst_simple/src/main.rs @@ -40,7 +40,7 @@ fn main() { // let args = vec!["test.exe".to_string(), "-f".to_string(), "@@".to_string()]; let observer = unsafe { ListObserver::new("cov", &mut COVERAGE) }; - let mut feedback = ListFeedback::with_observer(&observer); + let mut feedback = ListFeedback::new(&observer); #[cfg(windows)] let mut shmem_provider = Win32ShMemProvider::new().unwrap(); diff --git a/libafl/src/feedbacks/list.rs b/libafl/src/feedbacks/list.rs new file mode 100644 index 0000000000..8cacc507ba --- /dev/null +++ b/libafl/src/feedbacks/list.rs @@ -0,0 +1,172 @@ +use alloc::string::{String, ToString}; +use core::{fmt::Debug, hash::Hash, marker::PhantomData}; + +use hashbrown::HashSet; +use libafl_bolts::{Error, HasRefCnt, Named}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; + +use crate::{ + events::EventFirer, + executors::ExitKind, + feedbacks::Feedback, + observers::{ListObserver, ObserversTuple}, + state::{HasNamedMetadata, State}, +}; + +/// The metadata to remember past observed value +#[derive(Default, Serialize, Deserialize, Clone, Debug)] +#[serde(bound = "T: DeserializeOwned")] +#[cfg_attr( + any(not(feature = "serdeany_autoreg"), miri), + allow(clippy::unsafe_derive_deserialize) +)] +pub struct ListFeedbackMetadata +where + T: Default + Copy + 'static + Serialize + Eq + Hash, +{ + /// Contains the information of past observed set of values. + pub set: HashSet, + /// A refcount used to know when we can remove this metadata + pub tcref: isize, +} + +impl ListFeedbackMetadata +where + T: Default + Copy + 'static + Serialize + Eq + Hash, +{ + /// The constructor + #[must_use] + pub fn new() -> Self { + Self { + set: HashSet::::new(), + tcref: 0, + } + } + + /// Reset the inner hashset + pub fn reset(&mut self) -> Result<(), Error> { + self.set.clear(); + Ok(()) + } +} + +impl HasRefCnt for ListFeedbackMetadata +where + T: Default + Copy + 'static + Serialize + Eq + Hash, +{ + fn refcnt(&self) -> isize { + self.tcref + } + + fn refcnt_mut(&mut self) -> &mut isize { + &mut self.tcref + } +} + +/// Consider interesting a testcase if the list in `ListObserver` is not empty. +#[derive(Clone, Debug)] +pub struct ListFeedback +where + T: Hash + Eq, +{ + name: String, + observer_name: String, + novelty: HashSet, + phantom: PhantomData, +} + +libafl_bolts::impl_serdeany!( + ListFeedbackMetadata, + ,,,,,,,,,, +); + +impl Feedback for ListFeedback +where + S: State + HasNamedMetadata, + T: Debug + Serialize + Hash + Eq + DeserializeOwned + Default + Copy + 'static, +{ + fn init_state(&mut self, state: &mut S) -> Result<(), Error> { + // eprintln!("self.name {:#?}", &self.name); + state.add_named_metadata(&self.name, ListFeedbackMetadata::::default()); + Ok(()) + } + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &S::Input, + 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.observer_name) + .unwrap(); + // TODO register the list content in a testcase metadata + self.novelty.clear(); + // can't fail + let history_set = state + .named_metadata_map_mut() + .get_mut::>(&self.name) + .unwrap(); + for v in observer.list() { + if !history_set.set.contains(v) { + self.novelty.insert(*v); + } + } + Ok(!self.novelty.is_empty()) + } + + fn append_metadata( + &mut self, + state: &mut S, + _manager: &mut EM, + _observers: &OT, + _testcase: &mut crate::corpus::Testcase<::Input>, + ) -> Result<(), Error> + where + OT: ObserversTuple, + EM: EventFirer, + { + let history_set = state + .named_metadata_map_mut() + .get_mut::>(&self.name) + .unwrap(); + + for v in &self.novelty { + history_set.set.insert(*v); + } + Ok(()) + } +} + +impl Named for ListFeedback +where + T: Debug + Serialize + Hash + Eq + DeserializeOwned, +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl ListFeedback +where + T: Debug + Serialize + Hash + Eq + DeserializeOwned, +{ + /// Creates a new [`ListFeedback`], deciding if the given [`ListObserver`] value of a run is interesting. + #[must_use] + pub fn new(observer: &ListObserver) -> Self { + Self { + name: observer.name().to_string(), + observer_name: observer.name().to_string(), + novelty: HashSet::::new(), + phantom: PhantomData, + } + } +} diff --git a/libafl/src/feedbacks/map.rs b/libafl/src/feedbacks/map.rs index f9a3d778b4..f70b87c413 100644 --- a/libafl/src/feedbacks/map.rs +++ b/libafl/src/feedbacks/map.rs @@ -28,9 +28,6 @@ use crate::{ Error, }; -/// The prefix of the metadata names -pub const MAPFEEDBACK_PREFIX: &str = "mapfeedback_metadata_"; - /// A [`MapFeedback`] that implements the AFL algorithm using an [`OrReducer`] combining the bits for the history map and the bit from ``HitcountsMapObserver``. pub type AflMapFeedback = MapFeedback; @@ -700,7 +697,7 @@ where Self { indexes: false, novelties: None, - name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(), + name: map_observer.name().to_string(), observer_name: map_observer.name().to_string(), stats_name: create_stats_name(map_observer.name()), phantom: PhantomData, @@ -713,7 +710,7 @@ where Self { indexes: track_indexes, novelties: if track_novelties { Some(vec![]) } else { None }, - name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(), + name: map_observer.name().to_string(), observer_name: map_observer.name().to_string(), stats_name: create_stats_name(map_observer.name()), phantom: PhantomData, diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index d030a75b3b..ce63e5f58a 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -25,6 +25,8 @@ pub use new_hash_feedback::NewHashFeedbackMetadata; pub mod nautilus; pub mod transferred; +/// The module for list feedback +pub mod list; use alloc::string::{String, ToString}; use core::{ fmt::{self, Debug, Formatter}, @@ -32,6 +34,7 @@ use core::{ }; use libafl_bolts::Named; +pub use list::*; #[cfg(feature = "nautilus")] pub use nautilus::*; use serde::{Deserialize, Serialize}; @@ -40,7 +43,7 @@ use crate::{ corpus::Testcase, events::EventFirer, executors::ExitKind, - observers::{ListObserver, ObserversTuple, TimeObserver}, + observers::{ObserversTuple, TimeObserver}, state::State, Error, }; @@ -972,79 +975,6 @@ 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, - last_addr: usize, - phantom: PhantomData, -} - -impl Feedback for ListFeedback -where - S: State, - T: Debug + Serialize + serde::de::DeserializeOwned, -{ - #[allow(clippy::wrong_self_convention)] - fn is_interesting( - &mut self, - _state: &mut S, - _manager: &mut EM, - _input: &S::Input, - 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(), - last_addr: 0, - phantom: PhantomData, - } - } - - /// Creates a new [`TimeFeedback`], deciding if the given [`ListObserver`] value of a run is interesting. - #[must_use] - pub fn with_observer(observer: &ListObserver) -> Self { - Self { - name: observer.name().to_string(), - last_addr: 0, - phantom: PhantomData, - } - } -} - /// The [`ConstFeedback`] reports the same value, always. /// It can be used to enable or disable feedback results through composition. #[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)] diff --git a/libafl/src/observers/list.rs b/libafl/src/observers/list.rs new file mode 100644 index 0000000000..d9cc467a20 --- /dev/null +++ b/libafl/src/observers/list.rs @@ -0,0 +1,73 @@ +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; +use core::fmt::Debug; + +use libafl_bolts::{ownedref::OwnedMutPtr, Error, Named}; +use serde::{Deserialize, Serialize}; + +use crate::{inputs::UsesInput, observers::Observer}; + +/// A simple observer with a list of things. +#[derive(Serialize, Deserialize, Debug)] +#[serde(bound = "T: serde::de::DeserializeOwned")] +#[allow(clippy::unsafe_derive_deserialize)] +pub struct ListObserver +where + T: Debug + Serialize, +{ + name: String, + /// The list + list: OwnedMutPtr>, +} + +impl ListObserver +where + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + /// Creates a new [`ListObserver`] with the given name. + /// + /// # Safety + /// Will dereference the list. + /// The list may not move in memory. + #[must_use] + pub unsafe fn new(name: &'static str, list: *mut Vec) -> Self { + Self { + name: name.to_string(), + list: OwnedMutPtr::Ptr(list), + } + } + + /// Get a list ref + #[must_use] + pub fn list(&self) -> &Vec { + self.list.as_ref() + } + + /// Get a list mut + #[must_use] + pub fn list_mut(&mut self) -> &mut Vec { + self.list.as_mut() + } +} + +impl Observer for ListObserver +where + S: UsesInput, + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { + self.list.as_mut().clear(); + Ok(()) + } +} + +impl Named for ListObserver +where + T: Debug + Serialize + serde::de::DeserializeOwned, +{ + fn name(&self) -> &str { + &self.name + } +} diff --git a/libafl/src/observers/mod.rs b/libafl/src/observers/mod.rs index 69f73d7066..2a5f904d83 100644 --- a/libafl/src/observers/mod.rs +++ b/libafl/src/observers/mod.rs @@ -20,17 +20,17 @@ pub mod concolic; pub mod value; -use alloc::{ - string::{String, ToString}, - vec::Vec, -}; +/// List observer +pub mod list; +use alloc::string::{String, ToString}; use core::{fmt::Debug, time::Duration}; #[cfg(feature = "std")] use std::time::Instant; #[cfg(feature = "no_std")] use libafl_bolts::current_time; -use libafl_bolts::{ownedref::OwnedMutPtr, tuples::MatchName, Named}; +use libafl_bolts::{tuples::MatchName, Named}; +pub use list::*; use serde::{Deserialize, Serialize}; pub use value::*; @@ -526,73 +526,11 @@ where { } -/// A simple observer with a list of things. -#[derive(Serialize, Deserialize, Debug)] -#[serde(bound = "T: serde::de::DeserializeOwned")] -#[allow(clippy::unsafe_derive_deserialize)] -pub struct ListObserver -where - T: Debug + Serialize, -{ - name: String, - /// The list - list: OwnedMutPtr>, -} - -impl ListObserver -where - T: Debug + Serialize + serde::de::DeserializeOwned, -{ - /// Creates a new [`ListObserver`] with the given name. - /// - /// # Safety - /// Will dereference the list. - /// The list may not move in memory. - #[must_use] - pub unsafe fn new(name: &'static str, list: *mut Vec) -> Self { - Self { - name: name.to_string(), - list: OwnedMutPtr::Ptr(list), - } - } - - /// Get a list ref - #[must_use] - pub fn list(&self) -> &Vec { - self.list.as_ref() - } - - /// Get a list mut - #[must_use] - pub fn list_mut(&mut self) -> &mut Vec { - self.list.as_mut() - } -} - -impl Observer for ListObserver -where - S: UsesInput, - T: Debug + Serialize + serde::de::DeserializeOwned, -{ - fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { - self.list.as_mut().clear(); - Ok(()) - } -} - -impl Named for ListObserver -where - T: Debug + Serialize + serde::de::DeserializeOwned, -{ - fn name(&self) -> &str { - &self.name - } -} - /// `Observer` Python bindings #[cfg(feature = "python")] #[allow(missing_docs)] pub mod pybind { + use alloc::vec::Vec; use core::ptr; use std::cell::UnsafeCell; @@ -603,7 +541,7 @@ pub mod pybind { use pyo3::prelude::*; use serde::{Deserialize, Serialize}; - use super::{Debug, Observer, ObserversTuple, String, Vec}; + use super::{Debug, Observer, ObserversTuple, String}; use crate::{ executors::{pybind::PythonExitKind, ExitKind}, inputs::{BytesInput, HasBytesVec}, diff --git a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs index ed50a571a6..0384c78ebe 100644 --- a/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs +++ b/libafl_libfuzzer/libafl_libfuzzer_runtime/src/report.rs @@ -3,7 +3,7 @@ use std::ffi::c_int; use libafl::{ events::{ProgressReporter, SimpleEventManager}, executors::HasObservers, - feedbacks::{MapFeedbackMetadata, MAPFEEDBACK_PREFIX}, + feedbacks::MapFeedbackMetadata, inputs::UsesInput, monitors::SimpleMonitor, stages::{HasCurrentStage, StagesTuple}, @@ -35,7 +35,7 @@ where ST: StagesTuple, { let meta = state - .named_metadata::>(&(MAPFEEDBACK_PREFIX.to_string() + "edges")) + .named_metadata::>("edges") .unwrap(); let observed = meta.history_map.iter().filter(|&&e| e != 0).count(); let total = meta.history_map.len();