pub mod map;
pub use map::*;
#[cfg(feature = "std")]
pub mod concolic;
#[cfg(feature = "std")]
pub use concolic::ConcolicFeedback;
use alloc::string::{String, ToString};
use serde::{Deserialize, Serialize};
use crate::{
bolts::tuples::{MatchName, Named},
corpus::Testcase,
events::EventFirer,
executors::ExitKind,
inputs::Input,
observers::{ObserversTuple, TimeObserver},
state::HasClientPerfStats,
Error,
};
use core::{marker::PhantomData, time::Duration};
pub trait Feedback<I, S>: Named
where
I: Input,
S: HasClientPerfStats,
{
fn is_interesting<EM, OT>(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>;
#[cfg(feature = "introspection")]
#[allow(clippy::too_many_arguments)]
fn is_interesting_introspection<EM, OT>(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let start_time = crate::bolts::cpu::read_time_counter();
let ret = self.is_interesting(state, manager, input, observers, exit_kind);
let elapsed = crate::bolts::cpu::read_time_counter() - start_time;
state
.introspection_stats_mut()
.update_feedback(self.name(), elapsed);
ret
}
#[inline]
fn append_metadata(
&mut self,
_state: &mut S,
_testcase: &mut Testcase<I>,
) -> Result<(), Error> {
Ok(())
}
#[inline]
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
Ok(())
}
}
pub trait FeedbackState: Named + serde::Serialize + serde::de::DeserializeOwned {}
pub trait FeedbackStatesTuple: MatchName + serde::Serialize + serde::de::DeserializeOwned {}
impl FeedbackStatesTuple for () {}
impl<Head, Tail> FeedbackStatesTuple for (Head, Tail)
where
Head: FeedbackState,
Tail: FeedbackStatesTuple,
{
}
pub struct CombinedFeedback<A, B, I, S, FL>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
S: HasClientPerfStats,
{
pub first: A,
pub second: B,
name: String,
phantom: PhantomData<(I, S, FL)>,
}
impl<A, B, I, S, FL> Named for CombinedFeedback<A, B, I, S, FL>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
S: HasClientPerfStats,
{
fn name(&self) -> &str {
self.name.as_ref()
}
}
impl<A, B, I, S, FL> CombinedFeedback<A, B, I, S, FL>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
S: HasClientPerfStats,
{
pub fn new(first: A, second: B) -> Self {
let name = format!("{} ({},{})", FL::name(), first.name(), second.name());
Self {
first,
second,
name,
phantom: PhantomData,
}
}
}
impl<A, B, I, S, FL> Feedback<I, S> for CombinedFeedback<A, B, I, S, FL>
where
A: Feedback<I, S>,
B: Feedback<I, S>,
FL: FeedbackLogic<A, B, I, S>,
I: Input,
S: HasClientPerfStats,
{
fn is_interesting<EM, OT>(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
FL::is_pair_interesting(
&mut self.first,
&mut self.second,
state,
manager,
input,
observers,
exit_kind,
)
}
#[cfg(feature = "introspection")]
fn is_interesting_introspection<EM, OT>(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
FL::is_pair_interesting_introspection(
&mut self.first,
&mut self.second,
state,
manager,
input,
observers,
exit_kind,
)
}
#[inline]
fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(state, testcase)?;
self.second.append_metadata(state, testcase)
}
#[inline]
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
self.first.discard_metadata(state, input)?;
self.second.discard_metadata(state, input)
}
}
pub trait FeedbackLogic<A, B, I, S>: 'static
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
fn name() -> &'static str;
fn is_pair_interesting<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>;
#[cfg(feature = "introspection")]
#[allow(clippy::too_many_arguments)]
fn is_pair_interesting_introspection<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>;
}
pub struct LogicEagerOr {}
pub struct LogicFastOr {}
pub struct LogicEagerAnd {}
pub struct LogicFastAnd {}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicEagerOr
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
fn name() -> &'static str {
"Eager OR"
}
fn is_pair_interesting<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
let b = second.is_interesting(state, manager, input, observers, exit_kind)?;
Ok(a || b)
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_introspection<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
let b = second.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
Ok(a || b)
}
}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicFastOr
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
fn name() -> &'static str {
"Fast OR"
}
fn is_pair_interesting<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
if a {
return Ok(true);
}
second.is_interesting(state, manager, input, observers, exit_kind)
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_introspection<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
if a {
return Ok(true);
}
second.is_interesting_introspection(state, manager, input, observers, exit_kind)
}
}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicEagerAnd
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
fn name() -> &'static str {
"Eager AND"
}
fn is_pair_interesting<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
let b = second.is_interesting(state, manager, input, observers, exit_kind)?;
Ok(a && b)
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_introspection<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
let b = second.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
Ok(a && b)
}
}
impl<A, B, I, S> FeedbackLogic<A, B, I, S> for LogicFastAnd
where
A: Feedback<I, S>,
B: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
fn name() -> &'static str {
"Fast AND"
}
fn is_pair_interesting<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let a = first.is_interesting(state, manager, input, observers, exit_kind)?;
if !a {
return Ok(false);
}
second.is_interesting(state, manager, input, observers, exit_kind)
}
#[cfg(feature = "introspection")]
fn is_pair_interesting_introspection<EM, OT>(
first: &mut A,
second: &mut B,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let a = first.is_interesting_introspection(state, manager, input, observers, exit_kind)?;
if !a {
return Ok(false);
}
second.is_interesting_introspection(state, manager, input, observers, exit_kind)
}
}
pub type EagerAndFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicEagerAnd>;
pub type FastAndFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicFastAnd>;
pub type EagerOrFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicEagerOr>;
pub type FastOrFeedback<A, B, I, S> = CombinedFeedback<A, B, I, S, LogicFastOr>;
pub struct NotFeedback<A, I, S>
where
A: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
pub first: A,
name: String,
phantom: PhantomData<(I, S)>,
}
impl<A, I, S> Feedback<I, S> for NotFeedback<A, I, S>
where
A: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
fn is_interesting<EM, OT>(
&mut self,
state: &mut S,
manager: &mut EM,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
Ok(!self
.first
.is_interesting(state, manager, input, observers, exit_kind)?)
}
#[inline]
fn append_metadata(&mut self, state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.first.append_metadata(state, testcase)
}
#[inline]
fn discard_metadata(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
self.first.discard_metadata(state, input)
}
}
impl<A, I, S> Named for NotFeedback<A, I, S>
where
A: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
#[inline]
fn name(&self) -> &str {
&self.name
}
}
impl<A, I, S> NotFeedback<A, I, S>
where
A: Feedback<I, S>,
I: Input,
S: HasClientPerfStats,
{
pub fn new(first: A) -> Self {
let name = format!("Not({})", first.name());
Self {
first,
name,
phantom: PhantomData,
}
}
}
#[macro_export]
macro_rules! feedback_and {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
$crate::feedbacks::EagerAndFeedback::new($head , feedback_and!($($tail),+))
};
}
#[macro_export]
macro_rules! feedback_and_fast {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
$crate::feedbacks::FastAndFeedback::new($head , feedback_and_fast!($($tail),+))
};
}
#[macro_export]
macro_rules! feedback_or {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
$crate::feedbacks::EagerOrFeedback::new($head , feedback_or!($($tail),+))
};
}
#[macro_export]
macro_rules! feedback_or_fast {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
$crate::feedbacks::FastOrFeedback::new($head , feedback_or_fast!($($tail),+))
};
}
#[macro_export]
macro_rules! feedback_not {
( $last:expr ) => {
$crate::feedbacks::NotFeedback::new($last)
};
}
impl<I, S> Feedback<I, S> for ()
where
I: Input,
S: HasClientPerfStats,
{
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
Ok(false)
}
}
impl Named for () {
#[inline]
fn name(&self) -> &str {
"Empty"
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CrashFeedback {}
impl<I, S> Feedback<I, S> for CrashFeedback
where
I: Input,
S: HasClientPerfStats,
{
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
if let ExitKind::Crash = exit_kind {
Ok(true)
} else {
Ok(false)
}
}
}
impl Named for CrashFeedback {
#[inline]
fn name(&self) -> &str {
"CrashFeedback"
}
}
impl CrashFeedback {
#[must_use]
pub fn new() -> Self {
Self {}
}
}
impl Default for CrashFeedback {
fn default() -> Self {
Self::new()
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeoutFeedback {}
impl<I, S> Feedback<I, S> for TimeoutFeedback
where
I: Input,
S: HasClientPerfStats,
{
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
_observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
if let ExitKind::Timeout = exit_kind {
Ok(true)
} else {
Ok(false)
}
}
}
impl Named for TimeoutFeedback {
#[inline]
fn name(&self) -> &str {
"TimeoutFeedback"
}
}
impl TimeoutFeedback {
#[must_use]
pub fn new() -> Self {
Self {}
}
}
impl Default for TimeoutFeedback {
fn default() -> Self {
Self::new()
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeFeedback {
exec_time: Option<Duration>,
name: String,
}
impl<I, S> Feedback<I, S> for TimeFeedback
where
I: Input,
S: HasClientPerfStats,
{
fn is_interesting<EM, OT>(
&mut self,
_state: &mut S,
_manager: &mut EM,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple<I, S>,
{
let observer = observers.match_name::<TimeObserver>(self.name()).unwrap();
self.exec_time = *observer.last_runtime();
Ok(false)
}
#[inline]
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
*testcase.exec_time_mut() = self.exec_time;
self.exec_time = None;
Ok(())
}
#[inline]
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
self.exec_time = None;
Ok(())
}
}
impl Named for TimeFeedback {
#[inline]
fn name(&self) -> &str {
self.name.as_str()
}
}
impl TimeFeedback {
#[must_use]
pub fn new(name: &'static str) -> Self {
Self {
exec_time: None,
name: name.to_string(),
}
}
#[must_use]
pub fn new_with_observer(observer: &TimeObserver) -> Self {
Self {
exec_time: None,
name: observer.name().to_string(),
}
}
}