Introduce feedbacks hit tracking for testcases (#2248)

* introduce feedbacks hit tracking for testcases

* make Testcase::hit_feedbacks into Cow<&str> instead of String
rename get_hit_feedbacks to append_hit_feedbacks
update documentation

* simplify ConstFeedback

* rename Feedback::last_result to prev_result

* impl TODO prev_result for NewHashFeedback, ListFeedback, TransferredFeedback, NautilusFeedback

* rename prev_result to last_result

* add docs

* introduce Objectives hit tracking

* update docs

* update Cargo.toml docs

* update docs

* track Feedbacks & Objectives hit in Fuzzer::add_input

* fmt

* clippy

* fix type error in OomFeedback::last_result

* impl last_result for AsanErrorsFeedback

* add track_hit_feedbacks as a feature to libafl_libfuzzer_runtime

* fix clippy

* change return type of Feedback::last_result to a Result

* remove expect in NewHashFeedback::is_interesting

* move Error::premature_last_result to libafl from libafl_bolts
This commit is contained in:
Aarnav 2024-05-28 02:43:05 -07:00 committed by GitHub
parent e4446b908c
commit bce0f08294
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
22 changed files with 443 additions and 46 deletions

View File

@ -26,6 +26,9 @@ document-features = ["dep:document-features"]
## Enables features that need rust's `std` lib to work, like print, env, ... support
std = ["serde_json", "serde_json/std", "nix", "serde/std", "bincode", "wait-timeout", "uuid", "backtrace", "serial_test", "libafl_bolts/std", "typed-builder"]
## Tracks the Feedbacks and the Objectives that were interesting for a Testcase
track_hit_feedbacks = ["std"]
## Collects performance statistics of the fuzzing pipeline and displays it on `Monitor` components
introspection = []

View File

@ -2,6 +2,8 @@
//! It will contain a respective input, and metadata.
use alloc::string::String;
#[cfg(feature = "track_hit_feedbacks")]
use alloc::{borrow::Cow, vec::Vec};
use core::{
cell::{Ref, RefMut},
time::Duration,
@ -67,6 +69,12 @@ where
disabled: bool,
/// has found crash (or timeout) or not
objectives_found: usize,
/// Vector of `Feedback` names that deemed this `Testcase` as corpus worthy
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec<Cow<'static, str>>,
/// Vector of `Feedback` names that deemed this `Testcase` as solution worthy
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec<Cow<'static, str>>,
}
impl<I> HasMetadata for Testcase<I>
@ -211,6 +219,34 @@ where
self.disabled = disabled;
}
/// Get the hit feedbacks
#[inline]
#[cfg(feature = "track_hit_feedbacks")]
pub fn hit_feedbacks(&self) -> &Vec<Cow<'static, str>> {
&self.hit_feedbacks
}
/// Get the hit feedbacks (mutable)
#[inline]
#[cfg(feature = "track_hit_feedbacks")]
pub fn hit_feedbacks_mut(&mut self) -> &mut Vec<Cow<'static, str>> {
&mut self.hit_feedbacks
}
/// Get the hit objectives
#[inline]
#[cfg(feature = "track_hit_feedbacks")]
pub fn hit_objectives(&self) -> &Vec<Cow<'static, str>> {
&self.hit_objectives
}
/// Get the hit objectives (mutable)
#[inline]
#[cfg(feature = "track_hit_feedbacks")]
pub fn hit_objectives_mut(&mut self) -> &mut Vec<Cow<'static, str>> {
&mut self.hit_objectives
}
/// Create a new Testcase instance given an input
#[inline]
pub fn new(mut input: I) -> Self {
@ -230,6 +266,10 @@ where
parent_id: None,
disabled: false,
objectives_found: 0,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec::new(),
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec::new(),
}
}
@ -252,6 +292,10 @@ where
parent_id: Some(parent_id),
disabled: false,
objectives_found: 0,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec::new(),
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec::new(),
}
}
@ -274,6 +318,10 @@ where
parent_id: None,
disabled: false,
objectives_found: 0,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec::new(),
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec::new(),
}
}
@ -296,6 +344,10 @@ where
parent_id: None,
disabled: false,
objectives_found: 0,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec::new(),
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec::new(),
}
}
@ -348,6 +400,10 @@ where
metadata_path: None,
disabled: false,
objectives_found: 0,
#[cfg(feature = "track_hit_feedbacks")]
hit_feedbacks: Vec::new(),
#[cfg(feature = "track_hit_feedbacks")]
hit_objectives: Vec::new(),
}
}
}

