use core::{fmt::Debug, marker::PhantomData, time::Duration};
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use std::{
fs,
path::{Path, PathBuf},
};
use crate::{
bolts::serdeany::{SerdeAny, SerdeAnyMap},
corpus::{Corpus, CorpusScheduler, Testcase},
events::{Event, EventManager, LogSeverity},
executors::{
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
},
feedbacks::Feedback,
generators::Generator,
inputs::Input,
mark_feature_time,
observers::ObserversTuple,
start_timer,
stats::ClientPerfStats,
utils::Rand,
Error,
};
#[cfg(feature = "introspection")]
use crate::stats::PerfFeature;
#[cfg(feature = "std")]
use crate::inputs::bytes::BytesInput;
pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
pub trait HasCorpus<C, I>
where
C: Corpus<I>,
I: Input,
{
fn corpus(&self) -> &C;
fn corpus_mut(&mut self) -> &mut C;
}
pub trait HasMaxSize {
fn max_size(&self) -> usize;
fn set_max_size(&mut self, max_size: usize);
}
pub trait HasSolutions<C, I>
where
C: Corpus<I>,
I: Input,
{
fn solutions(&self) -> &C;
fn solutions_mut(&mut self) -> &mut C;
}
pub trait HasRand<R>
where
R: Rand,
{
fn rand(&self) -> &R;
fn rand_mut(&mut self) -> &mut R;
}
pub trait HasClientPerfStats {
fn introspection_stats(&self) -> &ClientPerfStats;
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats;
}
pub trait HasMetadata {
fn metadata(&self) -> &SerdeAnyMap;
fn metadata_mut(&mut self) -> &mut SerdeAnyMap;
#[inline]
fn add_metadata<M>(&mut self, meta: M)
where
M: SerdeAny,
{
self.metadata_mut().insert(meta);
}
#[inline]
fn has_metadata<M>(&self) -> bool
where
M: SerdeAny,
{
self.metadata().get::<M>().is_some()
}
}
pub trait HasFeedback<F, I>: Sized
where
F: Feedback<I>,
I: Input,
{
fn feedback(&self) -> &F;
fn feedback_mut(&mut self) -> &mut F;
}
pub trait HasObjective<OF, I>: Sized
where
OF: Feedback<I>,
I: Input,
{
fn objective(&self) -> &OF;
fn objective_mut(&mut self) -> &mut OF;
}
pub trait HasExecutions {
fn executions(&self) -> &usize;
fn executions_mut(&mut self) -> &mut usize;
}
pub trait HasStartTime {
fn start_time(&self) -> &Duration;
fn start_time_mut(&mut self) -> &mut Duration;
}
pub trait IfInteresting<I>: Sized
where
I: Input,
{
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
OT: ObserversTuple;
fn add_if_interesting<CS>(
&mut self,
input: &I,
is_interesting: bool,
scheduler: &CS,
) -> Result<Option<usize>, Error>
where
CS: CorpusScheduler<I, Self>,
Self: Sized;
}
pub trait Evaluator<I>: Sized
where
I: Input,
{
fn evaluate_input<CS, E, EM, OT>(
&mut self,
input: I,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
) -> Result<(bool, Option<usize>), Error>
where
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>;
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "F: serde::de::DeserializeOwned")]
pub struct State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
rand: R,
executions: usize,
start_time: Duration,
corpus: C,
feedback: F,
solutions: SC,
objective: OF,
metadata: SerdeAnyMap,
max_size: usize,
#[cfg(feature = "introspection")]
introspection_stats: ClientPerfStats,
phantom: PhantomData<I>,
}
impl<C, F, I, OF, R, SC> HasRand<R> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
#[inline]
fn rand(&self) -> &R {
&self.rand
}
#[inline]
fn rand_mut(&mut self) -> &mut R {
&mut self.rand
}
}
impl<C, F, I, OF, R, SC> HasCorpus<C, I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
#[inline]
fn corpus(&self) -> &C {
&self.corpus
}
#[inline]
fn corpus_mut(&mut self) -> &mut C {
&mut self.corpus
}
}
impl<C, F, I, OF, R, SC> HasSolutions<SC, I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
#[inline]
fn solutions(&self) -> &SC {
&self.solutions
}
#[inline]
fn solutions_mut(&mut self) -> &mut SC {
&mut self.solutions
}
}
impl<C, F, I, OF, R, SC> HasMetadata for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
#[inline]
fn metadata(&self) -> &SerdeAnyMap {
&self.metadata
}
#[inline]
fn metadata_mut(&mut self) -> &mut SerdeAnyMap {
&mut self.metadata
}
}
impl<C, F, I, OF, R, SC> HasFeedback<F, I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
#[inline]
fn feedback(&self) -> &F {
&self.feedback
}
#[inline]
fn feedback_mut(&mut self) -> &mut F {
&mut self.feedback
}
}
impl<C, F, I, OF, R, SC> HasObjective<OF, I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
#[inline]
fn objective(&self) -> &OF {
&self.objective
}
#[inline]
fn objective_mut(&mut self) -> &mut OF {
&mut self.objective
}
}
impl<C, F, I, OF, R, SC> HasExecutions for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
#[inline]
fn executions(&self) -> &usize {
&self.executions
}
#[inline]
fn executions_mut(&mut self) -> &mut usize {
&mut self.executions
}
}
impl<C, F, I, OF, R, SC> HasMaxSize for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
fn max_size(&self) -> usize {
self.max_size
}
fn set_max_size(&mut self, max_size: usize) {
self.max_size = max_size
}
}
impl<C, F, I, OF, R, SC> HasStartTime for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
#[inline]
fn start_time(&self) -> &Duration {
&self.start_time
}
#[inline]
fn start_time_mut(&mut self) -> &mut Duration {
&mut self.start_time
}
}
impl<C, F, I, OF, R, SC> IfInteresting<I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
self.feedback_mut()
.is_interesting(input, observers, exit_kind)
}
#[inline]
fn add_if_interesting<CS>(
&mut self,
input: &I,
is_interesting: bool,
scheduler: &CS,
) -> Result<Option<usize>, Error>
where
CS: CorpusScheduler<I, Self>,
{
if is_interesting {
let mut testcase = Testcase::new(input.clone());
self.feedback_mut().append_metadata(&mut testcase)?;
let idx = self.corpus.add(testcase)?;
scheduler.on_add(self, idx)?;
Ok(Some(idx))
} else {
self.feedback_mut().discard_metadata(&input)?;
Ok(None)
}
}
}
impl<C, F, I, OF, R, SC> Evaluator<I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
#[inline]
fn evaluate_input<CS, E, EM, OT>(
&mut self,
input: I,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
) -> Result<(bool, Option<usize>), Error>
where
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
C: Corpus<I>,
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>,
{
let (is_interesting, is_solution) = self.execute_input(&input, executor, manager)?;
let observers = executor.observers();
if is_solution {
let mut testcase = Testcase::new(input.clone());
self.objective_mut().append_metadata(&mut testcase)?;
self.solutions_mut().add(testcase)?;
} else {
self.objective_mut().discard_metadata(&input)?;
}
let corpus_idx = self.add_if_interesting(&input, is_interesting, scheduler)?;
if corpus_idx.is_some() {
let observers_buf = manager.serialize_observers(observers)?;
manager.fire(
self,
Event::NewTestcase {
input,
observers_buf,
corpus_size: self.corpus().count() + 1,
client_config: "TODO".into(),
time: crate::utils::current_time(),
executions: *self.executions(),
},
)?;
}
Ok((is_interesting, corpus_idx))
}
}
#[cfg(feature = "std")]
impl<C, F, OF, R, SC> State<C, F, BytesInput, OF, R, SC>
where
C: Corpus<BytesInput>,
R: Rand,
F: Feedback<BytesInput>,
SC: Corpus<BytesInput>,
OF: Feedback<BytesInput>,
{
fn load_from_directory<CS, E, OT, EM>(
&mut self,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
in_dir: &Path,
) -> Result<(), Error>
where
E: Executor<BytesInput>
+ HasObservers<OT>
+ HasExecHooks<EM, BytesInput, Self>
+ HasObserversHooks<EM, BytesInput, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, BytesInput, Self>,
EM: EventManager<BytesInput, Self>,
CS: CorpusScheduler<BytesInput, Self>,
{
for entry in fs::read_dir(in_dir)? {
let entry = entry?;
let path = entry.path();
let attributes = fs::metadata(&path);
if attributes.is_err() {
continue;
}
let attr = attributes?;
if attr.is_file() && attr.len() > 0 {
println!("Loading file {:?} ...", &path);
let bytes = fs::read(&path)?;
let input = BytesInput::new(bytes);
let (is_interesting, is_solution) =
self.execute_input(&input, executor, manager)?;
if self
.add_if_interesting(&input, is_interesting, scheduler)?
.is_none()
{
println!("File {:?} was not interesting, skipped.", &path);
}
if is_solution {
println!("File {:?} is a solution, however will be not considered as it is an initial testcase.", &path);
}
} else if attr.is_dir() {
self.load_from_directory(executor, manager, scheduler, &path)?;
}
}
Ok(())
}
pub fn load_initial_inputs<CS, E, OT, EM>(
&mut self,
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
E: Executor<BytesInput>
+ HasObservers<OT>
+ HasExecHooks<EM, BytesInput, Self>
+ HasObserversHooks<EM, BytesInput, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, BytesInput, Self>,
EM: EventManager<BytesInput, Self>,
CS: CorpusScheduler<BytesInput, Self>,
{
for in_dir in in_dirs {
self.load_from_directory(executor, manager, scheduler, in_dir)?;
}
manager.fire(
self,
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {} initial testcases.", self.corpus().count()),
phantom: PhantomData,
},
)?;
manager.process(self, executor, scheduler)?;
Ok(())
}
}
impl<C, F, I, OF, R, SC> State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
pub fn execute_input<E, EM, OT>(
&mut self,
input: &I,
executor: &mut E,
event_mgr: &mut EM,
) -> Result<(bool, bool), Error>
where
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
C: Corpus<I>,
EM: EventManager<I, Self>,
{
start_timer!(self);
executor.pre_exec_observers(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PreExecObservers);
start_timer!(self);
executor.pre_exec(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PreExec);
start_timer!(self);
let exit_kind = executor.run_target(input)?;
mark_feature_time!(self, PerfFeature::TargetExecution);
start_timer!(self);
executor.post_exec(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PostExec);
*self.executions_mut() += 1;
start_timer!(self);
executor.post_exec_observers(self, event_mgr, input)?;
mark_feature_time!(self, PerfFeature::PostExecObservers);
let observers = executor.observers();
#[cfg(not(feature = "introspection"))]
let is_interesting = self
.feedback_mut()
.is_interesting(&input, observers, &exit_kind)?;
#[cfg(feature = "introspection")]
let is_interesting = {
let mut feedback_stats = [0_u64; crate::stats::NUM_FEEDBACKS];
let feedback_index = 0;
let is_interesting = self.feedback_mut().is_interesting_with_perf(
&input,
observers,
&exit_kind,
&mut feedback_stats,
feedback_index,
)?;
self.introspection_stats_mut()
.update_feedbacks(feedback_stats);
is_interesting
};
start_timer!(self);
let is_solution = self
.objective_mut()
.is_interesting(&input, observers, &exit_kind)?;
mark_feature_time!(self, PerfFeature::GetObjectivesInterestingAll);
Ok((is_interesting, is_solution))
}
pub fn generate_initial_inputs<CS, G, E, OT, EM>(
&mut self,
executor: &mut E,
generator: &mut G,
manager: &mut EM,
scheduler: &CS,
num: usize,
) -> Result<(), Error>
where
G: Generator<I, R>,
C: Corpus<I>,
E: Executor<I>
+ HasObservers<OT>
+ HasExecHooks<EM, I, Self>
+ HasObserversHooks<EM, I, OT, Self>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, Self>,
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>,
{
let mut added = 0;
for _ in 0..num {
let input = generator.generate(self.rand_mut())?;
let (is_interesting, _) = self.evaluate_input(input, executor, manager, scheduler)?;
if is_interesting {
added += 1;
}
}
manager.fire(
self,
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {} over {} initial testcases", added, num),
phantom: PhantomData,
},
)?;
manager.process(self, executor, scheduler)?;
Ok(())
}
pub fn new(rand: R, corpus: C, feedback: F, solutions: SC, objective: OF) -> Self {
Self {
rand,
executions: 0,
start_time: Duration::from_millis(0),
metadata: SerdeAnyMap::default(),
corpus,
feedback,
solutions,
objective,
max_size: DEFAULT_MAX_SIZE,
#[cfg(feature = "introspection")]
introspection_stats: ClientPerfStats::new(),
phantom: PhantomData,
}
}
}
#[cfg(feature = "introspection")]
impl<C, F, I, OF, R, SC> HasClientPerfStats for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
fn introspection_stats(&self) -> &ClientPerfStats {
&self.introspection_stats
}
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats {
&mut self.introspection_stats
}
}
#[cfg(not(feature = "introspection"))]
impl<C, F, I, OF, R, SC> HasClientPerfStats for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
F: Feedback<I>,
SC: Corpus<I>,
OF: Feedback<I>,
{
fn introspection_stats(&self) -> &ClientPerfStats {
unimplemented!()
}
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats {
unimplemented!()
}
}