#[cfg(feature = "std")]
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::{Deserialize, Serialize, de::DeserializeOwned};
mod stack;
pub use stack::StageStack;
#[cfg(feature = "introspection")]
use crate::monitors::stats::ClientPerfStats;
use crate::{
Error, HasMetadata, HasNamedMetadata,
corpus::{Corpus, CorpusId, HasCurrentCorpusId, HasTestcase, InMemoryCorpus, Testcase},
events::{Event, EventFirer, EventWithStats, LogSeverity},
feedbacks::StateInitializer,
fuzzer::{Evaluator, ExecuteInputResult},
generators::Generator,
inputs::{Input, NopInput},
stages::StageId,
};
pub const DEFAULT_MAX_SIZE: usize = 1_048_576;
pub trait HasCorpus<I> {
type Corpus: Corpus<I>;
fn corpus(&self) -> &Self::Corpus;
fn corpus_mut(&mut self) -> &mut Self::Corpus;
}
#[allow(dead_code)]
trait State:
Serialize
+ DeserializeOwned
+ MaybeHasClientPerfMonitor
+ HasCurrentCorpusId
+ HasCurrentStageId
+ Stoppable
{
}
impl<C, I, R, SC> State for StdState<C, I, R, SC>
where
C: Serialize + DeserializeOwned,
R: Rand + Serialize + for<'de> Deserialize<'de>,
SC: Serialize + DeserializeOwned,
{
}
pub trait HasMaxSize {
fn max_size(&self) -> usize;
fn set_max_size(&mut self, max_size: usize);
}
pub trait HasSolutions<I> {
type Solutions: Corpus<I>;
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_stats(&self) -> &ClientPerfStats;
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats;
}
#[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 {}
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 HasLastFoundTime {
fn last_found_time(&self) -> &Duration;
fn last_found_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>;
}
#[cfg(feature = "std")]
pub struct LoadConfig<'a, I, S, Z> {
forced: bool,
loader: &'a mut dyn FnMut(&mut Z, &mut S, &Path) -> Result<I, Error>,
exit_on_solution: bool,
}
#[cfg(feature = "std")]
impl<I, S, Z> Debug for LoadConfig<'_, I, S, Z> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "LoadConfig {{}}")
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(bound = "
C: serde::Serialize + for<'a> serde::Deserialize<'a>,
R: serde::Serialize + for<'a> serde::Deserialize<'a>,
SC: serde::Serialize + for<'a> serde::Deserialize<'a>,
")]
pub struct StdState<C, I, 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_stats: ClientPerfStats,
#[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>,
last_found_time: Duration,
corpus_id: Option<CorpusId>,
stop_requested: bool,
stage_stack: StageStack,
phantom: PhantomData<I>,
}
impl<C, I, R, SC> HasRand for StdState<C, I, 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<C, I, R, SC> HasCorpus<I> for StdState<C, I, R, SC>
where
C: Corpus<I>,
{
type Corpus = C;
#[inline]
fn corpus(&self) -> &Self::Corpus {
&self.corpus
}
#[inline]
fn corpus_mut(&mut self) -> &mut Self::Corpus {
&mut self.corpus
}
}
impl<C, I, R, SC> HasTestcase<I> for StdState<C, I, R, SC>
where
C: Corpus<I>,
{
fn testcase(&self, id: CorpusId) -> Result<Ref<'_, Testcase<I>>, Error> {
Ok(self.corpus().get(id)?.borrow())
}
fn testcase_mut(&self, id: CorpusId) -> Result<RefMut<'_, Testcase<I>>, Error> {
Ok(self.corpus().get(id)?.borrow_mut())
}
}
impl<C, I, R, SC> HasSolutions<I> for StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
SC: Corpus<I>,
{
type Solutions = SC;
#[inline]
fn solutions(&self) -> &SC {
&self.solutions
}
#[inline]
fn solutions_mut(&mut self) -> &mut SC {
&mut self.solutions
}
}
impl<C, I, R, SC> HasMetadata for StdState<C, I, R, SC> {
#[inline]
fn metadata_map(&self) -> &SerdeAnyMap {
&self.metadata
}
#[inline]
fn metadata_map_mut(&mut self) -> &mut SerdeAnyMap {
&mut self.metadata
}
}
impl<C, I, R, SC> HasNamedMetadata for StdState<C, I, 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<C, I, R, SC> HasExecutions for StdState<C, I, R, SC> {
#[inline]
fn executions(&self) -> &u64 {
&self.executions
}
#[inline]
fn executions_mut(&mut self) -> &mut u64 {
&mut self.executions
}
}
impl<C, I, R, SC> HasImported for StdState<C, I, R, SC> {
#[inline]
fn imported(&self) -> &usize {
&self.imported
}
#[inline]
fn imported_mut(&mut self) -> &mut usize {
&mut self.imported
}
}
impl<C, I, R, SC> HasLastFoundTime for StdState<C, I, R, SC> {
#[inline]
fn last_found_time(&self) -> &Duration {
&self.last_found_time
}
#[inline]
fn last_found_time_mut(&mut self) -> &mut Duration {
&mut self.last_found_time
}
}
impl<C, I, R, SC> HasLastReportTime for StdState<C, I, 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<C, I, R, SC> HasMaxSize for StdState<C, I, 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<C, I, R, SC> HasStartTime for StdState<C, I, 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<C, I, R, SC> HasCurrentCorpusId for StdState<C, I, R, SC> {
fn set_corpus_id(&mut self, id: CorpusId) -> Result<(), Error> {
self.corpus_id = Some(id);
Ok(())
}
fn clear_corpus_id(&mut self) -> Result<(), Error> {
self.corpus_id = None;
Ok(())
}
fn current_corpus_id(&self) -> Result<Option<CorpusId>, Error> {
Ok(self.corpus_id)
}
}
pub trait HasCurrentTestcase<I>: HasCorpus<I> {
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
T: HasCorpus<I> + HasCurrentCorpusId,
I: Clone,
{
fn current_testcase(&self) -> Result<Ref<'_, Testcase<I>>, Error> {
let Some(corpus_id) = self.current_corpus_id()? 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_id()? 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())
}
}
pub trait Stoppable {
fn stop_requested(&self) -> bool;
fn request_stop(&mut self);
fn discard_stop_request(&mut self);
}
impl<C, I, R, SC> Stoppable for StdState<C, I, R, SC> {
fn request_stop(&mut self) {
self.stop_requested = true;
}
fn discard_stop_request(&mut self) {
self.stop_requested = false;
}
fn stop_requested(&self) -> bool {
self.stop_requested
}
}
impl<C, I, R, SC> HasCurrentStageId for StdState<C, I, R, SC> {
fn set_current_stage_id(&mut self, idx: StageId) -> Result<(), Error> {
self.stage_stack.set_current_stage_id(idx)
}
fn clear_stage_id(&mut self) -> Result<(), Error> {
self.stage_stack.clear_stage_id()
}
fn current_stage_id(&self) -> Result<Option<StageId>, Error> {
self.stage_stack.current_stage_id()
}
fn on_restart(&mut self) -> Result<(), Error> {
self.stage_stack.on_restart()
}
}
pub trait HasCurrentStageId {
fn set_current_stage_id(&mut self, id: StageId) -> Result<(), Error>;
fn clear_stage_id(&mut self) -> Result<(), Error>;
fn current_stage_id(&self) -> Result<Option<StageId>, Error>;
fn on_restart(&mut self) -> Result<(), Error> {
Ok(())
}
}
pub trait HasNestedStage: HasCurrentStageId {
fn enter_inner_stage(&mut self) -> Result<(), Error>;
fn exit_inner_stage(&mut self) -> Result<(), Error>;
}
impl<C, I, R, SC> HasNestedStage for StdState<C, I, R, SC> {
fn enter_inner_stage(&mut self) -> Result<(), Error> {
self.stage_stack.enter_inner_stage()
}
fn exit_inner_stage(&mut self) -> Result<(), Error> {
self.stage_stack.exit_inner_stage()
}
}
#[cfg(feature = "std")]
impl<C, I, R, SC> StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
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],
load_config: LoadConfig<I, Self, Z>,
) -> Result<(), Error>
where
EM: EventFirer<I, Self>,
Z: Evaluator<E, EM, I, 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, load_config)
}
fn load_file<E, EM, Z>(
&mut self,
path: &Path,
manager: &mut EM,
fuzzer: &mut Z,
executor: &mut E,
config: &mut LoadConfig<I, Self, Z>,
) -> Result<ExecuteInputResult, Error>
where
EM: EventFirer<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
log::info!("Loading file {} ...", path.display());
let input = match (config.loader)(fuzzer, self, path) {
Ok(input) => input,
Err(err) => {
log::error!(
"Skipping input that we could not load from {}: {err:?}",
path.display()
);
return Ok(ExecuteInputResult::None);
}
};
if config.forced {
let _ = fuzzer.add_input(self, executor, manager, input)?;
Ok(ExecuteInputResult::Corpus)
} else {
let (res, _) = fuzzer.evaluate_input(self, executor, manager, &input)?;
if res == ExecuteInputResult::None {
fuzzer.add_disabled_input(self, input)?;
log::warn!(
"Input {} was not interesting, adding as disabled.",
path.display()
);
}
Ok(res)
}
}
fn continue_loading_initial_inputs_custom<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
mut config: LoadConfig<I, Self, Z>,
) -> Result<(), Error>
where
EM: EventFirer<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
loop {
match self.next_file() {
Ok(path) => {
let res = self.load_file(&path, manager, fuzzer, executor, &mut config)?;
if config.exit_on_solution && matches!(res, ExecuteInputResult::Solution) {
return Err(Error::invalid_corpus(format!(
"Input {} resulted in a solution.",
path.display()
)));
}
}
Err(Error::IteratorEnd(_, _)) => break,
Err(e) => return Err(e),
}
}
manager.fire(
self,
EventWithStats::with_current_time(
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {} initial testcases.", self.corpus().count()), phantom: PhantomData::<I>,
},
*self.executions(),
),
)?;
Ok(())
}
pub fn walk_initial_inputs<F>(
&mut self,
in_dirs: &[PathBuf],
mut closure: F,
) -> Result<(), Error>
where
F: FnMut(&PathBuf) -> Result<(), Error>,
{
self.canonicalize_input_dirs(in_dirs)?;
loop {
match self.next_file() {
Ok(path) => {
closure(&path)?;
}
Err(Error::IteratorEnd(_, _)) => break,
Err(e) => return Err(e),
}
}
self.reset_initial_files_state();
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
EM: EventFirer<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
self.load_initial_inputs_custom_by_filenames(
fuzzer,
executor,
manager,
file_list,
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: false,
exit_on_solution: false,
},
)
}
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
EM: EventFirer<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
self.canonicalize_input_dirs(in_dirs)?;
self.continue_loading_initial_inputs_custom(
fuzzer,
executor,
manager,
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: true,
exit_on_solution: false,
},
)
}
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
EM: EventFirer<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
self.load_initial_inputs_custom_by_filenames(
fuzzer,
executor,
manager,
file_list,
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: true,
exit_on_solution: false,
},
)
}
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
EM: EventFirer<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
self.canonicalize_input_dirs(in_dirs)?;
self.continue_loading_initial_inputs_custom(
fuzzer,
executor,
manager,
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: false,
exit_on_solution: false,
},
)
}
pub fn load_initial_inputs_disallow_solution<E, EM, Z>(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
manager: &mut EM,
in_dirs: &[PathBuf],
) -> Result<(), Error>
where
EM: EventFirer<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
self.canonicalize_input_dirs(in_dirs)?;
self.continue_loading_initial_inputs_custom(
fuzzer,
executor,
manager,
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: false,
exit_on_solution: true,
},
)
}
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
EM: EventFirer<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
if self.multicore_inputs_processed.unwrap_or(false) {
self.continue_loading_initial_inputs_custom(
fuzzer,
executor,
manager,
LoadConfig {
loader: &mut |_, _, path| I::from_file(path),
forced: false,
exit_on_solution: false,
},
)?;
} 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 ({corpus_size}), no parallelism required.");
} 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<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
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
EM: EventFirer<I, Self>,
G: Generator<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
let mut added = 0;
for _ in 0..num {
let input = generator.generate(self)?;
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,
EventWithStats::with_current_time(
Event::Log {
severity_level: LogSeverity::Debug,
message: format!("Loaded {added} over {num} initial testcases"),
phantom: PhantomData,
},
*self.executions(),
),
)?;
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
EM: EventFirer<I, Self>,
G: Generator<I, Self>,
Z: Evaluator<E, EM, 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
EM: EventFirer<I, Self>,
G: Generator<I, Self>,
Z: Evaluator<E, EM, I, Self>,
{
self.generate_initial_internal(fuzzer, executor, generator, manager, num, false)
}
}
impl<C, I, R, SC> StdState<C, I, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
SC: Corpus<I>,
{
pub fn new<F, O>(
rand: R,
corpus: C,
solutions: SC,
feedback: &mut F,
objective: &mut O,
) -> Result<Self, Error>
where
F: StateInitializer<Self>,
O: StateInitializer<Self>,
C: Serialize + DeserializeOwned,
SC: Serialize + DeserializeOwned,
{
let mut state = Self {
rand,
executions: 0,
imported: 0,
start_time: libafl_bolts::current_time(),
metadata: SerdeAnyMap::default(),
named_metadata: NamedSerdeAnyMap::default(),
corpus,
solutions,
max_size: DEFAULT_MAX_SIZE,
stop_requested: false,
#[cfg(feature = "introspection")]
introspection_stats: ClientPerfStats::new(),
#[cfg(feature = "std")]
remaining_initial_files: None,
#[cfg(feature = "std")]
dont_reenter: None,
last_report_time: None,
last_found_time: libafl_bolts::current_time(),
corpus_id: None,
stage_stack: StageStack::default(),
phantom: PhantomData,
#[cfg(feature = "std")]
multicore_inputs_processed: None,
};
feedback.init_state(&mut state)?;
objective.init_state(&mut state)?;
Ok(state)
}
}
impl StdState<InMemoryCorpus<NopInput>, NopInput, StdRand, InMemoryCorpus<NopInput>> {
pub fn nop() -> Result<
StdState<InMemoryCorpus<NopInput>, NopInput, StdRand, InMemoryCorpus<NopInput>>,
Error,
> {
StdState::new(
StdRand::with_seed(0),
InMemoryCorpus::<NopInput>::new(),
InMemoryCorpus::new(),
&mut (),
&mut (),
)
}
}
#[cfg(feature = "introspection")]
impl<C, I, R, SC> HasClientPerfMonitor for StdState<C, I, R, SC> {
fn introspection_stats(&self) -> &ClientPerfStats {
&self.introspection_stats
}
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats {
&mut self.introspection_stats
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct NopState<I> {
metadata: SerdeAnyMap,
named_metadata: NamedSerdeAnyMap,
execution: u64,
stop_requested: bool,
rand: StdRand,
phantom: PhantomData<I>,
}
impl<I> NopState<I> {
#[must_use]
pub fn new() -> Self {
NopState {
metadata: SerdeAnyMap::new(),
named_metadata: NamedSerdeAnyMap::new(),
execution: 0,
rand: StdRand::default(),
stop_requested: false,
phantom: PhantomData,
}
}
}
impl<I> HasMaxSize for NopState<I> {
fn max_size(&self) -> usize {
16_384
}
fn set_max_size(&mut self, _max_size: usize) {
unimplemented!("NopState doesn't allow setting a max size")
}
}
impl<I> HasCorpus<I> for NopState<I> {
type Corpus = InMemoryCorpus<I>;
fn corpus(&self) -> &Self::Corpus {
unimplemented!("Unimplemented for NopState!");
}
fn corpus_mut(&mut self) -> &mut Self::Corpus {
unimplemented!("Unimplemented for No[State!");
}
}
impl<I> HasExecutions for NopState<I> {
fn executions(&self) -> &u64 {
&self.execution
}
fn executions_mut(&mut self) -> &mut u64 {
&mut self.execution
}
}
impl<I> Stoppable for NopState<I> {
fn request_stop(&mut self) {
self.stop_requested = true;
}
fn discard_stop_request(&mut self) {
self.stop_requested = false;
}
fn stop_requested(&self) -> bool {
self.stop_requested
}
}
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> HasNamedMetadata for NopState<I> {
fn named_metadata_map(&self) -> &NamedSerdeAnyMap {
&self.named_metadata
}
fn named_metadata_map_mut(&mut self) -> &mut NamedSerdeAnyMap {
&mut self.named_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> HasCurrentCorpusId for NopState<I> {
fn set_corpus_id(&mut self, _id: CorpusId) -> Result<(), Error> {
Ok(())
}
fn clear_corpus_id(&mut self) -> Result<(), Error> {
Ok(())
}
fn current_corpus_id(&self) -> Result<Option<CorpusId>, Error> {
Ok(None)
}
}
impl<I> HasCurrentStageId for NopState<I> {
fn set_current_stage_id(&mut self, _idx: StageId) -> Result<(), Error> {
Ok(())
}
fn clear_stage_id(&mut self) -> Result<(), Error> {
Ok(())
}
fn current_stage_id(&self) -> Result<Option<StageId>, Error> {
Ok(None)
}
}
#[cfg(feature = "introspection")]
impl<I> HasClientPerfMonitor for NopState<I> {
fn introspection_stats(&self) -> &ClientPerfStats {
unimplemented!();
}
fn introspection_stats_mut(&mut self) -> &mut ClientPerfStats {
unimplemented!();
}
}
#[cfg(test)]
mod test {
use crate::state::StdState;
#[test]
fn test_std_state() {
StdState::nop().expect("couldn't instantiate the test state");
}
}