View File

@ -97,4 +97,9 @@ where
) -> Result<(), Error> {
Ok(())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(false)
}
}

View File

@ -13,6 +13,8 @@ use libafl_bolts::{
};
use serde::{Deserialize, Serialize};
#[cfg(feature = "track_hit_feedbacks")]
use crate::feedbacks::premature_last_result_err;
use crate::{
events::EventFirer,
executors::ExitKind,
@ -61,6 +63,9 @@ where
o1_ref: Handle<O1>,
/// The second observer to compare against
o2_ref: Handle<O2>,
// The previous run's result of `Self::is_interesting`
#[cfg(feature = "track_hit_feedbacks")]
last_result: Option<bool>,
/// The function used to compare the two observers
compare_fn: F,
phantomm: PhantomData<(I, S)>,
@ -86,6 +91,8 @@ where
o1_ref,
o2_ref,
name: Cow::from(name),
#[cfg(feature = "track_hit_feedbacks")]
last_result: None,
compare_fn,
phantomm: PhantomData,
})
@ -108,6 +115,8 @@ where
o1_ref: self.o1_ref.clone(),
o2_ref: self.o2_ref.clone(),
compare_fn: self.compare_fn.clone(),
#[cfg(feature = "track_hit_feedbacks")]
last_result: None,
phantomm: self.phantomm,
}
}
@ -169,8 +178,17 @@ where
let o2: &O2 = observers
.get(&self.o2_ref)
.ok_or_else(|| err(self.o2_ref.name()))?;
let res = (self.compare_fn)(o1, o2) == DiffResult::Diff;
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(res);
}
Ok(res)
}
Ok((self.compare_fn)(o1, o2) == DiffResult::Diff)
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
self.last_result.ok_or(premature_last_result_err())
}
}

View File

@ -143,6 +143,10 @@ where
}
Ok(())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(!self.novelty.is_empty())
}
}
impl<T> Named for ListFeedback<T>

View File

@ -18,6 +18,8 @@ use libafl_bolts::{
use num_traits::PrimInt;
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(feature = "track_hit_feedbacks")]
use crate::feedbacks::premature_last_result_err;
use crate::{
corpus::Testcase,
events::{Event, EventFirer},
@ -391,6 +393,9 @@ pub struct MapFeedback<C, N, O, R, T> {
map_ref: Handle<C>,
/// Name of the feedback as shown in the `UserStats`
stats_name: Cow<'static, str>,
// The previous run's result of [`Self::is_interesting`]
#[cfg(feature = "track_hit_feedbacks")]
last_result: Option<bool>,
/// Phantom Data of Reducer
phantom: PhantomData<(C, N, O, R, T)>,
}
@ -424,7 +429,12 @@ where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
Ok(self.is_interesting_default(state, manager, input, observers, exit_kind))
let res = self.is_interesting_default(state, manager, input, observers, exit_kind);
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(res);
}
Ok(res)
}
#[rustversion::not(nightly)]
@ -440,7 +450,13 @@ where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
Ok(self.is_interesting_default(state, manager, input, observers, exit_kind))
let res = self.is_interesting_default(state, manager, input, observers, exit_kind);
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(res);
}
Ok(res)
}
fn append_metadata<EM, OT>(
@ -533,6 +549,11 @@ where
Ok(())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
self.last_result.ok_or(premature_last_result_err())
}
}
/// Specialize for the common coverage map size, maximization of u8s
@ -648,7 +669,10 @@ where
}
}
}
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(interesting);
}
Ok(interesting)
}
}
@ -699,6 +723,8 @@ where
name: map_observer.name().clone(),
map_ref: map_observer.handle(),
stats_name: create_stats_name(map_observer.name()),
#[cfg(feature = "track_hit_feedbacks")]
last_result: None,
phantom: PhantomData,
}
}
@ -714,6 +740,8 @@ where
map_ref: map_observer.handle(),
stats_name: create_stats_name(&name),
name,
#[cfg(feature = "track_hit_feedbacks")]
last_result: None,
phantom: PhantomData,
}
}

