use core::{fmt::Debug, marker::PhantomData, time::Duration};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(feature = "std")]
use std::{
fs,
path::{Path, PathBuf},
};
use crate::{
bolts::{
rands::Rand,
serdeany::{SerdeAny, SerdeAnyMap},
},
corpus::Corpus,
events::{Event, EventFirer, LogSeverity},
feedbacks::FeedbackStatesTuple,
fuzzer::{Evaluator, ExecuteInputResult},
generators::Generator,
inputs::Input,
stats::ClientPerfStats,
Error,
};
pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
pub trait State: Serialize + DeserializeOwned {}
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 HasFeedbackStates<FT>
where
FT: FeedbackStatesTuple,
{
fn feedback_states(&self) -> &FT;
fn feedback_states_mut(&mut self) -> &mut FT;
}
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;
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "FT: serde::de::DeserializeOwned")]
pub struct StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
rand: R,
executions: usize,
start_time: Duration,
corpus: C,
feedback_states: FT,
solutions: SC,
metadata: SerdeAnyMap,
max_size: usize,
#[cfg(feature = "introspection")]
introspection_stats: ClientPerfStats,
phantom: PhantomData<I>,
}
impl<C, FT, I, R, SC> State for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
}
impl<C, FT, I, R, SC> HasRand<R> for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
#[inline]
fn rand(&self) -> &R {
&self.rand
}
#[inline]
fn rand_mut(&mut self) -> &mut R {
&mut self.rand
}
}
impl<C, FT, I, R, SC> HasCorpus<C, I> for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
#[inline]
fn corpus(&self) -> &C {
&self.corpus
}
#[inline]
fn corpus_mut(&mut self) -> &mut C {
&mut self.corpus
}
}
impl<C, FT, I, R, SC> HasSolutions<SC, I> for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
#[inline]
fn solutions(&self) -> &SC {
&self.solutions
}
#[inline]
fn solutions_mut(&mut self) -> &mut SC {
&mut self.solutions
}
}
impl<C, FT, I, R, SC> HasMetadata for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
#[inline]
fn metadata(&self) -> &SerdeAnyMap {
&self.metadata
}
#[inline]
fn metadata_mut(&mut self) -> &mut SerdeAnyMap {
&mut self.metadata
}
}
impl<C, FT, I, R, SC> HasFeedbackStates<FT> for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
#[inline]
fn feedback_states(&self) -> &FT {
&self.feedback_states
}
#[inline]
fn feedback_states_mut(&mut self) -> &mut FT {
&mut self.feedback_states
}
}
impl<C, FT, I, R, SC> HasExecutions for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
#[inline]
fn executions(&self) -> &usize {
&self.executions
}
#[inline]
fn executions_mut(&mut self) -> &mut usize {
&mut self.executions
}
}
impl<C, FT, I, R, SC> HasMaxSize for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<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, FT, I, R, SC> HasStartTime for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
#[inline]
fn start_time(&self) -> &Duration {
&self.start_time
}
#[inline]
fn start_time_mut(&mut self) -> &mut Duration {
&mut self.start_time
}
}
#[cfg(feature = "std")]
impl<C, FT, I, R, SC> StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
fn load_from_directory<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dir: &Path,
forced: bool,
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, 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 input = I::from_file(&path)?;
if forced {
let _ = fuzzer.add_input(self, executor, manager, input)?;
} else {
let (res, _) = fuzzer.evaluate_input(self, executor, manager, input)?;
if res == ExecuteInputResult::None {
println!("File {:?} was not interesting, skipped.", &path);
}
}
} else if attr.is_dir() {
self.load_from_directory(fuzzer, executor, manager, &path, forced)?;
}
}
Ok(())
}
fn load_initial_inputs_internal<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dirs: &[PathBuf],
forced: bool,
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I, Self>,
{
for in_dir in in_dirs {
self.load_from_directory(fuzzer, executor, manager, in_dir, forced)?;
}
manager.fire(
self,
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {} initial testcases.", self.corpus().count()), phantom: PhantomData,
},
)?;
Ok(())
}
pub fn load_initial_inputs_forced<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I, Self>,
{
self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, true)
}
pub fn load_initial_inputs<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I, Self>,
{
self.load_initial_inputs_internal(fuzzer, executor, manager, in_dirs, false)
}
}
impl<C, FT, I, R, SC> StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
fn generate_initial_internal<G, E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
generator: &mut G,
manager: &mut EM,
num: usize,
forced: bool,
) -> Result<(), Error>
where
G: Generator<I, R>,
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I, Self>,
{
let mut added = 0;
for _ in 0..num {
let input = generator.generate(self.rand_mut())?;
if forced {
let _ = fuzzer.add_input(self, executor, manager, input)?;
added += 1;
} else {
let (res, _) = fuzzer.evaluate_input(self, executor, manager, input)?;
if res != ExecuteInputResult::None {
added += 1;
}
}
}
manager.fire(
self,
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {} over {} initial testcases", added, num),
phantom: PhantomData,
},
)?;
Ok(())
}
pub fn generate_initial_inputs_forced<G, E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
generator: &mut G,
manager: &mut EM,
num: usize,
) -> Result<(), Error>
where
G: Generator<I, R>,
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I, Self>,
{
self.generate_initial_internal(fuzzer, executor, generator, manager, num, true)
}
pub fn generate_initial_inputs<G, E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
generator: &mut G,
manager: &mut EM,
num: usize,
) -> Result<(), Error>
where
G: Generator<I, R>,
Z: Evaluator<E, EM, I, Self>,
EM: EventFirer<I, Self>,
{
self.generate_initial_internal(fuzzer, executor, generator, manager, num, false)
}
pub fn new(rand: R, corpus: C, solutions: SC, feedback_states: FT) -> Self {
Self {
rand,
executions: 0,
start_time: Duration::from_millis(0),
metadata: SerdeAnyMap::default(),
corpus,
feedback_states,
solutions,
max_size: DEFAULT_MAX_SIZE,
#[cfg(feature = "introspection")]
introspection_stats: ClientPerfStats::new(),
phantom: PhantomData,
}
}
}
#[cfg(feature = "introspection")]
impl<C, FT, I, R, SC> HasClientPerfStats for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<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, FT, I, R, SC> HasClientPerfStats for StdState<C, FT, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbackStatesTuple,
SC: Corpus<I>,
{
fn introspection_stats(&self) -> &ClientPerfStats {
unimplemented!()
}
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats {
unimplemented!()
}
}