use alloc::vec::Vec;
use core::{
borrow::BorrowMut,
cell::{Ref, RefMut},
fmt::Debug,
marker::PhantomData,
time::Duration,
};
#[cfg(feature = "std")]
use std::{
fs,
path::{Path, PathBuf},
};
#[cfg(feature = "std")]
use libafl_bolts::core_affinity::{CoreId, Cores};
use libafl_bolts::{
rands::{Rand, StdRand},
serdeany::{NamedSerdeAnyMap, SerdeAnyMap},
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(feature = "introspection")]
use crate::monitors::ClientPerfMonitor;
#[cfg(feature = "scalability_introspection")]
use crate::monitors::ScalabilityMonitor;
use crate::{
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, HasTestcase, Testcase},
events::{Event, EventFirer, LogSeverity},
feedbacks::Feedback,
fuzzer::{Evaluator, ExecuteInputResult},
generators::Generator,
inputs::{Input, UsesInput},
stages::{HasCurrentStage, HasNestedStageStatus},
Error, HasMetadata, HasNamedMetadata,
};
pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
pub trait State:
UsesInput
+ Serialize
+ DeserializeOwned
+ MaybeHasClientPerfMonitor
+ MaybeHasScalabilityMonitor
+ HasCurrentCorpusIdx
+ HasCurrentStage
{
}
pub trait UsesState: UsesInput<Input = <Self::State as UsesInput>::Input> {
type State: State;
}
impl<KS> UsesInput for KS
where
KS: UsesState,
{
type Input = <KS::State as UsesInput>::Input;
}
pub trait HasCorpus: UsesInput {
type Corpus: Corpus<Input = <Self as UsesInput>::Input>;
fn corpus(&self) -> &Self::Corpus;
fn corpus_mut(&mut self) -> &mut Self::Corpus;
}
pub trait HasMaxSize {
fn max_size(&self) -> usize;
fn set_max_size(&mut self, max_size: usize);
}
pub trait HasSolutions: UsesInput {
type Solutions: Corpus<Input = <Self as UsesInput>::Input>;
fn solutions(&self) -> &Self::Solutions;
fn solutions_mut(&mut self) -> &mut Self::Solutions;
}
pub trait HasRand {
type Rand: Rand;
fn rand(&self) -> &Self::Rand;
fn rand_mut(&mut self) -> &mut Self::Rand;
}
#[cfg(feature = "introspection")]
pub trait HasClientPerfMonitor {
fn introspection_monitor(&self) -> &ClientPerfMonitor;
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor;
}
#[cfg(feature = "introspection")]
pub trait MaybeHasClientPerfMonitor: HasClientPerfMonitor {}
#[cfg(not(feature = "introspection"))]
pub trait MaybeHasClientPerfMonitor {}
#[cfg(not(feature = "introspection"))]
impl<T> MaybeHasClientPerfMonitor for T {}
#[cfg(feature = "introspection")]
impl<T> MaybeHasClientPerfMonitor for T where T: HasClientPerfMonitor {}
#[cfg(feature = "scalability_introspection")]
pub trait MaybeHasScalabilityMonitor: HasScalabilityMonitor {}
#[cfg(not(feature = "scalability_introspection"))]
pub trait MaybeHasScalabilityMonitor {}
#[cfg(not(feature = "scalability_introspection"))]
impl<T> MaybeHasScalabilityMonitor for T {}
#[cfg(feature = "scalability_introspection")]
impl<T> MaybeHasScalabilityMonitor for T where T: HasScalabilityMonitor {}
#[cfg(feature = "scalability_introspection")]
pub trait HasScalabilityMonitor {
fn scalability_monitor(&self) -> &ScalabilityMonitor;
fn scalability_monitor_mut(&mut self) -> &mut ScalabilityMonitor;
}
pub trait HasExecutions {
fn executions(&self) -> &u64;
fn executions_mut(&mut self) -> &mut u64;
}
pub trait HasImported {
fn imported(&self) -> &usize;
fn imported_mut(&mut self) -> &mut usize;
}
pub trait HasStartTime {
fn start_time(&self) -> &Duration;
fn start_time_mut(&mut self) -> &mut Duration;
}
pub trait HasLastReportTime {
fn last_report_time(&self) -> &Option<Duration>;
fn last_report_time_mut(&mut self) -> &mut Option<Duration>;
}
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "
C: serde::Serialize + for<'a> serde::Deserialize<'a>,
SC: serde::Serialize + for<'a> serde::Deserialize<'a>,
R: serde::Serialize + for<'a> serde::Deserialize<'a>
")]
pub struct StdState<I, C, R, SC> {
rand: R,
executions: u64,
start_time: Duration,
imported: usize,
corpus: C,
solutions: SC,
metadata: SerdeAnyMap,
named_metadata: NamedSerdeAnyMap,
max_size: usize,
#[cfg(feature = "introspection")]
introspection_monitor: ClientPerfMonitor,
#[cfg(feature = "scalability_introspection")]
scalability_monitor: ScalabilityMonitor,
#[cfg(feature = "std")]
remaining_initial_files: Option<Vec<PathBuf>>,
#[cfg(feature = "std")]
dont_reenter: Option<Vec<PathBuf>>,
#[cfg(feature = "std")]
multicore_inputs_processed: Option<bool>,
last_report_time: Option<Duration>,
corpus_idx: Option<CorpusId>,
stage_idx_stack: Vec<usize>,
stage_depth: usize,
phantom: PhantomData<I>,
}
impl<I, C, R, SC> UsesInput for StdState<I, C, R, SC>
where
I: Input,
{
type Input = I;
}
impl<I, C, R, SC> State for StdState<I, C, R, SC>
where
C: Corpus<Input = Self::Input>,
R: Rand,
SC: Corpus<Input = Self::Input>,
Self: UsesInput,
{
}
impl<I, C, R, SC> HasRand for StdState<I, C, R, SC>
where
R: Rand,
{
type Rand = R;
#[inline]
fn rand(&self) -> &Self::Rand {
&self.rand
}
#[inline]
fn rand_mut(&mut self) -> &mut Self::Rand {
&mut self.rand
}
}
impl<I, C, R, SC> HasCorpus for StdState<I, C, R, SC>
where
I: Input,
C: Corpus<Input = <Self as UsesInput>::Input>,
R: Rand,
{
type Corpus = C;
#[inline]
fn corpus(&self) -> &Self::Corpus {
&self.corpus
}
#[inline]
fn corpus_mut(&mut self) -> &mut Self::Corpus {
&mut self.corpus
}
}
impl<I, C, R, SC> HasTestcase for StdState<I, C, R, SC>
where
I: Input,
C: Corpus<Input = <Self as UsesInput>::Input>,
R: Rand,
{
fn testcase(
&self,
id: CorpusId,
) -> Result<Ref<'_, Testcase<<Self as UsesInput>::Input>>, Error> {
Ok(self.corpus().get(id)?.borrow())
}
fn testcase_mut(
&self,
id: CorpusId,
) -> Result<RefMut<'_, Testcase<<Self as UsesInput>::Input>>, Error> {
Ok(self.corpus().get(id)?.borrow_mut())
}
}
impl<I, C, R, SC> HasSolutions for StdState<I, C, R, SC>
where
I: Input,
SC: Corpus<Input = <Self as UsesInput>::Input>,
{
type Solutions = SC;
#[inline]
fn solutions(&self) -> &SC {
&self.solutions
}
#[inline]
fn solutions_mut(&mut self) -> &mut SC {
&mut self.solutions
}
}
impl<I, C, R, SC> HasMetadata for StdState<I, C, R, SC> {
#[inline]
fn metadata_map(&self) -> &SerdeAnyMap {
&self.metadata
}
#[inline]
fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap {
&mut self.metadata
}
}
impl<I, C, R, SC> HasNamedMetadata for StdState<I, C, R, SC> {
#[inline]
fn named_metadata_map(&self) -> &NamedSerdeAnyMap {
&self.named_metadata
}
#[inline]
fn named_metadata_map_mut(&mut self) -> &mut NamedSerdeAnyMap {
&mut self.named_metadata
}
}
impl<I, C, R, SC> HasExecutions for StdState<I, C, R, SC> {
#[inline]
fn executions(&self) -> &u64 {
&self.executions
}
#[inline]
fn executions_mut(&mut self) -> &mut u64 {
&mut self.executions
}
}
impl<I, C, R, SC> HasImported for StdState<I, C, R, SC> {
#[inline]
fn imported(&self) -> &usize {
&self.imported
}
#[inline]
fn imported_mut(&mut self) -> &mut usize {
&mut self.imported
}
}
impl<I, C, R, SC> HasLastReportTime for StdState<I, C, R, SC> {
fn last_report_time(&self) -> &Option<Duration> {
&self.last_report_time
}
fn last_report_time_mut(&mut self) -> &mut Option<Duration> {
&mut self.last_report_time
}
}
impl<I, C, R, SC> HasMaxSize for StdState<I, C, R, SC> {
fn max_size(&self) -> usize {
self.max_size
}
fn set_max_size(&mut self, max_size: usize) {
self.max_size = max_size;
}
}
impl<I, C, R, SC> HasStartTime for StdState<I, C, R, SC> {
#[inline]
fn start_time(&self) -> &Duration {
&self.start_time
}
#[inline]
fn start_time_mut(&mut self) -> &mut Duration {
&mut self.start_time
}
}
impl<I, C, R, SC> HasCurrentCorpusIdx for StdState<I, C, R, SC> {
fn set_corpus_idx(&mut self, idx: CorpusId) -> Result<(), Error> {
self.corpus_idx = Some(idx);
Ok(())
}
fn clear_corpus_idx(&mut self) -> Result<(), Error> {
self.corpus_idx = None;
Ok(())
}
fn current_corpus_idx(&self) -> Result<Option<CorpusId>, Error> {
Ok(self.corpus_idx)
}
}
pub trait HasCurrentTestcase<I>
where
I: Input,
{
fn current_testcase(&self) -> Result<Ref<'_, Testcase<I>>, Error>;
fn current_testcase_mut(&self) -> Result<RefMut<'_, Testcase<I>>, Error>;
fn current_input_cloned(&self) -> Result<I, Error>;
}
impl<I, T> HasCurrentTestcase<I> for T
where
I: Input,
T: HasCorpus + HasCurrentCorpusIdx + UsesInput<Input = I>,
{
fn current_testcase(&self) -> Result<Ref<'_, Testcase<I>>, Error> {
let Some(corpus_id) = self.current_corpus_idx()? else {
return Err(Error::key_not_found(
"We are not currently processing a testcase",
));
};
Ok(self.corpus().get(corpus_id)?.borrow())
}
fn current_testcase_mut(&self) -> Result<RefMut<'_, Testcase<I>>, Error> {
let Some(corpus_id) = self.current_corpus_idx()? else {
return Err(Error::illegal_state(
"We are not currently processing a testcase",
));
};
Ok(self.corpus().get(corpus_id)?.borrow_mut())
}
fn current_input_cloned(&self) -> Result<I, Error> {
let mut testcase = self.current_testcase_mut()?;
Ok(testcase.borrow_mut().load_input(self.corpus())?.clone())
}
}
impl<I, C, R, SC> HasCurrentStage for StdState<I, C, R, SC> {
fn set_stage(&mut self, idx: usize) -> Result<(), Error> {
if self.stage_depth != self.stage_idx_stack.len() {
return Err(Error::illegal_state(
"stage not resumed before setting stage",
));
}
self.stage_idx_stack.push(idx);
Ok(())
}
fn clear_stage(&mut self) -> Result<(), Error> {
self.stage_idx_stack.truncate(self.stage_depth);
Ok(())
}
fn current_stage(&self) -> Result<Option<usize>, Error> {
Ok(self.stage_idx_stack.get(self.stage_depth).copied())
}
fn on_restart(&mut self) -> Result<(), Error> {
self.stage_depth = 0; Ok(())
}
}
impl<I, C, R, SC> HasNestedStageStatus for StdState<I, C, R, SC> {
fn enter_inner_stage(&mut self) -> Result<(), Error> {
self.stage_depth += 1;
Ok(())
}
fn exit_inner_stage(&mut self) -> Result<(), Error> {
self.stage_depth -= 1;
Ok(())
}
}
#[cfg(feature = "std")]
impl<C, I, R, SC> StdState<I, C, R, SC>
where
I: Input,
C: Corpus<Input = <Self as UsesInput>::Input>,
R: Rand,
SC: Corpus<Input = <Self as UsesInput>::Input>,
{
pub fn must_load_initial_inputs(&self) -> bool {
self.corpus().count() == 0
|| (self.remaining_initial_files.is_some()
&& !self.remaining_initial_files.as_ref().unwrap().is_empty())
}
fn next_file(&mut self) -> Result<PathBuf, Error> {
loop {
if let Some(path) = self.remaining_initial_files.as_mut().and_then(Vec::pop) {
let filename = path.file_name().unwrap().to_string_lossy();
if filename.starts_with('.')
{
continue;
}
let attributes = fs::metadata(&path);
if attributes.is_err() {
continue;
}
let attr = attributes?;
if attr.is_file() && attr.len() > 0 {
return Ok(path);
} else if attr.is_dir() {
let files = self.remaining_initial_files.as_mut().unwrap();
path.read_dir()?
.try_for_each(|entry| entry.map(|e| files.push(e.path())))?;
} else if attr.is_symlink() {
let path = fs::canonicalize(path)?;
let dont_reenter = self.dont_reenter.get_or_insert_with(Default::default);
if dont_reenter.iter().any(|p| path.starts_with(p)) {
continue;
}
if path.is_dir() {
dont_reenter.push(path.clone());
}
let files = self.remaining_initial_files.as_mut().unwrap();
files.push(path);
}
} else {
return Err(Error::iterator_end("No remaining files to load."));
}
}
}
fn reset_initial_files_state(&mut self) {
self.remaining_initial_files = None;
self.dont_reenter = None;
}
fn canonicalize_input_dirs(&mut self, in_dirs: &[PathBuf]) -> Result<(), Error> {
if let Some(remaining) = self.remaining_initial_files.as_ref() {
if remaining.is_empty() {
return Ok(());
}
} else {
let files = in_dirs.iter().try_fold(Vec::new(), |mut res, file| {
file.canonicalize().map(|canonicalized| {
res.push(canonicalized);
res
})
})?;
self.dont_reenter = Some(files.clone());
self.remaining_initial_files = Some(files);
}
Ok(())
}
fn load_initial_inputs_custom_by_filenames<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
file_list: &[PathBuf],
forced: bool,
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
) -> Result<(), Error>
where
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
if let Some(remaining) = self.remaining_initial_files.as_ref() {
if remaining.is_empty() {
return Ok(());
}
} else {
self.remaining_initial_files = Some(file_list.to_vec());
}
self.continue_loading_initial_inputs_custom(fuzzer, executor, manager, forced, loader)
}
fn load_file<E, EM, Z>(
&mut self,
path: &PathBuf,
manager: &mut EM,
fuzzer: &mut Z,
executor: &mut E,
forced: bool,
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
) -> Result<(), Error>
where
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
log::info!("Loading file {:?} ...", &path);
let input = loader(fuzzer, self, path)?;
if forced {
let _: CorpusId = fuzzer.add_input(self, executor, manager, input)?;
} else {
let (res, _) = fuzzer.evaluate_input(self, executor, manager, input.clone())?;
if res == ExecuteInputResult::None {
fuzzer.add_disabled_input(self, input)?;
log::warn!("input {:?} was not interesting, adding as disabled.", &path);
}
}
Ok(())
}
fn continue_loading_initial_inputs_custom<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
forced: bool,
loader: &mut dyn FnMut(&mut Z, &mut Self, &Path) -> Result<I, Error>,
) -> Result<(), Error>
where
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
loop {
match self.next_file() {
Ok(path) => {
self.load_file(&path, manager, fuzzer, executor, forced, loader)?;
}
Err(Error::IteratorEnd(_, _)) => break,
Err(e) => return Err(e),
}
}
manager.fire(
self,
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {} initial testcases.", self.corpus().count()), phantom: PhantomData::<I>,
},
)?;
Ok(())
}
pub fn load_initial_inputs_by_filenames<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
file_list: &[PathBuf],
) -> Result<(), Error>
where
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.load_initial_inputs_custom_by_filenames(
fuzzer,
executor,
manager,
file_list,
false,
&mut |_, _, path| I::from_file(path),
)
}
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
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.canonicalize_input_dirs(in_dirs)?;
self.continue_loading_initial_inputs_custom(
fuzzer,
executor,
manager,
true,
&mut |_, _, path| I::from_file(path),
)
}
pub fn load_initial_inputs_by_filenames_forced<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
file_list: &[PathBuf],
) -> Result<(), Error>
where
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.load_initial_inputs_custom_by_filenames(
fuzzer,
executor,
manager,
file_list,
true,
&mut |_, _, path| I::from_file(path),
)
}
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
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.canonicalize_input_dirs(in_dirs)?;
self.continue_loading_initial_inputs_custom(
fuzzer,
executor,
manager,
false,
&mut |_, _, path| I::from_file(path),
)
}
fn calculate_corpus_size(&mut self) -> Result<usize, Error> {
let mut count: usize = 0;
loop {
match self.next_file() {
Ok(_) => {
count = count.saturating_add(1);
}
Err(Error::IteratorEnd(_, _)) => break,
Err(e) => return Err(e),
}
}
Ok(count)
}
pub fn load_initial_inputs_multicore<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dirs: &[PathBuf],
core_id: &CoreId,
cores: &Cores,
) -> Result<(), Error>
where
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
Z: Evaluator<E, EM, State = Self>,
{
if self.multicore_inputs_processed.unwrap_or(false) {
self.continue_loading_initial_inputs_custom(
fuzzer,
executor,
manager,
false,
&mut |_, _, path| I::from_file(path),
)?;
} else {
self.canonicalize_input_dirs(in_dirs)?;
let corpus_size = self.calculate_corpus_size()?;
log::info!(
"{} total_corpus_size, {} cores",
corpus_size,
cores.ids.len()
);
self.reset_initial_files_state();
self.canonicalize_input_dirs(in_dirs)?;
if cores.ids.len() > corpus_size {
log::info!(
"low intial corpus count ({}), no parallelism required.",
corpus_size
);
} else {
let core_index = cores
.ids
.iter()
.enumerate()
.find(|(_, c)| *c == core_id)
.unwrap_or_else(|| panic!("core id {} not in cores list", core_id.0))
.0;
let chunk_size = corpus_size.saturating_div(cores.ids.len());
let mut skip = core_index.saturating_mul(chunk_size);
let mut inputs_todo = chunk_size;
let mut collected_inputs = Vec::new();
log::info!(
"core = {}, core_index = {}, chunk_size = {}, skip = {}",
core_id.0,
core_index,
chunk_size,
skip
);
loop {
match self.next_file() {
Ok(path) => {
if skip != 0 {
skip = skip.saturating_sub(1);
continue;
}
if inputs_todo == 0 {
break;
}
collected_inputs.push(path);
inputs_todo = inputs_todo.saturating_sub(1);
}
Err(Error::IteratorEnd(_, _)) => break,
Err(e) => {
return Err(e);
}
}
}
self.remaining_initial_files = Some(collected_inputs);
}
self.multicore_inputs_processed = Some(true);
return self
.load_initial_inputs_multicore(fuzzer, executor, manager, in_dirs, core_id, cores);
}
Ok(())
}
}
impl<C, I, R, SC> StdState<I, C, R, SC>
where
I: Input,
C: Corpus<Input = <Self as UsesInput>::Input>,
R: Rand,
SC: Corpus<Input = <Self as UsesInput>::Input>,
{
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
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
G: Generator<<Self as UsesInput>::Input, Self>,
Z: Evaluator<E, EM, State = Self>,
{
let mut added = 0;
for _ in 0..num {
let input = generator.generate(self)?;
if forced {
let _: CorpusId = 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 {added} over {num} initial testcases"),
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
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
G: Generator<<Self as UsesInput>::Input, Self>,
Z: Evaluator<E, EM, State = 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
E: UsesState<State = Self>,
EM: EventFirer<State = Self>,
G: Generator<<Self as UsesInput>::Input, Self>,
Z: Evaluator<E, EM, State = Self>,
{
self.generate_initial_internal(fuzzer, executor, generator, manager, num, false)
}
pub fn new<F, O>(
rand: R,
corpus: C,
solutions: SC,
feedback: &mut F,
objective: &mut O,
) -> Result<Self, Error>
where
F: Feedback<Self>,
O: Feedback<Self>,
{
let mut state = Self {
rand,
executions: 0,
imported: 0,
start_time: Duration::from_millis(0),
metadata: SerdeAnyMap::default(),
named_metadata: NamedSerdeAnyMap::default(),
corpus,
solutions,
max_size: DEFAULT_MAX_SIZE,
#[cfg(feature = "introspection")]
introspection_monitor: ClientPerfMonitor::new(),
#[cfg(feature = "scalability_introspection")]
scalability_monitor: ScalabilityMonitor::new(),
#[cfg(feature = "std")]
remaining_initial_files: None,
#[cfg(feature = "std")]
dont_reenter: None,
last_report_time: None,
corpus_idx: None,
stage_depth: 0,
stage_idx_stack: Vec::new(),
phantom: PhantomData,
#[cfg(feature = "std")]
multicore_inputs_processed: None,
};
feedback.init_state(&mut state)?;
objective.init_state(&mut state)?;
Ok(state)
}
}
#[cfg(feature = "introspection")]
impl<I, C, R, SC> HasClientPerfMonitor for StdState<I, C, R, SC> {
fn introspection_monitor(&self) -> &ClientPerfMonitor {
&self.introspection_monitor
}
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor {
&mut self.introspection_monitor
}
}
#[cfg(feature = "scalability_introspection")]
impl<I, C, R, SC> HasScalabilityMonitor for StdState<I, C, R, SC> {
fn scalability_monitor(&self) -> &ScalabilityMonitor {
&self.scalability_monitor
}
fn scalability_monitor_mut(&mut self) -> &mut ScalabilityMonitor {
&mut self.scalability_monitor
}
}
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct NopState<I> {
metadata: SerdeAnyMap,
execution: u64,
rand: StdRand,
phantom: PhantomData<I>,
}
impl<I> NopState<I> {
#[must_use]
pub fn new() -> Self {
NopState {
metadata: SerdeAnyMap::new(),
execution: 0,
rand: StdRand::default(),
phantom: PhantomData,
}
}
}
impl<I> UsesInput for NopState<I>
where
I: Input,
{
type Input = I;
}
impl<I> HasExecutions for NopState<I> {
fn executions(&self) -> &u64 {
&self.execution
}
fn executions_mut(&mut self) -> &mut u64 {
&mut self.execution
}
}
impl<I> HasLastReportTime for NopState<I> {
fn last_report_time(&self) -> &Option<Duration> {
unimplemented!();
}
fn last_report_time_mut(&mut self) -> &mut Option<Duration> {
unimplemented!();
}
}
impl<I> HasMetadata for NopState<I> {
fn metadata_map(&self) -> &SerdeAnyMap {
&self.metadata
}
fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap {
&mut self.metadata
}
}
impl<I> HasRand for NopState<I> {
type Rand = StdRand;
fn rand(&self) -> &Self::Rand {
&self.rand
}
fn rand_mut(&mut self) -> &mut Self::Rand {
&mut self.rand
}
}
impl<I> State for NopState<I> where I: Input {}
impl<I> HasCurrentCorpusIdx for NopState<I> {
fn set_corpus_idx(&mut self, _idx: CorpusId) -> Result<(), Error> {
Ok(())
}
fn clear_corpus_idx(&mut self) -> Result<(), Error> {
Ok(())
}
fn current_corpus_idx(&self) -> Result<Option<CorpusId>, Error> {
Ok(None)
}
}
impl<I> HasCurrentStage for NopState<I> {
fn set_stage(&mut self, _idx: usize) -> Result<(), Error> {
Ok(())
}
fn clear_stage(&mut self) -> Result<(), Error> {
Ok(())
}
fn current_stage(&self) -> Result<Option<usize>, Error> {
Ok(None)
}
}
#[cfg(feature = "introspection")]
impl<I> HasClientPerfMonitor for NopState<I> {
fn introspection_monitor(&self) -> &ClientPerfMonitor {
unimplemented!();
}
fn introspection_monitor_mut(&mut self) -> &mut ClientPerfMonitor {
unimplemented!();
}
}
#[cfg(feature = "scalability_introspection")]
impl<I> HasScalabilityMonitor for NopState<I> {
fn scalability_monitor(&self) -> &ScalabilityMonitor {
unimplemented!();
}
fn scalability_monitor_mut(&mut self) -> &mut ScalabilityMonitor {
unimplemented!();
}
}
#[cfg(test)]
pub mod test {
use libafl_bolts::rands::StdRand;
use super::StdState;
use crate::{corpus::InMemoryCorpus, inputs::Input};
#[must_use]
pub fn test_std_state<I: Input>() -> StdState<I, InMemoryCorpus<I>, StdRand, InMemoryCorpus<I>>
{
StdState::new(
StdRand::with_seed(0),
InMemoryCorpus::<I>::new(),
InMemoryCorpus::new(),
&mut (),
&mut (),
)
.expect("couldn't instantiate the test state")
}
}