View File

@ -5,6 +5,8 @@
// TODO: make S of Feedback<S> an associated type when specialisation + AT is stable
use alloc::borrow::Cow;
#[cfg(feature = "track_hit_feedbacks")]
use alloc::vec::Vec;
use core::{
fmt::{self, Debug, Formatter},
marker::PhantomData,
@ -35,12 +37,10 @@ use crate::{
state::State,
Error,
};
pub mod map;
#[cfg(feature = "std")]
pub mod concolic;
pub mod differential;
pub mod map;
#[cfg(feature = "nautilus")]
pub mod nautilus;
#[cfg(feature = "std")]
@ -113,6 +113,21 @@ where
ret
}
/// CUT MY LIFE INTO PIECES; THIS IS MY LAST [`Feedback::is_interesting`] run
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error>;
/// Append this [`Feedback`]'s name if [`Feedback::last_result`] is true
/// If you have any nested Feedbacks, you must call this function on them if relevant.
/// See the implementations of [`CombinedFeedback`]
#[cfg(feature = "track_hit_feedbacks")]
fn append_hit_feedbacks(&self, list: &mut Vec<Cow<'static, str>>) -> Result<(), Error> {
if self.last_result()? {
list.push(self.name().clone());
}
Ok(())
}
/// Append to the testcase the generated metadata in case of a new corpus item
#[inline]
#[allow(unused_variables)]
@ -211,7 +226,14 @@ where
self.second.init_state(state)?;
Ok(())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
FL::last_result(&self.first, &self.second)
}
#[cfg(feature = "track_hit_feedbacks")]
fn append_hit_feedbacks(&self, list: &mut Vec<Cow<'static, str>>) -> Result<(), Error> {
FL::append_hit_feedbacks(&self.first, &self.second, list)
}
#[allow(clippy::wrong_self_convention)]
fn is_interesting<EM, OT>(
&mut self,
@ -326,6 +348,20 @@ where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>;
/// Get the result of the last `Self::is_interesting` run
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(first: &A, second: &B) -> Result<bool, Error>;
/// Append this [`Feedback`]'s name if [`Feedback::last_result`] is true
/// If you have any nested Feedbacks, you must call this function on them if relevant.
/// See the implementations of [`CombinedFeedback`]
#[cfg(feature = "track_hit_feedbacks")]
fn append_hit_feedbacks(
first: &A,
second: &B,
list: &mut Vec<Cow<'static, str>>,
) -> Result<(), Error>;
/// If this pair is interesting (with introspection features enabled)
#[cfg(feature = "introspection")]
#[allow(clippy::too_many_arguments)]
@ -407,6 +443,27 @@ where
let b = second.is_interesting(state, manager, input, observers, exit_kind)?;
Ok(a || b)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(first: &A, second: &B) -> Result<bool, Error> {
Ok(first.last_result()? || second.last_result()?)
}
/// Note: Eager OR's hit feedbacks will behave like Fast OR
/// because the second feedback will not have contributed to the result.
/// Set the second feedback as the first (A, B) vs (B, A)
/// to "prioritize" the result in case of Eager OR.
#[cfg(feature = "track_hit_feedbacks")]
fn append_hit_feedbacks(
first: &A,
second: &B,
list: &mut Vec<Cow<'static, str>>,
) -> Result<(), Error> {
if first.last_result()? {
first.append_hit_feedbacks(list)?;
} else if second.last_result()? {
second.append_hit_feedbacks(list)?;
}
Ok(())
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_introspection<EM, OT>(
@ -460,6 +517,28 @@ where
second.is_interesting(state, manager, input, observers, exit_kind)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(first: &A, second: &B) -> Result<bool, Error> {
if first.last_result()? {
return Ok(true);
}
// The second must have run if the first wasn't interesting
second.last_result()
}
#[cfg(feature = "track_hit_feedbacks")]
fn append_hit_feedbacks(
first: &A,
second: &B,
list: &mut Vec<Cow<'static, str>>,
) -> Result<(), Error> {
if first.last_result()? {
first.append_hit_feedbacks(list)?;
} else if second.last_result()? {
second.append_hit_feedbacks(list)?;
}
Ok(())
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_introspection<EM, OT>(
@ -514,6 +593,23 @@ where
Ok(a && b)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(first: &A, second: &B) -> Result<bool, Error> {
Ok(first.last_result()? && second.last_result()?)
}
#[cfg(feature = "track_hit_feedbacks")]
fn append_hit_feedbacks(
first: &A,
second: &B,
list: &mut Vec<Cow<'static, str>>,
) -> Result<(), Error> {
if first.last_result()? && second.last_result()? {
first.append_hit_feedbacks(list)?;
second.append_hit_feedbacks(list)?;
}
Ok(())
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_introspection<EM, OT>(
first: &mut A,
@ -567,6 +663,30 @@ where
second.is_interesting(state, manager, input, observers, exit_kind)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(first: &A, second: &B) -> Result<bool, Error> {
if !first.last_result()? {
return Ok(false);
}
// The second must have run if the first wasn't interesting
second.last_result()
}
#[cfg(feature = "track_hit_feedbacks")]
fn append_hit_feedbacks(
first: &A,
second: &B,
list: &mut Vec<Cow<'static, str>>,
) -> Result<(), Error> {
if first.last_result()? {
first.append_hit_feedbacks(list)?;
} else if second.last_result()? {
second.append_hit_feedbacks(list)?;
}
Ok(())
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_introspection<EM, OT>(
first: &mut A,
@ -684,6 +804,11 @@ where
fn discard_metadata(&mut self, state: &mut S, input: &S::Input) -> Result<(), Error> {
self.first.discard_metadata(state, input)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(!self.first.last_result()?)
}
}
impl<A, S> Named for NotFeedback<A, S>
@ -785,11 +910,19 @@ where
{
Ok(false)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(false)
}
}
/// A [`CrashFeedback`] reports as interesting if the target crashed.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CrashFeedback;
pub struct CrashFeedback {
#[cfg(feature = "track_hit_feedbacks")]
// The previous run's result of `Self::is_interesting`
last_result: Option<bool>,
}
impl<S> Feedback<S> for CrashFeedback
where
@ -808,11 +941,17 @@ where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
if let ExitKind::Crash = exit_kind {
Ok(true)
} else {
Ok(false)
let res = matches!(exit_kind, ExitKind::Crash);
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(res);
}
Ok(res)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
self.last_result.ok_or(premature_last_result_err())
}
}
@ -828,7 +967,10 @@ impl CrashFeedback {
/// Creates a new [`CrashFeedback`]
#[must_use]
pub fn new() -> Self {
Self
Self {
#[cfg(feature = "track_hit_feedbacks")]
last_result: None,
}
}
}
@ -846,7 +988,11 @@ impl<S: State, T> FeedbackFactory<CrashFeedback, S, T> for CrashFeedback {
/// A [`TimeoutFeedback`] reduces the timeout value of a run.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeoutFeedback;
pub struct TimeoutFeedback {
#[cfg(feature = "track_hit_feedbacks")]
// The previous run's result of `Self::is_interesting`
last_result: Option<bool>,
}
impl<S> Feedback<S> for TimeoutFeedback
where
@ -865,11 +1011,17 @@ where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
if let ExitKind::Timeout = exit_kind {
Ok(true)
} else {
Ok(false)
let res = matches!(exit_kind, ExitKind::Timeout);
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(res);
}
Ok(res)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
self.last_result.ok_or(premature_last_result_err())
}
}
@ -885,7 +1037,10 @@ impl TimeoutFeedback {
/// Returns a new [`TimeoutFeedback`].
#[must_use]
pub fn new() -> Self {
Self
Self {
#[cfg(feature = "track_hit_feedbacks")]
last_result: None,
}
}
}
@ -904,7 +1059,11 @@ impl<S: State, T> FeedbackFactory<TimeoutFeedback, S, T> for TimeoutFeedback {
/// A [`DiffExitKindFeedback`] checks if there is a difference in the [`crate::executors::ExitKind`]s in a [`crate::executors::DiffExecutor`].
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct DiffExitKindFeedback;
pub struct DiffExitKindFeedback {
#[cfg(feature = "track_hit_feedbacks")]
// The previous run's result of `Self::is_interesting`
last_result: Option<bool>,
}
impl<S> Feedback<S> for DiffExitKindFeedback
where
@ -923,7 +1082,16 @@ where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
Ok(matches!(exit_kind, ExitKind::Diff { .. }))
let res = matches!(exit_kind, ExitKind::Diff { .. });
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(res);
}
Ok(res)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
self.last_result.ok_or(premature_last_result_err())
}
}
@ -939,7 +1107,10 @@ impl DiffExitKindFeedback {
/// Returns a new [`DiffExitKindFeedback`].
#[must_use]
pub fn new() -> Self {
Self
Self {
#[cfg(feature = "track_hit_feedbacks")]
last_result: None,
}
}
}
@ -1008,6 +1179,11 @@ where
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(false)
}
}
impl Named for TimeFeedback {
@ -1055,10 +1231,12 @@ where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
Ok(match self {
ConstFeedback::True => true,
ConstFeedback::False => false,
})
Ok((*self).into())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok((*self).into())
}
}
@ -1087,3 +1265,18 @@ impl From<bool> for ConstFeedback {
}
}
}
impl From<ConstFeedback> for bool {
fn from(value: ConstFeedback) -> Self {
match value {
ConstFeedback::True => true,
ConstFeedback::False => false,
}
}
}
#[cfg(feature = "track_hit_feedbacks")]
/// Error if [`Feedback::last_result`] is called before the `Feedback` is actually run.
pub(crate) fn premature_last_result_err() -> Error {
Error::illegal_state("last_result called before Feedback was run")
}

