diff --git a/libafl/src/events/llmp/restarting.rs b/libafl/src/events/llmp/restarting.rs index 4feeababcf..361e979ac1 100644 --- a/libafl/src/events/llmp/restarting.rs +++ b/libafl/src/events/llmp/restarting.rs @@ -737,6 +737,13 @@ mod tests { #[serial] #[cfg_attr(miri, ignore)] fn test_mgr_state_restore() { + // # Safety + // The same testcase doesn't usually run twice + #[cfg(any(not(feature = "serdeany_autoreg"), miri))] + unsafe { + crate::stages::RetryCountRestartHelper::register(); + } + let rand = StdRand::with_seed(0); let time = TimeObserver::new("time"); diff --git a/libafl/src/feedbacks/bool.rs b/libafl/src/feedbacks/bool.rs new file mode 100644 index 0000000000..74a6ba577b --- /dev/null +++ b/libafl/src/feedbacks/bool.rs @@ -0,0 +1,154 @@ +//! The [`BoolValueFeedback`] is a [`Feedback`] returning `true` or `false` as the `is_interesting` value. + +use alloc::borrow::Cow; + +use libafl_bolts::{ + tuples::{Handle, MatchNameRef}, + Error, Named, +}; + +use crate::{ + feedbacks::{Feedback, StateInitializer}, + observers::{ObserversTuple, ValueObserver}, + HasNamedMetadata, +}; + +/// This feedback returns `true` or `false` as the `is_interesting` value. +#[derive(Debug)] +pub struct BoolValueFeedback<'a> { + name: Cow<'static, str>, + observer_hnd: Handle>, + #[cfg(feature = "track_hit_feedbacks")] + last_result: Option, +} + +impl<'a> BoolValueFeedback<'a> { + /// Create a new [`BoolValueFeedback`] + #[must_use] + pub fn new(observer_hnd: &Handle>) -> Self { + Self::with_name(observer_hnd.name().clone(), observer_hnd) + } + + /// Create a new [`BoolValueFeedback`] with a given name + #[must_use] + pub fn with_name( + name: Cow<'static, str>, + observer_hnd: &Handle>, + ) -> Self { + Self { + name, + observer_hnd: observer_hnd.clone(), + #[cfg(feature = "track_hit_feedbacks")] + last_result: None, + } + } +} + +impl Named for BoolValueFeedback<'_> { + fn name(&self) -> &Cow<'static, str> { + &self.name + } +} + +impl StateInitializer for BoolValueFeedback<'_> { + fn init_state(&mut self, _state: &mut S) -> Result<(), Error> { + Ok(()) + } +} + +impl Feedback for BoolValueFeedback<'_> +where + OT: ObserversTuple, + S: HasNamedMetadata, +{ + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &I, + observers: &OT, + _exit_kind: &crate::executors::ExitKind, + ) -> Result { + let Some(observer) = observers.get(&self.observer_hnd) else { + return Err(Error::illegal_state(format!( + "Observer {:?} not found", + self.observer_hnd + ))); + }; + + let val = observer.value.as_ref(); + + Ok(*val) + } + + fn append_metadata( + &mut self, + _state: &mut S, + _manager: &mut EM, + _observers: &OT, + _testcase: &mut crate::corpus::Testcase, + ) -> Result<(), Error> { + Ok(()) + } + + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + Ok(()) + } + + #[cfg(feature = "track_hit_feedbacks")] + fn last_result(&self) -> Result { + self.last_result.ok_or_else(|| Error::illegal_state("No last result set in `BoolValuefeedback`. Either `is_interesting` has never been called or the fuzzer restarted in the meantime.")) + } +} + +#[cfg(test)] +mod test { + use core::{cell::UnsafeCell, ptr::write_volatile}; + + use libafl_bolts::{ownedref::OwnedRef, tuples::Handled}; + use tuple_list::tuple_list; + + use crate::{ + executors::ExitKind, + feedbacks::{BoolValueFeedback, Feedback, StateInitializer}, + observers::ValueObserver, + state::NopState, + }; + + #[test] + fn test_bool_value_feedback() { + let value: UnsafeCell = false.into(); + + // # Safety + // The value is only read from in the feedback, not while we change the value. + let value_ptr = unsafe { OwnedRef::from_ptr(value.get()) }; + + let observer = ValueObserver::new("test_value", value_ptr); + + let mut bool_feedback = BoolValueFeedback::new(&observer.handle()); + + let mut state: NopState<()> = NopState::new(); + bool_feedback.init_state(&mut state).unwrap(); + + let observers = tuple_list!(observer); + let mut mgr = (); + let input = (); + let exit_ok = ExitKind::Ok; + + let false_eval = bool_feedback + .is_interesting(&mut state, &mut mgr, &input, &observers, &exit_ok) + .unwrap(); + assert!(!false_eval); + + // # Safety + // The feedback is not keeping a borrow around, only the pointer. + unsafe { + write_volatile(value.get(), true); + } + + let true_eval = bool_feedback + .is_interesting(&mut state, &mut mgr, &input, &observers, &exit_ok) + .unwrap(); + assert!(true_eval); + } +} diff --git a/libafl/src/feedbacks/mod.rs b/libafl/src/feedbacks/mod.rs index c18d08ea49..5fa19965ea 100644 --- a/libafl/src/feedbacks/mod.rs +++ b/libafl/src/feedbacks/mod.rs @@ -31,6 +31,9 @@ use crate::{corpus::Testcase, executors::ExitKind, observers::TimeObserver, Erro #[cfg(feature = "std")] pub mod capture_feedback; +pub mod bool; +pub use bool::BoolValueFeedback; + #[cfg(feature = "std")] pub mod concolic; #[cfg(feature = "std")] diff --git a/libafl/src/feedbacks/new_hash_feedback.rs b/libafl/src/feedbacks/new_hash_feedback.rs index 5fa7a4b825..f79be125fc 100644 --- a/libafl/src/feedbacks/new_hash_feedback.rs +++ b/libafl/src/feedbacks/new_hash_feedback.rs @@ -1,4 +1,4 @@ -//! The ``NewHashFeedback`` uses the backtrace hash and a hashset to only keep novel cases +//! The [`NewHashFeedback`] uses the backtrace hash and a hashset to only keep novel cases use alloc::{borrow::Cow, string::ToString}; use std::fmt::Debug; diff --git a/libafl/src/feedbacks/value_bloom.rs b/libafl/src/feedbacks/value_bloom.rs index eb2dbef55e..031b603dee 100644 --- a/libafl/src/feedbacks/value_bloom.rs +++ b/libafl/src/feedbacks/value_bloom.rs @@ -12,9 +12,9 @@ use libafl_bolts::{ }; use serde::{Deserialize, Serialize}; -use super::{Feedback, StateInitializer}; use crate::{ executors::ExitKind, + feedbacks::{Feedback, StateInitializer}, observers::{ObserversTuple, ValueObserver}, HasNamedMetadata, }; @@ -117,73 +117,66 @@ impl, S: HasNamedMetadata, T: Hash> Feedback &NamedSerdeAnyMap { - &self.map - } - - fn named_metadata_map_mut(&mut self) -> &mut NamedSerdeAnyMap { - &mut self.map - } - } - #[test] fn test_value_bloom_feedback() { - let value_ptr = unsafe { OwnedRef::from_ptr(&raw mut VALUE) }; + let value: UnsafeCell = 0_u32.into(); + + // # Safety + // The same testcase doesn't usually run twice + #[cfg(any(not(feature = "serdeany_autoreg"), miri))] + unsafe { + super::ValueBloomFeedbackMetadata::register(); + } + + // # Safety + // The value is only read from in the feedback, not while we change the value. + let value_ptr = unsafe { OwnedRef::from_ptr(value.get()) }; let observer = ValueObserver::new("test_value", value_ptr); - let mut vbf = ValueBloomFeedback::new(&observer.handle()); - let mut state = NamedMetadataState { - map: NamedSerdeAnyMap::new(), - }; - vbf.init_state(&mut state).unwrap(); - let observers = tuple_list!(observer); - let mut mgr = NopEventManager::::new(); - let input = NopInput {}; + + let mut state: NopState<()> = NopState::new(); + let mut mgr = (); + let input = (); let exit_ok = ExitKind::Ok; + vbf.init_state(&mut state).unwrap(); + let first_eval = vbf .is_interesting(&mut state, &mut mgr, &input, &observers, &exit_ok) .unwrap(); - assert_eq!(first_eval, true); + assert!(first_eval); let second_eval = vbf .is_interesting(&mut state, &mut mgr, &input, &observers, &exit_ok) .unwrap(); - assert_ne!(first_eval, second_eval); + assert!(!second_eval); + // # Safety + // The feedback is not keeping a borrow around, only the pointer. unsafe { - write_volatile(&raw mut VALUE, 1234_u32); + write_volatile(value.get(), 1234_u32); } let next_eval = vbf .is_interesting(&mut state, &mut mgr, &input, &observers, &exit_ok) .unwrap(); - assert_eq!(next_eval, true); + assert!(next_eval); } } diff --git a/libafl/src/inputs/mod.rs b/libafl/src/inputs/mod.rs index 055e2fb407..91aac35652 100644 --- a/libafl/src/inputs/mod.rs +++ b/libafl/src/inputs/mod.rs @@ -116,13 +116,23 @@ macro_rules! none_input_converter { } /// An input for tests, mainly. There is no real use much else. -#[derive(Copy, Clone, Serialize, Deserialize, Debug, Hash)] +#[derive(Copy, Clone, Serialize, Deserialize, Debug, Default, Hash)] pub struct NopInput {} + +impl NopInput { + /// Creates a new [`NopInput`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} + impl Input for NopInput { fn generate_name(&self, _id: Option) -> String { "nop-input".to_string() } } + impl HasTargetBytes for NopInput { fn target_bytes(&self) -> OwnedSlice { OwnedSlice::from(vec![0]) diff --git a/libafl/src/stages/mod.rs b/libafl/src/stages/mod.rs index da01ae7886..d0626abe8c 100644 --- a/libafl/src/stages/mod.rs +++ b/libafl/src/stages/mod.rs @@ -612,13 +612,6 @@ mod test { /// Test to test retries in stages #[test] fn test_tries_progress() -> Result<(), Error> { - // # Safety - // No concurrency per testcase - #[cfg(any(not(feature = "serdeany_autoreg"), miri))] - unsafe { - RetryCountRestartHelper::register(); - } - struct StageWithOneTry; impl Named for StageWithOneTry { @@ -628,6 +621,13 @@ mod test { } } + // # Safety + // No concurrency per testcase + #[cfg(any(not(feature = "serdeany_autoreg"), miri))] + unsafe { + RetryCountRestartHelper::register(); + } + let mut state = StdState::nop()?; let stage = StageWithOneTry; diff --git a/libafl/src/state/mod.rs b/libafl/src/state/mod.rs index 86477a6aa1..35ffedba68 100644 --- a/libafl/src/state/mod.rs +++ b/libafl/src/state/mod.rs @@ -1242,6 +1242,7 @@ impl HasScalabilityMonitor for StdState { #[derive(Debug, Serialize, Deserialize, Default)] pub struct NopState { metadata: SerdeAnyMap, + named_metadata: NamedSerdeAnyMap, execution: u64, stop_requested: bool, rand: StdRand, @@ -1254,6 +1255,7 @@ impl NopState { pub fn new() -> Self { NopState { metadata: SerdeAnyMap::new(), + named_metadata: NamedSerdeAnyMap::new(), execution: 0, rand: StdRand::default(), stop_requested: false, @@ -1323,6 +1325,16 @@ impl HasMetadata for NopState { } } +impl HasNamedMetadata for NopState { + fn named_metadata_map(&self) -> &NamedSerdeAnyMap { + &self.named_metadata + } + + fn named_metadata_map_mut(&mut self) -> &mut NamedSerdeAnyMap { + &mut self.named_metadata + } +} + impl HasRand for NopState { type Rand = StdRand;