Add BoolValueFeedback (#2815)
* Add BoolValueFeedback * No_std * clippy * Fix tests * More clip * fix no_std tests
This commit is contained in:
parent
4b4a22bc44
commit
742773bc17
@ -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");
|
||||
|
154
libafl/src/feedbacks/bool.rs
Normal file
154
libafl/src/feedbacks/bool.rs
Normal file
@ -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<ValueObserver<'a, bool>>,
|
||||
#[cfg(feature = "track_hit_feedbacks")]
|
||||
last_result: Option<bool>,
|
||||
}
|
||||
|
||||
impl<'a> BoolValueFeedback<'a> {
|
||||
/// Create a new [`BoolValueFeedback`]
|
||||
#[must_use]
|
||||
pub fn new(observer_hnd: &Handle<ValueObserver<'a, bool>>) -> 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<ValueObserver<'a, bool>>,
|
||||
) -> 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<S> StateInitializer<S> for BoolValueFeedback<'_> {
|
||||
fn init_state(&mut self, _state: &mut S) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for BoolValueFeedback<'_>
|
||||
where
|
||||
OT: ObserversTuple<I, S>,
|
||||
S: HasNamedMetadata,
|
||||
{
|
||||
fn is_interesting(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &I,
|
||||
observers: &OT,
|
||||
_exit_kind: &crate::executors::ExitKind,
|
||||
) -> Result<bool, Error> {
|
||||
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<I>,
|
||||
) -> 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<bool, Error> {
|
||||
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<bool> = 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);
|
||||
}
|
||||
}
|
@ -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")]
|
||||
|
@ -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;
|
||||
|
@ -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<EM, I, OT: ObserversTuple<I, S>, S: HasNamedMetadata, T: Hash> Feedback<EM,
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use core::ptr::write_volatile;
|
||||
use core::{cell::UnsafeCell, ptr::write_volatile};
|
||||
|
||||
use libafl_bolts::{ownedref::OwnedRef, serdeany::NamedSerdeAnyMap, tuples::Handled};
|
||||
use libafl_bolts::{ownedref::OwnedRef, tuples::Handled};
|
||||
use tuple_list::tuple_list;
|
||||
|
||||
use super::ValueBloomFeedback;
|
||||
use crate::{
|
||||
events::NopEventManager,
|
||||
executors::ExitKind,
|
||||
feedbacks::{Feedback, StateInitializer},
|
||||
inputs::NopInput,
|
||||
observers::ValueObserver,
|
||||
HasNamedMetadata,
|
||||
state::NopState,
|
||||
};
|
||||
|
||||
static mut VALUE: u32 = 0;
|
||||
|
||||
struct NamedMetadataState {
|
||||
map: NamedSerdeAnyMap,
|
||||
}
|
||||
|
||||
impl HasNamedMetadata for NamedMetadataState {
|
||||
fn named_metadata_map(&self) -> &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<u32> = 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::<NamedMetadataState>::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);
|
||||
}
|
||||
}
|
||||
|
@ -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<CorpusId>) -> String {
|
||||
"nop-input".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasTargetBytes for NopInput {
|
||||
fn target_bytes(&self) -> OwnedSlice<u8> {
|
||||
OwnedSlice::from(vec![0])
|
||||
|
@ -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;
|
||||
|
||||
|
@ -1242,6 +1242,7 @@ impl<I, C, R, SC> HasScalabilityMonitor for StdState<I, C, R, SC> {
|
||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||
pub struct NopState<I> {
|
||||
metadata: SerdeAnyMap,
|
||||
named_metadata: NamedSerdeAnyMap,
|
||||
execution: u64,
|
||||
stop_requested: bool,
|
||||
rand: StdRand,
|
||||
@ -1254,6 +1255,7 @@ impl<I> NopState<I> {
|
||||
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<I> HasMetadata for NopState<I> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> HasNamedMetadata for NopState<I> {
|
||||
fn named_metadata_map(&self) -> &NamedSerdeAnyMap {
|
||||
&self.named_metadata
|
||||
}
|
||||
|
||||
fn named_metadata_map_mut(&mut self) -> &mut NamedSerdeAnyMap {
|
||||
&mut self.named_metadata
|
||||
}
|
||||
}
|
||||
|
||||
impl<I> HasRand for NopState<I> {
|
||||
type Rand = StdRand;
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user