View File

@ -123,4 +123,8 @@ where
fn discard_metadata(&mut self, _state: &mut S, _input: &NautilusInput) -> Result<(), Error> {
Ok(())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(false)
}
}

View File

@ -10,6 +10,8 @@ use libafl_bolts::{
};
use serde::{Deserialize, Serialize};
#[cfg(feature = "track_hit_feedbacks")]
use crate::feedbacks::premature_last_result_err;
use crate::{
events::EventFirer,
executors::ExitKind,
@ -85,6 +87,9 @@ pub struct NewHashFeedback<O, S> {
o_ref: Handle<O>,
/// Initial capacity of hash set
capacity: usize,
#[cfg(feature = "track_hit_feedbacks")]
// The previous run's result of `Self::is_interesting`
last_result: Option<bool>,
phantom: PhantomData<S>,
}
@ -123,18 +128,22 @@ where
.get_mut::<NewHashFeedbackMetadata>(&self.name)
.unwrap();
match observer.hash() {
Some(hash) => {
let res = backtrace_state
.update_hash_set(hash)
.expect("Failed to update the hash state");
Ok(res)
}
let res = match observer.hash() {
Some(hash) => backtrace_state.update_hash_set(hash)?,
None => {
// We get here if the hash was not updated, i.e the first run or if no crash happens
Ok(false)
false
}
};
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(res);
}
Ok(res)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
self.last_result.ok_or(premature_last_result_err())
}
}
@ -178,6 +187,8 @@ where
name: Cow::from(NEWHASHFEEDBACK_PREFIX.to_string() + observer.name()),
o_ref: observer.handle(),
capacity,
#[cfg(feature = "track_hit_feedbacks")]
last_result: None,
phantom: PhantomData,
}
}

