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:
parent
e4446b908c
commit
bce0f08294
@ -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 = []
|
||||
|
||||
|
@ -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(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -97,4 +97,9 @@ where
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "track_hit_feedbacks")]
|
||||
fn last_result(&self) -> Result<bool, Error> {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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())
|
||||
}
|
||||
}
|
||||
|
@ -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)?;
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
@ -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"] }
|
||||
|
||||
|
@ -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> {
|
||||
|
@ -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"] }
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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"] }
|
||||
|
@ -167,4 +167,9 @@ where
|
||||
{
|
||||
Ok(Self::oomed())
|
||||
}
|
||||
|
||||
#[cfg(feature = "track_hit_feedbacks")]
|
||||
fn last_result(&self) -> Result<bool, Error> {
|
||||
Ok(Self::oomed())
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user