View File

@ -90,6 +90,11 @@ where
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(false)
}
}
impl Named for StdOutToMetadataFeedback {
@ -180,6 +185,10 @@ where
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
Ok(())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(false)
}
}
impl Named for StdErrToMetadataFeedback {

View File

@ -6,11 +6,12 @@ use alloc::borrow::Cow;
use libafl_bolts::{impl_serdeany, Error, Named};
use serde::{Deserialize, Serialize};
#[cfg(feature = "track_hit_feedbacks")]
use crate::feedbacks::premature_last_result_err;
use crate::{
events::EventFirer, executors::ExitKind, feedbacks::Feedback, observers::ObserversTuple,
state::State, HasMetadata,
};
/// Constant name of the [`TransferringMetadata`].
pub const TRANSFERRED_FEEDBACK_NAME: Cow<'static, str> =
Cow::Borrowed("transferred_feedback_internal");
@ -37,7 +38,11 @@ impl TransferringMetadata {
/// Simple feedback which may be used to test whether the testcase was transferred from another node
/// in a multi-node fuzzing arrangement.
#[derive(Copy, Clone, Debug)]
pub struct TransferredFeedback;
pub struct TransferredFeedback {
#[cfg(feature = "track_hit_feedbacks")]
// The previous run's result of `Self::is_interesting`
last_result: Option<bool>,
}
impl Named for TransferredFeedback {
fn name(&self) -> &Cow<'static, str> {
@ -66,6 +71,15 @@ where
EM: EventFirer<State = S>,
OT: ObserversTuple<S>,
{
Ok(state.metadata::<TransferringMetadata>()?.transferring)
let res = state.metadata::<TransferringMetadata>()?.transferring;
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(res);
}
Ok(res)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
self.last_result.ok_or(premature_last_result_err())
}
}

View File

@ -465,6 +465,9 @@ where
// Add the input to the main corpus
let mut testcase = Testcase::with_executions(input.clone(), *state.executions());
#[cfg(feature = "track_hit_feedbacks")]
self.feedback_mut()
.append_hit_feedbacks(testcase.hit_feedbacks_mut())?;
self.feedback_mut()
.append_metadata(state, manager, observers, &mut testcase)?;
let idx = state.corpus_mut().add(testcase)?;
@ -507,6 +510,9 @@ where
if let Ok(mut tc) = state.current_testcase_mut() {
tc.found_objective();
}
#[cfg(feature = "track_hit_feedbacks")]
self.objective_mut()
.append_hit_feedbacks(testcase.hit_objectives_mut())?;
self.objective_mut()
.append_metadata(state, manager, observers, &mut testcase)?;
state.solutions_mut().add(testcase)?;
@ -621,6 +627,9 @@ where
)?;
if is_solution {
#[cfg(feature = "track_hit_feedbacks")]
self.objective_mut()
.append_hit_feedbacks(testcase.hit_objectives_mut())?;
self.objective_mut()
.append_metadata(state, manager, &*observers, &mut testcase)?;
let idx = state.solutions_mut().add(testcase)?;
@ -656,6 +665,9 @@ where
&exit_kind,
)?;
#[cfg(feature = "track_hit_feedbacks")]
self.feedback_mut()
.append_hit_feedbacks(testcase.hit_feedbacks_mut())?;
// Add the input to the main corpus
self.feedback_mut()
.append_metadata(state, manager, &*observers, &mut testcase)?;

View File

@ -9,6 +9,8 @@ use libafl_bolts::{
HasLen, Named,
};
#[cfg(feature = "track_hit_feedbacks")]
use crate::feedbacks::premature_last_result_err;
use crate::{
corpus::{Corpus, HasCurrentCorpusId, Testcase},
events::EventFirer,
@ -31,7 +33,6 @@ use crate::{
};
#[cfg(feature = "introspection")]
use crate::{monitors::PerfFeature, state::HasClientPerfMonitor};
/// Mutational stage which minimizes corpus entries.
///
/// You must provide at least one mutator that actually reduces size.
@ -355,6 +356,9 @@ pub struct MapEqualityFeedback<C, M, S> {
name: Cow<'static, str>,
map_ref: Handle<C>,
orig_hash: u64,
#[cfg(feature = "track_hit_feedbacks")]
// The previous run's result of `Self::is_interesting`
last_result: Option<bool>,
phantom: PhantomData<(M, S)>,
}
@ -393,7 +397,16 @@ where
let obs = observers
.get(self.observer_handle())
.expect("Should have been provided valid observer name.");
Ok(obs.as_ref().hash_simple() == self.orig_hash)
let res = obs.as_ref().hash_simple() == self.orig_hash;
#[cfg(feature = "track_hit_feedbacks")]
{
self.last_result = Some(res);
}
Ok(res)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
self.last_result.ok_or(premature_last_result_err())
}
}
@ -442,6 +455,8 @@ where
name: Cow::from("MapEq"),
map_ref: obs.handle(),
orig_hash: obs.as_ref().hash_simple(),
#[cfg(feature = "track_hit_feedbacks")]
last_result: None,
phantom: PhantomData,
}
}

View File

@ -24,7 +24,7 @@ all-features = true
default = ["serdeany_autoreg"]
cmplog = ["iced-x86"]
serdeany_autoreg = ["libafl_bolts/serdeany_autoreg"]
track_hit_feedbacks = ["libafl/track_hit_feedbacks"]
[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }

View File

@ -701,6 +701,11 @@ where
self.errors = None;
Ok(())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(self.errors.is_some())
}
}
impl<S> Named for AsanErrorsFeedback<S> {

View File

@ -10,6 +10,7 @@ publish = false
default = ["fork"]
## Enables forking mode for the LibAFL launcher (instead of starting new processes)
fork = ["libafl/fork"]
track_hit_feedbacks = ["libafl/track_hit_feedbacks", "libafl_targets/track_hit_feedbacks"]
[profile.release]
lto = true
@ -47,6 +48,7 @@ utf8-chars = "3.0.1"
env_logger = "0.10"
[build-dependencies]
bindgen = "0.69.4"
cc = { version = "1.0", features = ["parallel"] }

View File

@ -61,6 +61,11 @@ where
{
Ok(*self.keep.borrow())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(*self.keep.borrow())
}
}
#[derive(Deserialize, Serialize, Debug)]
@ -133,6 +138,10 @@ where
self.exit_kind = *exit_kind;
Ok(false)
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(false)
}
fn append_metadata<EM, OT>(
&mut self,

View File

@ -93,7 +93,7 @@ fn minimize_crash_with_mutator<M: Mutator<BytesInput, TMinState>>(
fuzzer.fuzz_one(&mut stages, &mut executor, &mut state, &mut mgr)?;
}
ExitKind::Timeout => {
let factory = TimeoutFeedback;
let factory = TimeoutFeedback::new();
let tmin = StdTMinMutationalStage::new(
mutator,
factory,

View File

@ -7,7 +7,7 @@ use std::{
use libafl::{
executors::{Executor, ExitKind, HasObservers},
inputs::HasTargetBytes,
observers::{ObserversTuple, StdErrObserver, StdOutObserver, UsesObservers},
observers::{ObserversTuple, StdOutObserver, UsesObservers},
state::{HasExecutions, State, UsesState},
Error,
};
@ -153,6 +153,12 @@ pub struct NyxExecutorBuilder {
// stderr: Option<StdErrObserver>,
}
impl Default for NyxExecutorBuilder {
fn default() -> Self {
Self::new()
}
}
impl NyxExecutorBuilder {
pub fn new() -> Self {
Self {

View File

@ -41,8 +41,6 @@ use crate::{
#[cfg(emulation_mode = "usermode")]
mod usermode;
#[cfg(emulation_mode = "usermode")]
pub use usermode::*;
#[cfg(emulation_mode = "systemmode")]
mod systemmode;

View File

@ -59,7 +59,7 @@ windows_asan = ["common"] # Compile C code for ASAN on Windows
whole_archive = [] # use +whole-archive to ensure the presence of weak symbols
cmplog_extended_instrumentation = [] # support for aflpp cmplog map, we will remove this once aflpp and libafl cmplog shares the same LLVM passes.
function-logging = ["common"]
track_hit_feedbacks = ["libafl/track_hit_feedbacks"]
[build-dependencies]
bindgen = "0.69.4"
cc = { version = "1.0", features = ["parallel"] }

View File

@ -167,4 +167,9 @@ where
{
Ok(Self::oomed())
}
#[cfg(feature = "track_hit_feedbacks")]
fn last_result(&self) -> Result<bool, Error> {
Ok(Self::oomed())
}
}