Allow dyn in StagesTuple, add Current Testcase API, Untraitify Progress (#1915)
* Move into_vec to extra trait * fix no_std * First step towards stages tuples as vecs * Allow dyn in StagesTuple, add Current Testcase API, un-traitify Progress, cleanups * Move from generics to impl keyword, more replacements with better API * rename fn * Fix additional stages, more cleanup, rename progress to retries * Fix more fixes * Fixes * Rename ProgressHelper -> RestartHelper * Fix sugar, python, add perform_restartable * fixes * remove prelude bs * rename to restart_progress_should_run * more cleanup, remove tests I don't understand (sorry) * fix docs * more fix * fix miri * unsafe safety annotations * more comments * last docs * Mixed_attributes only allowed for bindgen
This commit is contained in:
parent
61046c4157
commit
dd410c590a
@ -6,11 +6,13 @@ edition = "2021"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
pyo3 = { version = "0.18.3", features = ["extension-module"] }
|
pyo3 = { version = "0.18.3", features = ["extension-module"] }
|
||||||
pyo3-log = "0.8.1"
|
pyo3-log = "0.8.1"
|
||||||
libafl_qemu = { path = "../../libafl_qemu", version = "0.11.2", features = ["python"] }
|
|
||||||
libafl_sugar = { path = "../../libafl_sugar", version = "0.11.2", features = ["python"] }
|
libafl_sugar = { path = "../../libafl_sugar", version = "0.11.2", features = ["python"] }
|
||||||
libafl = { path = "../../libafl", version = "0.11.2", features = ["python"] }
|
libafl = { path = "../../libafl", version = "0.11.2", features = ["python"] }
|
||||||
libafl_bolts = { path = "../../libafl_bolts", version = "0.11.2", features = ["python"] }
|
libafl_bolts = { path = "../../libafl_bolts", version = "0.11.2", features = ["python"] }
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
libafl_qemu = { path = "../../libafl_qemu", version = "0.11.2", features = ["python"] }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pyo3-build-config = { version = "0.17" }
|
pyo3-build-config = { version = "0.17" }
|
||||||
|
|
||||||
|
@ -1,8 +1,3 @@
|
|||||||
use libafl;
|
|
||||||
use libafl_bolts;
|
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
use libafl_qemu;
|
|
||||||
use libafl_sugar;
|
|
||||||
use pyo3::{prelude::*, types::PyDict};
|
use pyo3::{prelude::*, types::PyDict};
|
||||||
|
|
||||||
const LIBAFL_CODE: &str = r#"
|
const LIBAFL_CODE: &str = r#"
|
||||||
|
@ -57,7 +57,7 @@ pub fn main() -> Result<(), Error> {
|
|||||||
// RNG
|
// RNG
|
||||||
StdRand::with_seed(current_nanos()),
|
StdRand::with_seed(current_nanos()),
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
InMemoryOnDiskCorpus::new(&corpus_dir).unwrap(),
|
InMemoryOnDiskCorpus::new(corpus_dir).unwrap(),
|
||||||
// Corpus in which we store solutions (crashes in this example),
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
OnDiskCorpus::new(&solution_dir).unwrap(),
|
OnDiskCorpus::new(&solution_dir).unwrap(),
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
use std::ptr::write_volatile;
|
use std::ptr::write_volatile;
|
||||||
use std::{fs, io::Read, path::PathBuf, ptr::write};
|
use std::{fs, io::Read, path::PathBuf};
|
||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{InMemoryCorpus, OnDiskCorpus},
|
corpus::{InMemoryCorpus, OnDiskCorpus},
|
||||||
|
@ -11,7 +11,7 @@ use libafl::{
|
|||||||
mutators::{havoc_mutations, StdScheduledMutator},
|
mutators::{havoc_mutations, StdScheduledMutator},
|
||||||
observers::StdMapObserver,
|
observers::StdMapObserver,
|
||||||
schedulers::QueueScheduler,
|
schedulers::QueueScheduler,
|
||||||
stages::StdMutationalStage,
|
stages::{ExecutionCountRestartHelperMetadata, StdMutationalStage},
|
||||||
state::{HasSolutions, StdState},
|
state::{HasSolutions, StdState},
|
||||||
Fuzzer, StdFuzzer,
|
Fuzzer, StdFuzzer,
|
||||||
};
|
};
|
||||||
@ -23,7 +23,7 @@ use web_sys::{Performance, Window};
|
|||||||
|
|
||||||
use crate::utils::set_panic_hook;
|
use crate::utils::set_panic_hook;
|
||||||
|
|
||||||
// defined for internal use by libafl
|
// Defined for internal use by LibAFL
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||||
pub extern "C" fn external_current_millis() -> u64 {
|
pub extern "C" fn external_current_millis() -> u64 {
|
||||||
@ -39,8 +39,14 @@ pub extern "C" fn external_current_millis() -> u64 {
|
|||||||
pub fn fuzz() {
|
pub fn fuzz() {
|
||||||
set_panic_hook();
|
set_panic_hook();
|
||||||
|
|
||||||
|
// We need to register the types as LibAFL doesn't support `SerdeAny`
|
||||||
|
// auto registration in non-standard environments.
|
||||||
|
//
|
||||||
|
// # Safety
|
||||||
|
// No concurrency in WASM so these accesses are not racing.
|
||||||
unsafe {
|
unsafe {
|
||||||
RegistryBuilder::register::<MapFeedbackMetadata<u8>>();
|
RegistryBuilder::register::<MapFeedbackMetadata<u8>>();
|
||||||
|
RegistryBuilder::register::<ExecutionCountRestartHelperMetadata>();
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut signals = [0u8; 64];
|
let mut signals = [0u8; 64];
|
||||||
|
@ -9,7 +9,7 @@ use std::{
|
|||||||
|
|
||||||
use clap::{Arg, ArgAction, Command};
|
use clap::{Arg, ArgAction, Command};
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, HasCurrentCorpusIdx, InMemoryOnDiskCorpus, OnDiskCorpus},
|
corpus::{Corpus, InMemoryOnDiskCorpus, OnDiskCorpus},
|
||||||
events::SimpleEventManager,
|
events::SimpleEventManager,
|
||||||
executors::forkserver::ForkserverExecutor,
|
executors::forkserver::ForkserverExecutor,
|
||||||
feedback_or,
|
feedback_or,
|
||||||
@ -29,7 +29,7 @@ use libafl::{
|
|||||||
calibrate::CalibrationStage, mutational::MultiMutationalStage,
|
calibrate::CalibrationStage, mutational::MultiMutationalStage,
|
||||||
power::StdPowerMutationalStage, ColorizationStage, IfStage,
|
power::StdPowerMutationalStage, ColorizationStage, IfStage,
|
||||||
},
|
},
|
||||||
state::{HasCorpus, HasMetadata, StdState},
|
state::{HasCorpus, HasCurrentTestcase, HasMetadata, StdState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
use libafl_bolts::{
|
use libafl_bolts::{
|
||||||
@ -373,14 +373,8 @@ fn fuzz(
|
|||||||
state: &mut StdState<_, InMemoryOnDiskCorpus<_>, _, _>,
|
state: &mut StdState<_, InMemoryOnDiskCorpus<_>, _, _>,
|
||||||
_event_manager: &mut _|
|
_event_manager: &mut _|
|
||||||
-> Result<bool, Error> {
|
-> Result<bool, Error> {
|
||||||
let Some(corpus_id) = state.current_corpus_idx()? else {
|
let testcase = state.current_testcase()?;
|
||||||
return Err(Error::illegal_state(
|
let res = testcase.scheduled_count() == 1; // let's try on the 2nd trial
|
||||||
"state is not currently processing a corpus index",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
let corpus = state.corpus().get(corpus_id)?.borrow();
|
|
||||||
let res = corpus.scheduled_count() == 1; // let's try on the 2nd trial
|
|
||||||
|
|
||||||
Ok(res)
|
Ok(res)
|
||||||
};
|
};
|
||||||
|
@ -8,12 +8,11 @@ use std::{env, fs::DirEntry, io, path::PathBuf, process};
|
|||||||
use clap::{builder::Str, Parser};
|
use clap::{builder::Str, Parser};
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, NopCorpus},
|
corpus::{Corpus, NopCorpus},
|
||||||
events::{launcher::Launcher, EventConfig, EventRestarter},
|
events::{launcher::Launcher, EventConfig, EventRestarter, LlmpRestartingEventManager},
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
fuzzer::StdFuzzer,
|
fuzzer::StdFuzzer,
|
||||||
inputs::{BytesInput, HasTargetBytes},
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
monitors::MultiMonitor,
|
monitors::MultiMonitor,
|
||||||
prelude::LlmpRestartingEventManager,
|
|
||||||
schedulers::QueueScheduler,
|
schedulers::QueueScheduler,
|
||||||
state::{HasCorpus, StdState},
|
state::{HasCorpus, StdState},
|
||||||
Error,
|
Error,
|
||||||
|
@ -42,7 +42,7 @@ pub struct OnDiskMetadata<'a> {
|
|||||||
/// The exec time for this [`Testcase`]
|
/// The exec time for this [`Testcase`]
|
||||||
pub exec_time: &'a Option<Duration>,
|
pub exec_time: &'a Option<Duration>,
|
||||||
/// The amount of executions for this [`Testcase`]
|
/// The amount of executions for this [`Testcase`]
|
||||||
pub executions: &'a usize,
|
pub executions: &'a u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A corpus able to store [`Testcase`]s to disk, and load them from disk, when they are being used.
|
/// A corpus able to store [`Testcase`]s to disk, and load them from disk, when they are being used.
|
||||||
|
@ -59,7 +59,7 @@ where
|
|||||||
/// Cached len of the input, if any
|
/// Cached len of the input, if any
|
||||||
cached_len: Option<usize>,
|
cached_len: Option<usize>,
|
||||||
/// Number of executions done at discovery time
|
/// Number of executions done at discovery time
|
||||||
executions: usize,
|
executions: u64,
|
||||||
/// Number of fuzzing iterations of this particular input updated in `perform_mutational`
|
/// Number of fuzzing iterations of this particular input updated in `perform_mutational`
|
||||||
scheduled_count: usize,
|
scheduled_count: usize,
|
||||||
/// Parent [`CorpusId`], if known
|
/// Parent [`CorpusId`], if known
|
||||||
@ -174,13 +174,13 @@ where
|
|||||||
|
|
||||||
/// Get the executions
|
/// Get the executions
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn executions(&self) -> &usize {
|
pub fn executions(&self) -> &u64 {
|
||||||
&self.executions
|
&self.executions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get the executions (mutable)
|
/// Get the executions (mutable)
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn executions_mut(&mut self) -> &mut usize {
|
pub fn executions_mut(&mut self) -> &mut u64 {
|
||||||
&mut self.executions
|
&mut self.executions
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -258,7 +258,7 @@ where
|
|||||||
|
|
||||||
/// Create a new Testcase instance given an [`Input`] and the number of executions
|
/// Create a new Testcase instance given an [`Input`] and the number of executions
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn with_executions(mut input: I, executions: usize) -> Self {
|
pub fn with_executions(mut input: I, executions: u64) -> Self {
|
||||||
input.wrapped_as_testcase();
|
input.wrapped_as_testcase();
|
||||||
Self {
|
Self {
|
||||||
input: Some(input),
|
input: Some(input),
|
||||||
@ -541,7 +541,7 @@ pub mod pybind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[getter]
|
#[getter]
|
||||||
fn executions(&self) -> usize {
|
fn executions(&self) -> u64 {
|
||||||
*self.inner.as_ref().executions()
|
*self.inner.as_ref().executions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -251,7 +251,7 @@ where
|
|||||||
if id == client_id {
|
if id == client_id {
|
||||||
// do not update executions for forwarded messages, otherwise we loose the total order
|
// do not update executions for forwarded messages, otherwise we loose the total order
|
||||||
// as a forwarded msg with a lower executions may arrive after a stats msg with an higher executions
|
// as a forwarded msg with a lower executions may arrive after a stats msg with an higher executions
|
||||||
client.update_executions(*executions as u64, *time);
|
client.update_executions(*executions, *time);
|
||||||
}
|
}
|
||||||
monitor.display(event.name(), id);
|
monitor.display(event.name(), id);
|
||||||
Ok(BrokerEventResult::Forward)
|
Ok(BrokerEventResult::Forward)
|
||||||
@ -264,7 +264,7 @@ where
|
|||||||
// TODO: The monitor buffer should be added on client add.
|
// TODO: The monitor buffer should be added on client add.
|
||||||
monitor.client_stats_insert(client_id);
|
monitor.client_stats_insert(client_id);
|
||||||
let client = monitor.client_stats_mut_for(client_id);
|
let client = monitor.client_stats_mut_for(client_id);
|
||||||
client.update_executions(*executions as u64, *time);
|
client.update_executions(*executions, *time);
|
||||||
monitor.display(event.name(), client_id);
|
monitor.display(event.name(), client_id);
|
||||||
Ok(BrokerEventResult::Handled)
|
Ok(BrokerEventResult::Handled)
|
||||||
}
|
}
|
||||||
@ -294,7 +294,7 @@ where
|
|||||||
let client = monitor.client_stats_mut_for(client_id);
|
let client = monitor.client_stats_mut_for(client_id);
|
||||||
|
|
||||||
// Update the normal monitor for this client
|
// Update the normal monitor for this client
|
||||||
client.update_executions(*executions as u64, *time);
|
client.update_executions(*executions, *time);
|
||||||
|
|
||||||
// Update the performance monitor for this client
|
// Update the performance monitor for this client
|
||||||
client.update_introspection_monitor((**introspection_monitor).clone());
|
client.update_introspection_monitor((**introspection_monitor).clone());
|
||||||
|
@ -296,7 +296,7 @@ where
|
|||||||
/// The time of generation of the event
|
/// The time of generation of the event
|
||||||
time: Duration,
|
time: Duration,
|
||||||
/// The executions of this client
|
/// The executions of this client
|
||||||
executions: usize,
|
executions: u64,
|
||||||
/// The original sender if, if forwarded
|
/// The original sender if, if forwarded
|
||||||
forward_id: Option<ClientId>,
|
forward_id: Option<ClientId>,
|
||||||
},
|
},
|
||||||
@ -305,7 +305,7 @@ where
|
|||||||
/// The time of generation of the [`Event`]
|
/// The time of generation of the [`Event`]
|
||||||
time: Duration,
|
time: Duration,
|
||||||
/// The executions of this client
|
/// The executions of this client
|
||||||
executions: usize,
|
executions: u64,
|
||||||
/// [`PhantomData`]
|
/// [`PhantomData`]
|
||||||
phantom: PhantomData<I>,
|
phantom: PhantomData<I>,
|
||||||
},
|
},
|
||||||
@ -324,7 +324,7 @@ where
|
|||||||
/// The time of generation of the event
|
/// The time of generation of the event
|
||||||
time: Duration,
|
time: Duration,
|
||||||
/// The executions of this client
|
/// The executions of this client
|
||||||
executions: usize,
|
executions: u64,
|
||||||
/// Current performance statistics
|
/// Current performance statistics
|
||||||
introspection_monitor: Box<ClientPerfMonitor>,
|
introspection_monitor: Box<ClientPerfMonitor>,
|
||||||
|
|
||||||
|
@ -216,7 +216,7 @@ where
|
|||||||
.update_corpus_size(*corpus_size as u64);
|
.update_corpus_size(*corpus_size as u64);
|
||||||
monitor
|
monitor
|
||||||
.client_stats_mut_for(ClientId(0))
|
.client_stats_mut_for(ClientId(0))
|
||||||
.update_executions(*executions as u64, *time);
|
.update_executions(*executions, *time);
|
||||||
monitor.display(event.name(), ClientId(0));
|
monitor.display(event.name(), ClientId(0));
|
||||||
Ok(BrokerEventResult::Handled)
|
Ok(BrokerEventResult::Handled)
|
||||||
}
|
}
|
||||||
@ -229,7 +229,7 @@ where
|
|||||||
monitor.client_stats_insert(ClientId(0));
|
monitor.client_stats_insert(ClientId(0));
|
||||||
let client = monitor.client_stats_mut_for(ClientId(0));
|
let client = monitor.client_stats_mut_for(ClientId(0));
|
||||||
|
|
||||||
client.update_executions(*executions as u64, *time);
|
client.update_executions(*executions, *time);
|
||||||
|
|
||||||
monitor.display(event.name(), ClientId(0));
|
monitor.display(event.name(), ClientId(0));
|
||||||
Ok(BrokerEventResult::Handled)
|
Ok(BrokerEventResult::Handled)
|
||||||
@ -257,7 +257,7 @@ where
|
|||||||
// TODO: The monitor buffer should be added on client add.
|
// TODO: The monitor buffer should be added on client add.
|
||||||
monitor.client_stats_insert(ClientId(0));
|
monitor.client_stats_insert(ClientId(0));
|
||||||
let client = monitor.client_stats_mut_for(ClientId(0));
|
let client = monitor.client_stats_mut_for(ClientId(0));
|
||||||
client.update_executions(*executions as u64, *time);
|
client.update_executions(*executions, *time);
|
||||||
client.update_introspection_monitor((**introspection_monitor).clone());
|
client.update_introspection_monitor((**introspection_monitor).clone());
|
||||||
monitor.display(event.name(), ClientId(0));
|
monitor.display(event.name(), ClientId(0));
|
||||||
Ok(BrokerEventResult::Handled)
|
Ok(BrokerEventResult::Handled)
|
||||||
|
@ -333,7 +333,7 @@ where
|
|||||||
monitor.client_stats_insert(id);
|
monitor.client_stats_insert(id);
|
||||||
let client = monitor.client_stats_mut_for(id);
|
let client = monitor.client_stats_mut_for(id);
|
||||||
client.update_corpus_size(*corpus_size as u64);
|
client.update_corpus_size(*corpus_size as u64);
|
||||||
client.update_executions(*executions as u64, *time);
|
client.update_executions(*executions, *time);
|
||||||
monitor.display(event.name(), id);
|
monitor.display(event.name(), id);
|
||||||
Ok(BrokerEventResult::Forward)
|
Ok(BrokerEventResult::Forward)
|
||||||
}
|
}
|
||||||
@ -345,7 +345,7 @@ where
|
|||||||
// TODO: The monitor buffer should be added on client add.
|
// TODO: The monitor buffer should be added on client add.
|
||||||
monitor.client_stats_insert(client_id);
|
monitor.client_stats_insert(client_id);
|
||||||
let client = monitor.client_stats_mut_for(client_id);
|
let client = monitor.client_stats_mut_for(client_id);
|
||||||
client.update_executions(*executions as u64, *time);
|
client.update_executions(*executions, *time);
|
||||||
monitor.display(event.name(), client_id);
|
monitor.display(event.name(), client_id);
|
||||||
Ok(BrokerEventResult::Handled)
|
Ok(BrokerEventResult::Handled)
|
||||||
}
|
}
|
||||||
@ -375,7 +375,7 @@ where
|
|||||||
let client = monitor.client_stats_mut_for(client_id);
|
let client = monitor.client_stats_mut_for(client_id);
|
||||||
|
|
||||||
// Update the normal monitor for this client
|
// Update the normal monitor for this client
|
||||||
client.update_executions(*executions as u64, *time);
|
client.update_executions(*executions, *time);
|
||||||
|
|
||||||
// Update the performance monitor for this client
|
// Update the performance monitor for this client
|
||||||
client.update_introspection_monitor((**introspection_monitor).clone());
|
client.update_introspection_monitor((**introspection_monitor).clone());
|
||||||
|
@ -198,7 +198,7 @@ impl ExecutorHook for InProcessHooks {
|
|||||||
(*data).timeout_handler = self.timeout_handler;
|
(*data).timeout_handler = self.timeout_handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(all(feature = "std", not(all(miri, target_vendor = "apple"))))]
|
||||||
self.timer_mut().set_timer();
|
self.timer_mut().set_timer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -212,7 +212,7 @@ impl ExecutorHook for InProcessHooks {
|
|||||||
_input: &I,
|
_input: &I,
|
||||||
) {
|
) {
|
||||||
// timeout stuff
|
// timeout stuff
|
||||||
#[cfg(feature = "std")]
|
#[cfg(all(feature = "std", not(all(miri, target_vendor = "apple"))))]
|
||||||
self.timer_mut().unset_timer();
|
self.timer_mut().unset_timer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,7 @@ where
|
|||||||
.field("observers", &self.observers)
|
.field("observers", &self.observers)
|
||||||
.field("shmem_provider", &self.shmem_provider)
|
.field("shmem_provider", &self.shmem_provider)
|
||||||
.field("itimerspec", &self.itimerspec)
|
.field("itimerspec", &self.itimerspec)
|
||||||
.finish()
|
.finish_non_exhaustive()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
@ -81,7 +81,7 @@ where
|
|||||||
.field("observers", &self.observers)
|
.field("observers", &self.observers)
|
||||||
.field("shmem_provider", &self.shmem_provider)
|
.field("shmem_provider", &self.shmem_provider)
|
||||||
.field("itimerval", &self.itimerval)
|
.field("itimerval", &self.itimerval)
|
||||||
.finish();
|
.finish_non_exhaustive();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -278,7 +278,7 @@ where
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Creates a new [`GenericInProcessForkExecutor`], non linux
|
/// Creates a new [`GenericInProcessForkExecutorInner`], non linux
|
||||||
#[cfg(not(target_os = "linux"))]
|
#[cfg(not(target_os = "linux"))]
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub fn with_hooks(
|
pub fn with_hooks(
|
||||||
|
@ -401,7 +401,7 @@ where
|
|||||||
fn init_state(&mut self, state: &mut S) -> Result<(), Error> {
|
fn init_state(&mut self, state: &mut S) -> Result<(), Error> {
|
||||||
// Initialize `MapFeedbackMetadata` with an empty vector and add it to the state.
|
// Initialize `MapFeedbackMetadata` with an empty vector and add it to the state.
|
||||||
// The `MapFeedbackMetadata` would be resized on-demand in `is_interesting`
|
// The `MapFeedbackMetadata` would be resized on-demand in `is_interesting`
|
||||||
state.add_named_metadata(MapFeedbackMetadata::<T>::default(), &self.name);
|
state.add_named_metadata(&self.name, MapFeedbackMetadata::<T>::default());
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,8 +92,8 @@ where
|
|||||||
{
|
{
|
||||||
fn init_state(&mut self, state: &mut S) -> Result<(), Error> {
|
fn init_state(&mut self, state: &mut S) -> Result<(), Error> {
|
||||||
state.add_named_metadata(
|
state.add_named_metadata(
|
||||||
NewHashFeedbackMetadata::with_capacity(self.capacity),
|
|
||||||
&self.name,
|
&self.name,
|
||||||
|
NewHashFeedbackMetadata::with_capacity(self.capacity),
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -135,8 +135,12 @@ pub unsafe extern "C" fn external_current_millis() -> u64 {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
|
||||||
|
#[cfg(miri)]
|
||||||
|
use libafl_bolts::serdeany::RegistryBuilder;
|
||||||
use libafl_bolts::{rands::StdRand, tuples::tuple_list};
|
use libafl_bolts::{rands::StdRand, tuples::tuple_list};
|
||||||
|
|
||||||
|
#[cfg(miri)]
|
||||||
|
use crate::stages::ExecutionCountRestartHelperMetadata;
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, InMemoryCorpus, Testcase},
|
corpus::{Corpus, InMemoryCorpus, Testcase},
|
||||||
events::NopEventManager,
|
events::NopEventManager,
|
||||||
@ -155,6 +159,13 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
#[allow(clippy::similar_names)]
|
#[allow(clippy::similar_names)]
|
||||||
fn test_fuzzer() {
|
fn test_fuzzer() {
|
||||||
|
// # Safety
|
||||||
|
// No concurrency per testcase
|
||||||
|
#[cfg(miri)]
|
||||||
|
unsafe {
|
||||||
|
RegistryBuilder::register::<ExecutionCountRestartHelperMetadata>();
|
||||||
|
}
|
||||||
|
|
||||||
let rand = StdRand::with_seed(0);
|
let rand = StdRand::with_seed(0);
|
||||||
|
|
||||||
let mut corpus = InMemoryCorpus::<BytesInput>::new();
|
let mut corpus = InMemoryCorpus::<BytesInput>::new();
|
||||||
|
@ -381,6 +381,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tuning() {
|
fn test_tuning() {
|
||||||
|
// # Safety
|
||||||
|
// No concurrency per testcase
|
||||||
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
||||||
unsafe {
|
unsafe {
|
||||||
TuneableScheduledMutatorMetadata::register();
|
TuneableScheduledMutatorMetadata::register();
|
||||||
@ -404,6 +406,8 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mutation_distribution() {
|
fn test_mutation_distribution() {
|
||||||
|
// # Safety
|
||||||
|
// No concurrency per testcase
|
||||||
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
||||||
unsafe {
|
unsafe {
|
||||||
TuneableScheduledMutatorMetadata::register();
|
TuneableScheduledMutatorMetadata::register();
|
||||||
|
@ -232,12 +232,7 @@ where
|
|||||||
S: HasMetadata,
|
S: HasMetadata,
|
||||||
{
|
{
|
||||||
#[allow(clippy::option_if_let_else)] // we can't mutate state in a closure
|
#[allow(clippy::option_if_let_else)] // we can't mutate state in a closure
|
||||||
let meta = if let Some(meta) = state.metadata_map_mut().get_mut::<M>() {
|
let meta = state.metadata_or_insert_with(|| M::new_metadata());
|
||||||
meta
|
|
||||||
} else {
|
|
||||||
state.add_metadata(M::new_metadata());
|
|
||||||
state.metadata_map_mut().get_mut::<M>().unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
let usable_count = self.usable_count();
|
let usable_count = self.usable_count();
|
||||||
let cmp_observer_data = self.cmp_observer_data();
|
let cmp_observer_data = self.cmp_observer_data();
|
||||||
|
@ -228,6 +228,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_prob_sampling() {
|
fn test_prob_sampling() {
|
||||||
|
// # Safety
|
||||||
|
// No concurrency per testcase
|
||||||
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
||||||
unsafe {
|
unsafe {
|
||||||
super::ProbabilityMetadata::register();
|
super::ProbabilityMetadata::register();
|
||||||
|
@ -114,13 +114,9 @@ where
|
|||||||
/// Create a new [`WeightedScheduler`]
|
/// Create a new [`WeightedScheduler`]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_schedule(state: &mut S, map_observer: &O, strat: Option<PowerSchedule>) -> Self {
|
pub fn with_schedule(state: &mut S, map_observer: &O, strat: Option<PowerSchedule>) -> Self {
|
||||||
if !state.has_metadata::<SchedulerMetadata>() {
|
let _ = state.metadata_or_insert_with(|| SchedulerMetadata::new(strat));
|
||||||
state.add_metadata(SchedulerMetadata::new(strat));
|
let _ = state.metadata_or_insert_with(WeightedScheduleMetadata::new);
|
||||||
}
|
|
||||||
|
|
||||||
if !state.has_metadata::<WeightedScheduleMetadata>() {
|
|
||||||
state.add_metadata(WeightedScheduleMetadata::new());
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
strat,
|
strat,
|
||||||
map_observer_name: map_observer.name().to_string(),
|
map_observer_name: map_observer.name().to_string(),
|
||||||
|
@ -12,7 +12,7 @@ use num_traits::Bounded;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, HasCurrentCorpusIdx, SchedulerTestcaseMetadata},
|
corpus::{Corpus, SchedulerTestcaseMetadata},
|
||||||
events::{Event, EventFirer, LogSeverity},
|
events::{Event, EventFirer, LogSeverity},
|
||||||
executors::{Executor, ExitKind, HasObservers},
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
feedbacks::{map::MapFeedbackMetadata, HasObserverName},
|
feedbacks::{map::MapFeedbackMetadata, HasObserverName},
|
||||||
@ -20,8 +20,11 @@ use crate::{
|
|||||||
monitors::{AggregatorOps, UserStats, UserStatsValue},
|
monitors::{AggregatorOps, UserStats, UserStatsValue},
|
||||||
observers::{MapObserver, ObserversTuple, UsesObserver},
|
observers::{MapObserver, ObserversTuple, UsesObserver},
|
||||||
schedulers::powersched::SchedulerMetadata,
|
schedulers::powersched::SchedulerMetadata,
|
||||||
stages::Stage,
|
stages::{ExecutionCountRestartHelper, Stage},
|
||||||
state::{HasCorpus, HasExecutions, HasMetadata, HasNamedMetadata, State, UsesState},
|
state::{
|
||||||
|
HasCorpus, HasCurrentTestcase, HasExecutions, HasMetadata, HasNamedMetadata, State,
|
||||||
|
UsesState,
|
||||||
|
},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,7 +71,9 @@ pub struct CalibrationStage<O, OT, S> {
|
|||||||
map_observer_name: String,
|
map_observer_name: String,
|
||||||
map_name: String,
|
map_name: String,
|
||||||
stage_max: usize,
|
stage_max: usize,
|
||||||
|
/// If we should track stability
|
||||||
track_stability: bool,
|
track_stability: bool,
|
||||||
|
restart_helper: ExecutionCountRestartHelper,
|
||||||
phantom: PhantomData<(O, OT, S)>,
|
phantom: PhantomData<(O, OT, S)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,8 +97,6 @@ where
|
|||||||
E::State: HasCorpus + HasMetadata + HasNamedMetadata + HasExecutions,
|
E::State: HasCorpus + HasMetadata + HasNamedMetadata + HasExecutions,
|
||||||
Z: Evaluator<E, EM, State = E::State>,
|
Z: Evaluator<E, EM, State = E::State>,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO stage may be resumed, but how?
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(
|
#[allow(
|
||||||
clippy::let_and_return,
|
clippy::let_and_return,
|
||||||
@ -107,25 +110,19 @@ where
|
|||||||
state: &mut E::State,
|
state: &mut E::State,
|
||||||
mgr: &mut EM,
|
mgr: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
|
||||||
return Err(Error::illegal_state(
|
|
||||||
"state is not currently processing a corpus index",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Run this stage only once for each corpus entry and only if we haven't already inspected it
|
// Run this stage only once for each corpus entry and only if we haven't already inspected it
|
||||||
{
|
{
|
||||||
let corpus = state.corpus().get(corpus_idx)?.borrow();
|
let testcase = state.current_testcase()?;
|
||||||
// println!("calibration; corpus.scheduled_count() : {}", corpus.scheduled_count());
|
// println!("calibration; corpus.scheduled_count() : {}", corpus.scheduled_count());
|
||||||
|
|
||||||
if corpus.scheduled_count() > 0 {
|
if testcase.scheduled_count() > 0 {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut iter = self.stage_max;
|
let mut iter = self.stage_max;
|
||||||
|
|
||||||
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
let input = state.current_input_cloned()?;
|
||||||
|
|
||||||
// Run once to get the initial calibration map
|
// Run once to get the initial calibration map
|
||||||
executor.observers_mut().pre_exec_all(state, &input)?;
|
executor.observers_mut().pre_exec_all(state, &input)?;
|
||||||
@ -162,8 +159,11 @@ where
|
|||||||
let mut i = 1;
|
let mut i = 1;
|
||||||
let mut has_errors = false;
|
let mut has_errors = false;
|
||||||
|
|
||||||
|
// If we restarted after a timeout or crash, do less iterations.
|
||||||
|
iter -= usize::try_from(self.restart_helper.execs_since_progress_start(state)?)?;
|
||||||
|
|
||||||
while i < iter {
|
while i < iter {
|
||||||
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
let input = state.current_input_cloned()?;
|
||||||
|
|
||||||
executor.observers_mut().pre_exec_all(state, &input)?;
|
executor.observers_mut().pre_exec_all(state, &input)?;
|
||||||
start = current_time();
|
start = current_time();
|
||||||
@ -268,7 +268,7 @@ where
|
|||||||
psmeta.set_bitmap_size_log(psmeta.bitmap_size_log() + libm::log2(bitmap_size as f64));
|
psmeta.set_bitmap_size_log(psmeta.bitmap_size_log() + libm::log2(bitmap_size as f64));
|
||||||
psmeta.set_bitmap_entries(psmeta.bitmap_entries() + 1);
|
psmeta.set_bitmap_entries(psmeta.bitmap_entries() + 1);
|
||||||
|
|
||||||
let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut();
|
let mut testcase = state.current_testcase_mut()?;
|
||||||
|
|
||||||
testcase.set_exec_time(total_time / (iter as u32));
|
testcase.set_exec_time(total_time / (iter as u32));
|
||||||
// log::trace!("time: {:#?}", testcase.exec_time());
|
// log::trace!("time: {:#?}", testcase.exec_time());
|
||||||
@ -301,7 +301,7 @@ where
|
|||||||
data.set_handicap(handicap);
|
data.set_handicap(handicap);
|
||||||
}
|
}
|
||||||
|
|
||||||
*state.executions_mut() += i;
|
*state.executions_mut() += u64::try_from(i).unwrap();
|
||||||
|
|
||||||
// Send the stability event to the broker
|
// Send the stability event to the broker
|
||||||
if unstable_found {
|
if unstable_found {
|
||||||
@ -327,6 +327,16 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// TODO: Make sure this is the correct way / there may be a better way?
|
||||||
|
self.restart_helper.restart_progress_should_run(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
// TODO: Make sure this is the correct way / there may be a better way?
|
||||||
|
self.restart_helper.clear_restart_progress(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<O, OT, S> CalibrationStage<O, OT, S>
|
impl<O, OT, S> CalibrationStage<O, OT, S>
|
||||||
@ -347,6 +357,7 @@ where
|
|||||||
map_name: map_feedback.name().to_string(),
|
map_name: map_feedback.name().to_string(),
|
||||||
stage_max: CAL_STAGE_START,
|
stage_max: CAL_STAGE_START,
|
||||||
track_stability: true,
|
track_stability: true,
|
||||||
|
restart_helper: ExecutionCountRestartHelper::default(),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -363,6 +374,7 @@ where
|
|||||||
map_name: map_feedback.name().to_string(),
|
map_name: map_feedback.name().to_string(),
|
||||||
stage_max: CAL_STAGE_START,
|
stage_max: CAL_STAGE_START,
|
||||||
track_stability: false,
|
track_stability: false,
|
||||||
|
restart_helper: ExecutionCountRestartHelper::default(),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,18 +6,17 @@ use alloc::{
|
|||||||
};
|
};
|
||||||
use core::{cmp::Ordering, fmt::Debug, marker::PhantomData, ops::Range};
|
use core::{cmp::Ordering, fmt::Debug, marker::PhantomData, ops::Range};
|
||||||
|
|
||||||
use libafl_bolts::{rands::Rand, tuples::MatchName};
|
use libafl_bolts::{rands::Rand, tuples::MatchName, Named};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
|
||||||
events::EventFirer,
|
events::EventFirer,
|
||||||
executors::{Executor, HasObservers},
|
executors::{Executor, HasObservers},
|
||||||
inputs::HasBytesVec,
|
inputs::HasBytesVec,
|
||||||
mutators::mutations::buffer_copy,
|
mutators::mutations::buffer_copy,
|
||||||
observers::{MapObserver, ObserversTuple},
|
observers::{MapObserver, ObserversTuple},
|
||||||
stages::Stage,
|
stages::{RetryRestartHelper, Stage},
|
||||||
state::{HasCorpus, HasMetadata, HasRand, UsesState},
|
state::{HasCorpus, HasCurrentTestcase, HasMetadata, HasNamedMetadata, HasRand, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -68,17 +67,24 @@ where
|
|||||||
type State = E::State;
|
type State = E::State;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<EM, O, E, Z> Named for ColorizationStage<EM, O, E, Z>
|
||||||
|
where
|
||||||
|
E: UsesState,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.map_observer_name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E, EM, O, Z> Stage<E, EM, Z> for ColorizationStage<EM, O, E, Z>
|
impl<E, EM, O, Z> Stage<E, EM, Z> for ColorizationStage<EM, O, E, Z>
|
||||||
where
|
where
|
||||||
EM: UsesState<State = E::State> + EventFirer,
|
EM: UsesState<State = E::State> + EventFirer,
|
||||||
E: HasObservers + Executor<EM, Z>,
|
E: HasObservers + Executor<EM, Z>,
|
||||||
E::State: HasCorpus + HasMetadata + HasRand,
|
E::State: HasCorpus + HasMetadata + HasRand + HasNamedMetadata,
|
||||||
E::Input: HasBytesVec,
|
E::Input: HasBytesVec,
|
||||||
O: MapObserver,
|
O: MapObserver,
|
||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO this stage needs resume
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::let_and_return)]
|
#[allow(clippy::let_and_return)]
|
||||||
fn perform(
|
fn perform(
|
||||||
@ -93,6 +99,16 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// TODO this stage needs a proper resume
|
||||||
|
RetryRestartHelper::restart_progress_should_run(state, self, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
// TODO this stage needs a proper resume
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store the taint and the input
|
/// Store the taint and the input
|
||||||
@ -152,13 +168,7 @@ where
|
|||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
name: &str,
|
name: &str,
|
||||||
) -> Result<E::Input, Error> {
|
) -> Result<E::Input, Error> {
|
||||||
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
let mut input = state.current_input_cloned()?;
|
||||||
return Err(Error::illegal_state(
|
|
||||||
"state is not currently processing a corpus index",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
|
||||||
// The backup of the input
|
// The backup of the input
|
||||||
let backup = input.clone();
|
let backup = input.clone();
|
||||||
// This is the buffer we'll randomly mutate during type_replace
|
// This is the buffer we'll randomly mutate during type_replace
|
||||||
|
@ -4,23 +4,23 @@
|
|||||||
|
|
||||||
use alloc::string::String;
|
use alloc::string::String;
|
||||||
#[cfg(feature = "concolic_mutation")]
|
#[cfg(feature = "concolic_mutation")]
|
||||||
use alloc::{borrow::ToOwned, string::ToString, vec::Vec};
|
use alloc::{string::ToString, vec::Vec};
|
||||||
|
#[cfg(feature = "concolic_mutation")]
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use libafl_bolts::tuples::MatchName;
|
use libafl_bolts::{tuples::MatchName, Named};
|
||||||
|
|
||||||
use super::{RetryProgress, RetryingStage, Stage, TracingStage};
|
|
||||||
#[cfg(all(feature = "concolic_mutation", feature = "introspection"))]
|
#[cfg(all(feature = "concolic_mutation", feature = "introspection"))]
|
||||||
use crate::monitors::PerfFeature;
|
use crate::monitors::PerfFeature;
|
||||||
#[cfg(all(feature = "introspection", feature = "concolic_mutation"))]
|
#[cfg(all(feature = "introspection", feature = "concolic_mutation"))]
|
||||||
use crate::state::HasClientPerfMonitor;
|
use crate::state::HasClientPerfMonitor;
|
||||||
#[cfg(feature = "concolic_mutation")]
|
|
||||||
use crate::state::State;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
|
||||||
executors::{Executor, HasObservers},
|
executors::{Executor, HasObservers},
|
||||||
observers::concolic::ConcolicObserver,
|
observers::concolic::ConcolicObserver,
|
||||||
state::{HasCorpus, HasExecutions, HasMetadata, HasNamedMetadata, UsesState},
|
stages::{RetryRestartHelper, Stage, TracingStage},
|
||||||
|
state::{
|
||||||
|
HasCorpus, HasCurrentTestcase, HasExecutions, HasMetadata, HasNamedMetadata, UsesState,
|
||||||
|
},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "concolic_mutation")]
|
#[cfg(feature = "concolic_mutation")]
|
||||||
@ -28,7 +28,10 @@ use crate::{
|
|||||||
inputs::HasBytesVec,
|
inputs::HasBytesVec,
|
||||||
mark_feature_time,
|
mark_feature_time,
|
||||||
observers::concolic::{ConcolicMetadata, SymExpr, SymExprRef},
|
observers::concolic::{ConcolicMetadata, SymExpr, SymExprRef},
|
||||||
start_timer, Evaluator,
|
stages::ExecutionCountRestartHelper,
|
||||||
|
start_timer,
|
||||||
|
state::State,
|
||||||
|
Evaluator,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Wraps a [`TracingStage`] to add concolic observing.
|
/// Wraps a [`TracingStage`] to add concolic observing.
|
||||||
@ -45,6 +48,12 @@ where
|
|||||||
type State = TE::State;
|
type State = TE::State;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<EM, TE, Z> Named for ConcolicTracingStage<EM, TE, Z> {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"ConcolicTracingStage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E, EM, TE, Z> Stage<E, EM, Z> for ConcolicTracingStage<EM, TE, Z>
|
impl<E, EM, TE, Z> Stage<E, EM, Z> for ConcolicTracingStage<EM, TE, Z>
|
||||||
where
|
where
|
||||||
E: UsesState<State = TE::State>,
|
E: UsesState<State = TE::State>,
|
||||||
@ -53,8 +62,6 @@ where
|
|||||||
TE::State: HasExecutions + HasCorpus + HasNamedMetadata,
|
TE::State: HasExecutions + HasCorpus + HasNamedMetadata,
|
||||||
Z: UsesState<State = TE::State>,
|
Z: UsesState<State = TE::State>,
|
||||||
{
|
{
|
||||||
type Progress = RetryProgress;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -63,16 +70,7 @@ where
|
|||||||
state: &mut TE::State,
|
state: &mut TE::State,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
self.inner.trace(fuzzer, state, manager)?;
|
||||||
return Err(Error::illegal_state(
|
|
||||||
"state is not currently processing a corpus index",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
if Self::Progress::should_skip(state, &self.inner, corpus_idx)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.inner.trace(fuzzer, state, manager, corpus_idx)?;
|
|
||||||
if let Some(observer) = self
|
if let Some(observer) = self
|
||||||
.inner
|
.inner
|
||||||
.executor()
|
.executor()
|
||||||
@ -81,27 +79,25 @@ where
|
|||||||
{
|
{
|
||||||
let metadata = observer.create_metadata_from_current_map();
|
let metadata = observer.create_metadata_from_current_map();
|
||||||
state
|
state
|
||||||
.corpus_mut()
|
.current_testcase_mut()?
|
||||||
.get(corpus_idx)
|
|
||||||
.unwrap()
|
|
||||||
.borrow_mut()
|
|
||||||
.metadata_map_mut()
|
.metadata_map_mut()
|
||||||
.insert(metadata);
|
.insert(metadata);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<EM, TE, Z> RetryingStage for ConcolicTracingStage<EM, TE, Z> {
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
fn max_retries(&self) -> usize {
|
RetryRestartHelper::restart_progress_should_run(state, self, 3)
|
||||||
self.inner.max_retries()
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EM, TE, Z> ConcolicTracingStage<EM, TE, Z> {
|
impl<EM, TE, Z> ConcolicTracingStage<EM, TE, Z> {
|
||||||
/// Creates a new default tracing stage using the given [`Executor`], observing traces from a
|
/// Creates a new default tracing stage using the given [`Executor`], observing traces from a
|
||||||
/// [`ConcolicObserver`] with the given name. The [`RetryingStage::max_retries`] is
|
/// [`ConcolicObserver`] with the given name.
|
||||||
/// used from the provided inner stage.
|
|
||||||
pub fn new(inner: TracingStage<EM, TE, Z>, observer_name: String) -> Self {
|
pub fn new(inner: TracingStage<EM, TE, Z>, observer_name: String) -> Self {
|
||||||
Self {
|
Self {
|
||||||
inner,
|
inner,
|
||||||
@ -353,9 +349,12 @@ fn generate_mutations(iter: impl Iterator<Item = (SymExprRef, SymExpr)>) -> Vec<
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// A mutational stage that uses Z3 to solve concolic constraints attached to the [`crate::corpus::Testcase`] by the [`ConcolicTracingStage`].
|
/// A mutational stage that uses Z3 to solve concolic constraints attached to the [`crate::corpus::Testcase`] by the [`ConcolicTracingStage`].
|
||||||
|
#[cfg(feature = "concolic_mutation")]
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct SimpleConcolicMutationalStage<Z> {
|
pub struct SimpleConcolicMutationalStage<Z> {
|
||||||
_phantom: PhantomData<Z>,
|
/// The helper keeps track of progress for timeouting/restarting targets
|
||||||
|
restart_helper: ExecutionCountRestartHelper,
|
||||||
|
phantom: PhantomData<Z>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "concolic_mutation")]
|
#[cfg(feature = "concolic_mutation")]
|
||||||
@ -373,10 +372,8 @@ where
|
|||||||
EM: UsesState<State = Z::State>,
|
EM: UsesState<State = Z::State>,
|
||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::Input: HasBytesVec,
|
Z::Input: HasBytesVec,
|
||||||
Z::State: State + HasExecutions + HasCorpus,
|
Z::State: State + HasExecutions + HasCorpus + HasMetadata,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO we need a resume for this type
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -385,30 +382,25 @@ where
|
|||||||
state: &mut Z::State,
|
state: &mut Z::State,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
{
|
||||||
return Err(Error::illegal_state(
|
start_timer!(state);
|
||||||
"state is not currently processing a corpus index",
|
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
||||||
));
|
}
|
||||||
};
|
let testcase = state.current_testcase()?.clone();
|
||||||
|
|
||||||
start_timer!(state);
|
let mutations = testcase.metadata::<ConcolicMetadata>().ok().map(|meta| {
|
||||||
let testcase = state.corpus().get(corpus_idx)?.clone();
|
start_timer!(state);
|
||||||
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
let mutations = { generate_mutations(meta.iter_messages()) };
|
||||||
|
mark_feature_time!(state, PerfFeature::Mutate);
|
||||||
|
mutations
|
||||||
|
});
|
||||||
|
|
||||||
let mutations =
|
let post_restart_skip_cnt =
|
||||||
if let Some(meta) = testcase.borrow().metadata_map().get::<ConcolicMetadata>() {
|
usize::try_from(self.restart_helper.execs_since_progress_start(state)?)?;
|
||||||
start_timer!(state);
|
|
||||||
let mutations = generate_mutations(meta.iter_messages());
|
|
||||||
mark_feature_time!(state, PerfFeature::Mutate);
|
|
||||||
Some(mutations)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(mutations) = mutations {
|
if let Some(mutations) = mutations {
|
||||||
let input = { testcase.borrow().input().as_ref().unwrap().clone() };
|
for mutation in mutations.into_iter().skip(post_restart_skip_cnt) {
|
||||||
for mutation in mutations {
|
let mut input_copy = state.current_input_cloned()?;
|
||||||
let mut input_copy = input.to_owned();
|
|
||||||
for (index, new_byte) in mutation {
|
for (index, new_byte) in mutation {
|
||||||
input_copy.bytes_mut()[index] = new_byte;
|
input_copy.bytes_mut()[index] = new_byte;
|
||||||
}
|
}
|
||||||
@ -418,12 +410,24 @@ where
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
self.restart_helper.restart_progress_should_run(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
self.restart_helper.clear_restart_progress(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "concolic_mutation")]
|
||||||
impl<Z> Default for SimpleConcolicMutationalStage<Z> {
|
impl<Z> Default for SimpleConcolicMutationalStage<Z> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
_phantom: PhantomData,
|
restart_helper: ExecutionCountRestartHelper::default(),
|
||||||
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,8 +52,6 @@ where
|
|||||||
Z: UsesState,
|
Z: UsesState,
|
||||||
Z::State: HasCorpus + HasSolutions + HasRand + HasMetadata,
|
Z::State: HasCorpus + HasSolutions + HasRand + HasMetadata,
|
||||||
{
|
{
|
||||||
type Progress = (); // if this fails, we have bigger problems
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -115,6 +113,18 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// Not executing the target, so restart safety is not needed
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
// Not executing the target, so restart safety is not needed
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CB, EM, Z> DumpToDiskStage<CB, EM, Z>
|
impl<CB, EM, Z> DumpToDiskStage<CB, EM, Z>
|
||||||
|
@ -6,7 +6,7 @@ use alloc::{
|
|||||||
};
|
};
|
||||||
use core::{fmt::Debug, marker::PhantomData};
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
use libafl_bolts::AsSlice;
|
use libafl_bolts::{AsSlice, Named};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||||
@ -15,9 +15,9 @@ use crate::{
|
|||||||
inputs::{BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasBytesVec, UsesInput},
|
inputs::{BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasBytesVec, UsesInput},
|
||||||
mark_feature_time,
|
mark_feature_time,
|
||||||
observers::{MapObserver, ObserversTuple},
|
observers::{MapObserver, ObserversTuple},
|
||||||
stages::Stage,
|
stages::{RetryRestartHelper, Stage},
|
||||||
start_timer,
|
start_timer,
|
||||||
state::{HasCorpus, HasExecutions, HasMetadata, UsesState},
|
state::{HasCorpus, HasExecutions, HasMetadata, HasNamedMetadata, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
@ -47,6 +47,12 @@ pub struct GeneralizationStage<EM, O, OT, Z> {
|
|||||||
phantom: PhantomData<(EM, O, OT, Z)>,
|
phantom: PhantomData<(EM, O, OT, Z)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<EM, O, OT, Z> Named for GeneralizationStage<EM, O, OT, Z> {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"GeneralizationStage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<EM, O, OT, Z> UsesState for GeneralizationStage<EM, O, OT, Z>
|
impl<EM, O, OT, Z> UsesState for GeneralizationStage<EM, O, OT, Z>
|
||||||
where
|
where
|
||||||
EM: UsesState,
|
EM: UsesState,
|
||||||
@ -60,12 +66,11 @@ where
|
|||||||
O: MapObserver,
|
O: MapObserver,
|
||||||
E: Executor<EM, Z> + HasObservers,
|
E: Executor<EM, Z> + HasObservers,
|
||||||
E::Observers: ObserversTuple<E::State>,
|
E::Observers: ObserversTuple<E::State>,
|
||||||
E::State: UsesInput<Input = BytesInput> + HasExecutions + HasMetadata + HasCorpus,
|
E::State:
|
||||||
|
UsesInput<Input = BytesInput> + HasExecutions + HasMetadata + HasCorpus + HasNamedMetadata,
|
||||||
EM: UsesState<State = E::State>,
|
EM: UsesState<State = E::State>,
|
||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO this stage needs a resume
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::too_many_lines)]
|
#[allow(clippy::too_many_lines)]
|
||||||
fn perform(
|
fn perform(
|
||||||
@ -312,6 +317,18 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// TODO: We need to be able to resume better if something crashes or times out
|
||||||
|
RetryRestartHelper::restart_progress_should_run(state, self, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
// TODO: We need to be able to resume better if something crashes or times out
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EM, O, OT, Z> GeneralizationStage<EM, O, OT, Z>
|
impl<EM, O, OT, Z> GeneralizationStage<EM, O, OT, Z>
|
||||||
|
@ -3,36 +3,31 @@
|
|||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
stages::{HasCurrentStage, HasNestedStageStatus, Stage, StageProgress, StagesTuple},
|
stages::{HasCurrentStage, HasNestedStageStatus, Stage, StagesTuple},
|
||||||
state::UsesState,
|
state::UsesState,
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Progress for nested stages. This merely enters/exits the inner stage's scope.
|
/// Progress for nested stages. This merely enters/exits the inner stage's scope.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct NestedStageProgress;
|
pub struct NestedStageRestartHelper;
|
||||||
|
|
||||||
impl<S, ST> StageProgress<S, ST> for NestedStageProgress
|
impl NestedStageRestartHelper {
|
||||||
where
|
fn restart_progress_should_run<S, ST>(state: &mut S, _stage: &ST) -> Result<bool, Error>
|
||||||
S: HasNestedStageStatus,
|
where
|
||||||
{
|
S: HasNestedStageStatus,
|
||||||
fn initialize_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
{
|
||||||
state.enter_inner_stage()?;
|
state.enter_inner_stage()?;
|
||||||
Ok(())
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
fn clear_restart_progress<S, ST>(state: &mut S, _stage: &ST) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
S: HasNestedStageStatus,
|
||||||
|
{
|
||||||
state.exit_inner_stage()?;
|
state.exit_inner_stage()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress<'a>(_state: &'a S, _stage: &ST) -> Result<&'a Self, Error> {
|
|
||||||
unimplemented!("NestedStageProgress should not be queried")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn progress_mut<'a>(_state: &'a mut S, _stage: &ST) -> Result<&'a mut Self, Error> {
|
|
||||||
unimplemented!("NestedStageProgress should not be queried")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
@ -70,8 +65,6 @@ where
|
|||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
E::State: HasNestedStageStatus,
|
E::State: HasNestedStageStatus,
|
||||||
{
|
{
|
||||||
type Progress = NestedStageProgress;
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -86,6 +79,14 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
NestedStageRestartHelper::restart_progress_should_run(state, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
NestedStageRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CB, E, EM, ST, Z> WhileStage<CB, E, EM, ST, Z>
|
impl<CB, E, EM, ST, Z> WhileStage<CB, E, EM, ST, Z>
|
||||||
@ -142,8 +143,6 @@ where
|
|||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
E::State: HasNestedStageStatus,
|
E::State: HasNestedStageStatus,
|
||||||
{
|
{
|
||||||
type Progress = NestedStageProgress;
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -157,6 +156,14 @@ where
|
|||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
NestedStageRestartHelper::restart_progress_should_run(state, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
NestedStageRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CB, E, EM, ST, Z> IfStage<CB, E, EM, ST, Z>
|
impl<CB, E, EM, ST, Z> IfStage<CB, E, EM, ST, Z>
|
||||||
@ -217,8 +224,6 @@ where
|
|||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
E::State: HasNestedStageStatus,
|
E::State: HasNestedStageStatus,
|
||||||
{
|
{
|
||||||
type Progress = NestedStageProgress;
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -252,6 +257,14 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
NestedStageRestartHelper::restart_progress_should_run(state, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
NestedStageRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CB, E, EM, ST1, ST2, Z> IfElseStage<CB, E, EM, ST1, ST2, Z>
|
impl<CB, E, EM, ST1, ST2, Z> IfElseStage<CB, E, EM, ST1, ST2, Z>
|
||||||
@ -305,8 +318,6 @@ where
|
|||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
E::State: HasNestedStageStatus,
|
E::State: HasNestedStageStatus,
|
||||||
{
|
{
|
||||||
type Progress = NestedStageProgress;
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -320,6 +331,14 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
NestedStageRestartHelper::restart_progress_should_run(state, self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
NestedStageRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, EM, ST, Z> OptionalStage<E, EM, ST, Z>
|
impl<E, EM, ST, Z> OptionalStage<E, EM, ST, Z>
|
||||||
@ -356,178 +375,3 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use core::{cell::RefCell, marker::PhantomData};
|
|
||||||
|
|
||||||
use libafl_bolts::{tuples::tuple_list, Error};
|
|
||||||
|
|
||||||
use crate::{
|
|
||||||
inputs::NopInput,
|
|
||||||
stages::{
|
|
||||||
test::{test_resume, test_resume_stages},
|
|
||||||
ClosureStage, IfElseStage, IfStage, Stage, WhileStage,
|
|
||||||
},
|
|
||||||
state::{test::test_std_state, State, UsesState},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_resumability_while() {
|
|
||||||
let once = RefCell::new(true);
|
|
||||||
let (completed, stages) = test_resume_stages();
|
|
||||||
let whilestage = WhileStage::new(|_, _, _, _| Ok(once.replace(false)), stages);
|
|
||||||
let resetstage = ClosureStage::new(|_, _, _, _| {
|
|
||||||
once.replace(true);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut state = test_std_state::<NopInput>();
|
|
||||||
|
|
||||||
test_resume(&completed, &mut state, tuple_list!(whilestage, resetstage));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_resumability_if() {
|
|
||||||
let once = RefCell::new(true);
|
|
||||||
let (completed, stages) = test_resume_stages();
|
|
||||||
let ifstage = IfStage::new(|_, _, _, _| Ok(once.replace(false)), stages);
|
|
||||||
let resetstage = ClosureStage::new(|_, _, _, _| {
|
|
||||||
once.replace(true);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut state = test_std_state::<NopInput>();
|
|
||||||
|
|
||||||
test_resume(&completed, &mut state, tuple_list!(ifstage, resetstage));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_resumability_if_deep() {
|
|
||||||
let (completed, stages) = test_resume_stages();
|
|
||||||
let ifstage = IfStage::new(
|
|
||||||
|_, _, _, _| Ok(true),
|
|
||||||
tuple_list!(IfStage::new(
|
|
||||||
|_, _, _, _| Ok(true),
|
|
||||||
tuple_list!(IfStage::new(
|
|
||||||
|_, _, _, _| Ok(true),
|
|
||||||
tuple_list!(IfStage::new(
|
|
||||||
|_, _, _, _| Ok(true),
|
|
||||||
tuple_list!(IfStage::new(|_, _, _, _| Ok(true), stages),),
|
|
||||||
),),
|
|
||||||
))
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut state = test_std_state::<NopInput>();
|
|
||||||
|
|
||||||
test_resume(&completed, &mut state, tuple_list!(ifstage));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct PanicStage<S> {
|
|
||||||
phantom: PhantomData<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> PanicStage<S> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> UsesState for PanicStage<S>
|
|
||||||
where
|
|
||||||
S: State,
|
|
||||||
{
|
|
||||||
type State = S;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, EM, Z> Stage<E, EM, Z> for PanicStage<E::State>
|
|
||||||
where
|
|
||||||
E: UsesState,
|
|
||||||
EM: UsesState<State = E::State>,
|
|
||||||
Z: UsesState<State = E::State>,
|
|
||||||
{
|
|
||||||
type Progress = ();
|
|
||||||
|
|
||||||
fn perform(
|
|
||||||
&mut self,
|
|
||||||
_fuzzer: &mut Z,
|
|
||||||
_executor: &mut E,
|
|
||||||
_state: &mut Self::State,
|
|
||||||
_manager: &mut EM,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
panic!("Test failed; panic stage should never be executed.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_resumability_if_else_if() {
|
|
||||||
let once = RefCell::new(true);
|
|
||||||
let (completed, stages) = test_resume_stages();
|
|
||||||
let ifstage = IfElseStage::new(
|
|
||||||
|_, _, _, _| Ok(once.replace(false)),
|
|
||||||
stages,
|
|
||||||
tuple_list!(PanicStage::new()),
|
|
||||||
);
|
|
||||||
let resetstage = ClosureStage::new(|_, _, _, _| {
|
|
||||||
once.replace(true);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut state = test_std_state::<NopInput>();
|
|
||||||
|
|
||||||
test_resume(&completed, &mut state, tuple_list!(ifstage, resetstage));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_resumability_if_else_else() {
|
|
||||||
let once = RefCell::new(false);
|
|
||||||
let (completed, stages) = test_resume_stages();
|
|
||||||
let ifstage = IfElseStage::new(
|
|
||||||
|_, _, _, _| Ok(once.replace(true)),
|
|
||||||
tuple_list!(PanicStage::new()),
|
|
||||||
stages,
|
|
||||||
);
|
|
||||||
let resetstage = ClosureStage::new(|_, _, _, _| {
|
|
||||||
once.replace(false);
|
|
||||||
Ok(())
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut state = test_std_state::<NopInput>();
|
|
||||||
|
|
||||||
test_resume(&completed, &mut state, tuple_list!(ifstage, resetstage));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn check_resumability_if_else_else_deep() {
|
|
||||||
let (completed, stages) = test_resume_stages();
|
|
||||||
let ifstage = IfElseStage::new(
|
|
||||||
|_, _, _, _| Ok(false),
|
|
||||||
tuple_list!(PanicStage::new()),
|
|
||||||
tuple_list!(IfElseStage::new(
|
|
||||||
|_, _, _, _| Ok(false),
|
|
||||||
tuple_list!(PanicStage::new()),
|
|
||||||
tuple_list!(IfElseStage::new(
|
|
||||||
|_, _, _, _| Ok(false),
|
|
||||||
tuple_list!(PanicStage::new()),
|
|
||||||
tuple_list!(IfElseStage::new(
|
|
||||||
|_, _, _, _| Ok(false),
|
|
||||||
tuple_list!(PanicStage::new()),
|
|
||||||
tuple_list!(IfElseStage::new(
|
|
||||||
|_, _, _, _| Ok(false),
|
|
||||||
tuple_list!(PanicStage::new()),
|
|
||||||
stages,
|
|
||||||
)),
|
|
||||||
)),
|
|
||||||
)),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut state = test_std_state::<NopInput>();
|
|
||||||
|
|
||||||
test_resume(&completed, &mut state, tuple_list!(ifstage));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,19 +4,24 @@ A well-known [`Stage`], for example, is the mutational stage, running multiple [
|
|||||||
Other stages may enrich [`crate::corpus::Testcase`]s with metadata.
|
Other stages may enrich [`crate::corpus::Testcase`]s with metadata.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
use alloc::{boxed::Box, vec::Vec};
|
||||||
|
use core::{any, marker::PhantomData};
|
||||||
|
|
||||||
pub use calibrate::CalibrationStage;
|
pub use calibrate::CalibrationStage;
|
||||||
pub use colorization::*;
|
pub use colorization::*;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use concolic::ConcolicTracingStage;
|
pub use concolic::ConcolicTracingStage;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(all(feature = "std", feature = "concolic_mutation"))]
|
||||||
pub use concolic::SimpleConcolicMutationalStage;
|
pub use concolic::SimpleConcolicMutationalStage;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub use dump::*;
|
pub use dump::*;
|
||||||
pub use generalization::GeneralizationStage;
|
pub use generalization::GeneralizationStage;
|
||||||
use hashbrown::HashSet;
|
use hashbrown::HashSet;
|
||||||
use libafl_bolts::{impl_serdeany, tuples::HasConstLen};
|
use libafl_bolts::{
|
||||||
|
impl_serdeany,
|
||||||
|
tuples::{HasConstLen, IntoVec},
|
||||||
|
Named,
|
||||||
|
};
|
||||||
pub use logics::*;
|
pub use logics::*;
|
||||||
pub use mutational::{MutationalStage, StdMutationalStage};
|
pub use mutational::{MutationalStage, StdMutationalStage};
|
||||||
pub use power::{PowerMutationalStage, StdPowerMutationalStage};
|
pub use power::{PowerMutationalStage, StdPowerMutationalStage};
|
||||||
@ -31,8 +36,8 @@ pub use tmin::{
|
|||||||
};
|
};
|
||||||
pub use tracing::{ShadowTracingStage, TracingStage};
|
pub use tracing::{ShadowTracingStage, TracingStage};
|
||||||
pub use tuneable::*;
|
pub use tuneable::*;
|
||||||
|
use tuple_list::NonEmptyTuple;
|
||||||
|
|
||||||
use self::push::PushStage;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{CorpusId, HasCurrentCorpusIdx},
|
corpus::{CorpusId, HasCurrentCorpusIdx},
|
||||||
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
|
events::{EventFirer, EventRestarter, HasEventManagerId, ProgressReporter},
|
||||||
@ -40,8 +45,9 @@ use crate::{
|
|||||||
inputs::UsesInput,
|
inputs::UsesInput,
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
schedulers::Scheduler,
|
schedulers::Scheduler,
|
||||||
|
stages::push::PushStage,
|
||||||
state::{
|
state::{
|
||||||
HasCorpus, HasExecutions, HasLastReportTime, HasMetadata, HasNamedMetadata, HasRand,
|
HasCorpus, HasExecutions, HasLastReportTime, HasMetadata, HasNamedMetadata, HasRand, State,
|
||||||
UsesState,
|
UsesState,
|
||||||
},
|
},
|
||||||
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler,
|
Error, EvaluatorObservers, ExecutesInput, ExecutionProcessor, HasScheduler,
|
||||||
@ -77,13 +83,21 @@ where
|
|||||||
EM: UsesState<State = Self::State>,
|
EM: UsesState<State = Self::State>,
|
||||||
Z: UsesState<State = Self::State>,
|
Z: UsesState<State = Self::State>,
|
||||||
{
|
{
|
||||||
// TODO: default this to () when associated_type_defaults is stable
|
/// This method will be called before every call to [`Stage::perform`].
|
||||||
// TODO: see RFC 2532: https://github.com/rust-lang/rust/issues/29661
|
/// Initialize the restart tracking for this stage, _if it is not yet initialized_.
|
||||||
// type Status: ResumableStageStatus = ();
|
/// On restart, this will be called again.
|
||||||
/// The resumption data for this stage. Set to () if resuming is not necessary/possible.
|
/// As long as [`Stage::clear_restart_progress`], all subsequent calls happen on restart.
|
||||||
type Progress: StageProgress<Self::State, Self>;
|
/// Returns `true`, if the stage's [`Stage::perform`] method should run, else `false`.
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error>;
|
||||||
|
|
||||||
/// Run the stage
|
/// Clear the current status tracking of the associated stage
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Run the stage.
|
||||||
|
///
|
||||||
|
/// Before a call to perform, [`Stage::restart_progress_should_run`] will be (must be!) called.
|
||||||
|
/// After returning (so non-target crash or timeout in a restarting case), [`Stage::clear_restart_progress`] gets called.
|
||||||
|
/// A call to [`Stage::perform_restartable`] will do these things implicitly.
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -91,6 +105,20 @@ where
|
|||||||
state: &mut Self::State,
|
state: &mut Self::State,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
) -> Result<(), Error>;
|
) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Run the stage, calling [`Stage::restart_progress_should_run`] and [`Stage::clear_restart_progress`] appropriately
|
||||||
|
fn perform_restartable(
|
||||||
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
|
executor: &mut E,
|
||||||
|
state: &mut Self::State,
|
||||||
|
manager: &mut EM,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if self.restart_progress_should_run(state)? {
|
||||||
|
self.perform(fuzzer, executor, state, manager)?;
|
||||||
|
}
|
||||||
|
self.clear_restart_progress(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A tuple holding all `Stages` used for fuzzing.
|
/// A tuple holding all `Stages` used for fuzzing.
|
||||||
@ -158,9 +186,9 @@ where
|
|||||||
Some(idx) if idx == Self::LEN => {
|
Some(idx) if idx == Self::LEN => {
|
||||||
// perform the stage, but don't set it
|
// perform the stage, but don't set it
|
||||||
let stage = &mut self.0;
|
let stage = &mut self.0;
|
||||||
Head::Progress::initialize_progress(state, stage)?;
|
|
||||||
stage.perform(fuzzer, executor, state, manager)?;
|
stage.perform_restartable(fuzzer, executor, state, manager)?;
|
||||||
Head::Progress::clear_progress(state, stage)?;
|
|
||||||
state.clear_stage()?;
|
state.clear_stage()?;
|
||||||
}
|
}
|
||||||
Some(idx) if idx > Self::LEN => {
|
Some(idx) if idx > Self::LEN => {
|
||||||
@ -169,10 +197,10 @@ where
|
|||||||
// this is None, but the match can't deduce that
|
// this is None, but the match can't deduce that
|
||||||
_ => {
|
_ => {
|
||||||
state.set_stage(Self::LEN)?;
|
state.set_stage(Self::LEN)?;
|
||||||
|
|
||||||
let stage = &mut self.0;
|
let stage = &mut self.0;
|
||||||
Head::Progress::initialize_progress(state, stage)?;
|
stage.perform_restartable(fuzzer, executor, state, manager)?;
|
||||||
stage.perform(fuzzer, executor, state, manager)?;
|
|
||||||
Head::Progress::clear_progress(state, stage)?;
|
|
||||||
state.clear_stage()?;
|
state.clear_stage()?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -182,6 +210,71 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Head, Tail, E, EM, Z>
|
||||||
|
IntoVec<Box<dyn Stage<E, EM, Z, State = Head::State, Input = Head::Input>>> for (Head, Tail)
|
||||||
|
where
|
||||||
|
Head: Stage<E, EM, Z> + 'static,
|
||||||
|
Tail: StagesTuple<E, EM, Head::State, Z>
|
||||||
|
+ HasConstLen
|
||||||
|
+ IntoVec<Box<dyn Stage<E, EM, Z, State = Head::State, Input = Head::Input>>>,
|
||||||
|
E: UsesState<State = Head::State>,
|
||||||
|
EM: UsesState<State = Head::State>,
|
||||||
|
Z: UsesState<State = Head::State>,
|
||||||
|
Head::State: HasCurrentStage,
|
||||||
|
{
|
||||||
|
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, Z, State = Head::State, Input = Head::Input>>> {
|
||||||
|
let (head, tail) = self.uncons();
|
||||||
|
let mut ret = tail.0.into_vec();
|
||||||
|
ret.insert(0, Box::new(head));
|
||||||
|
ret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Tail, E, EM, Z> IntoVec<Box<dyn Stage<E, EM, Z, State = Tail::State, Input = Tail::Input>>>
|
||||||
|
for (Tail,)
|
||||||
|
where
|
||||||
|
Tail: UsesState + IntoVec<Box<dyn Stage<E, EM, Z, State = Tail::State, Input = Tail::Input>>>,
|
||||||
|
Z: UsesState<State = Tail::State>,
|
||||||
|
EM: UsesState<State = Tail::State>,
|
||||||
|
E: UsesState<State = Tail::State>,
|
||||||
|
{
|
||||||
|
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, Z, State = Tail::State, Input = Tail::Input>>> {
|
||||||
|
self.0.into_vec()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, Z> IntoVec<Box<dyn Stage<E, EM, Z, State = Z::State, Input = Z::Input>>>
|
||||||
|
for Vec<Box<dyn Stage<E, EM, Z, State = Z::State, Input = Z::Input>>>
|
||||||
|
where
|
||||||
|
Z: UsesState,
|
||||||
|
EM: UsesState<State = Z::State>,
|
||||||
|
E: UsesState<State = Z::State>,
|
||||||
|
{
|
||||||
|
fn into_vec(self) -> Vec<Box<dyn Stage<E, EM, Z, State = Z::State, Input = Z::Input>>> {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, S, Z> StagesTuple<E, EM, S, Z>
|
||||||
|
for Vec<Box<dyn Stage<E, EM, Z, State = S, Input = S::Input>>>
|
||||||
|
where
|
||||||
|
E: UsesState<State = S>,
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
|
S: UsesInput + HasCurrentStage + State,
|
||||||
|
{
|
||||||
|
fn perform_all(
|
||||||
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
|
executor: &mut E,
|
||||||
|
state: &mut S,
|
||||||
|
manager: &mut EM,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.iter_mut()
|
||||||
|
.try_for_each(|x| x.perform_restartable(fuzzer, executor, state, manager))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A [`Stage`] that will call a closure
|
/// A [`Stage`] that will call a closure
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ClosureStage<CB, E, EM, Z>
|
pub struct ClosureStage<CB, E, EM, Z>
|
||||||
@ -201,15 +294,24 @@ where
|
|||||||
type State = E::State;
|
type State = E::State;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<CB, E, EM, Z> Named for ClosureStage<CB, E, EM, Z>
|
||||||
|
where
|
||||||
|
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<(), Error>,
|
||||||
|
E: UsesState,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
any::type_name::<Self>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<CB, E, EM, Z> Stage<E, EM, Z> for ClosureStage<CB, E, EM, Z>
|
impl<CB, E, EM, Z> Stage<E, EM, Z> for ClosureStage<CB, E, EM, Z>
|
||||||
where
|
where
|
||||||
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<(), Error>,
|
CB: FnMut(&mut Z, &mut E, &mut E::State, &mut EM) -> Result<(), Error>,
|
||||||
E: UsesState,
|
E: UsesState,
|
||||||
EM: UsesState<State = E::State>,
|
EM: UsesState<State = E::State>,
|
||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
|
E::State: HasNamedMetadata,
|
||||||
{
|
{
|
||||||
type Progress = ();
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -219,6 +321,17 @@ where
|
|||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
(self.closure)(fuzzer, executor, state, manager)
|
(self.closure)(fuzzer, executor, state, manager)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// Make sure we don't get stuck crashing on a single closure
|
||||||
|
RetryRestartHelper::restart_progress_should_run(state, self, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A stage that takes a closure
|
/// A stage that takes a closure
|
||||||
@ -292,8 +405,6 @@ where
|
|||||||
+ EvaluatorObservers<OT>
|
+ EvaluatorObservers<OT>
|
||||||
+ HasScheduler<Scheduler = CS>,
|
+ HasScheduler<Scheduler = CS>,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO implement resume
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -336,6 +447,193 @@ where
|
|||||||
self.push_stage
|
self.push_stage
|
||||||
.deinit(fuzzer, state, event_mgr, executor.observers_mut())
|
.deinit(fuzzer, state, event_mgr, executor.observers_mut())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// TODO: Proper restart handling - call post_exec at the right time, etc...
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Progress which permits a fixed amount of resumes per round of fuzzing. If this amount is ever
|
||||||
|
/// exceeded, the input will no longer be executed by this stage.
|
||||||
|
#[derive(Clone, Deserialize, Serialize, Debug)]
|
||||||
|
pub struct RetryRestartHelper {
|
||||||
|
tries_remaining: Option<usize>,
|
||||||
|
skipped: HashSet<CorpusId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_serdeany!(RetryRestartHelper);
|
||||||
|
|
||||||
|
impl RetryRestartHelper {
|
||||||
|
/// Initializes (or counts down in) the progress helper, giving it the amount of max retries
|
||||||
|
///
|
||||||
|
/// Returns `true` if the stage should run
|
||||||
|
pub fn restart_progress_should_run<S, ST>(
|
||||||
|
state: &mut S,
|
||||||
|
stage: &ST,
|
||||||
|
max_retries: usize,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
S: HasNamedMetadata + HasCurrentCorpusIdx,
|
||||||
|
ST: Named,
|
||||||
|
{
|
||||||
|
let corpus_idx = state.current_corpus_idx()?.ok_or_else(|| {
|
||||||
|
Error::illegal_state(
|
||||||
|
"No current_corpus_idx set in State, but called RetryRestartHelper::should_skip",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
let initial_tries_remaining = max_retries + 1;
|
||||||
|
let metadata = state.named_metadata_or_insert_with(stage.name(), || Self {
|
||||||
|
tries_remaining: Some(initial_tries_remaining),
|
||||||
|
skipped: HashSet::new(),
|
||||||
|
});
|
||||||
|
let tries_remaining = metadata
|
||||||
|
.tries_remaining
|
||||||
|
.unwrap_or(initial_tries_remaining)
|
||||||
|
.checked_sub(1)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::illegal_state(
|
||||||
|
"Attempted further retries after we had already gotten to none remaining.",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
metadata.tries_remaining = Some(tries_remaining);
|
||||||
|
|
||||||
|
Ok(if tries_remaining == 0 {
|
||||||
|
metadata.skipped.insert(corpus_idx);
|
||||||
|
false
|
||||||
|
} else if metadata.skipped.contains(&corpus_idx) {
|
||||||
|
// skip this testcase, we already retried it often enough...
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clears the progress
|
||||||
|
pub fn clear_restart_progress<S, ST>(state: &mut S, stage: &ST) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
S: HasNamedMetadata,
|
||||||
|
ST: Named,
|
||||||
|
{
|
||||||
|
state
|
||||||
|
.named_metadata_mut::<Self>(stage.name())?
|
||||||
|
.tries_remaining = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for types which track the current stage
|
||||||
|
pub trait HasCurrentStage {
|
||||||
|
/// Set the current stage; we have started processing this stage
|
||||||
|
fn set_stage(&mut self, idx: usize) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Clear the current stage; we are done processing this stage
|
||||||
|
fn clear_stage(&mut self) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Fetch the current stage -- typically used after a state recovery or transfer
|
||||||
|
fn current_stage(&self) -> Result<Option<usize>, Error>;
|
||||||
|
|
||||||
|
/// Notify of a reset from which we may recover
|
||||||
|
fn on_restart(&mut self) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Trait for types which track nested stages. Stages which themselves contain stage tuples should
|
||||||
|
/// ensure that they constrain the state with this trait accordingly.
|
||||||
|
pub trait HasNestedStageStatus: HasCurrentStage {
|
||||||
|
/// Enter a stage scope, potentially resuming to an inner stage status. Returns Ok(true) if
|
||||||
|
/// resumed.
|
||||||
|
fn enter_inner_stage(&mut self) -> Result<(), Error>;
|
||||||
|
|
||||||
|
/// Exit a stage scope
|
||||||
|
fn exit_inner_stage(&mut self) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_serdeany!(ExecutionCountRestartHelperMetadata);
|
||||||
|
|
||||||
|
/// `SerdeAny` metadata used to keep track of executions since start for a given stage.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct ExecutionCountRestartHelperMetadata {
|
||||||
|
/// How many executions we had when we started this stage initially (this round)
|
||||||
|
started_at_execs: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A tool shed of functions to be used for stages that try to run for `n` iterations.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// This helper assumes resumable mutational stages are not nested.
|
||||||
|
/// If you want to nest them, you will have to switch all uses of `metadata` in this helper to `named_metadata` instead.
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct ExecutionCountRestartHelper {
|
||||||
|
/// At what exec count this Stage was started (cache)
|
||||||
|
/// Only used as cache for the value stored in [`MutationalStageMetadata`].
|
||||||
|
started_at_execs: Option<u64>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecutionCountRestartHelper {
|
||||||
|
/// Create a new [`ExecutionCountRestartHelperMetadata`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
started_at_execs: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The execs done since start of this [`Stage`]/helper
|
||||||
|
pub fn execs_since_progress_start<S>(&mut self, state: &mut S) -> Result<u64, Error>
|
||||||
|
where
|
||||||
|
S: HasMetadata + HasExecutions,
|
||||||
|
{
|
||||||
|
let started_at_execs = if let Some(started_at_execs) = self.started_at_execs {
|
||||||
|
started_at_execs
|
||||||
|
} else {
|
||||||
|
state
|
||||||
|
.metadata::<ExecutionCountRestartHelperMetadata>()
|
||||||
|
.map(|x| {
|
||||||
|
self.started_at_execs = Some(x.started_at_execs);
|
||||||
|
x.started_at_execs
|
||||||
|
})
|
||||||
|
.map_err(|err| {
|
||||||
|
Error::illegal_state(format!(
|
||||||
|
"The ExecutionCountRestartHelperMetadata should have been set at this point - {err}"
|
||||||
|
))
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
Ok(state.executions() - started_at_execs)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize progress for the stage this wrapper wraps.
|
||||||
|
pub fn restart_progress_should_run<S>(&mut self, state: &mut S) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
S: HasMetadata + HasExecutions,
|
||||||
|
{
|
||||||
|
let executions = *state.executions();
|
||||||
|
let metadata = state.metadata_or_insert_with(|| ExecutionCountRestartHelperMetadata {
|
||||||
|
started_at_execs: executions,
|
||||||
|
});
|
||||||
|
self.started_at_execs = Some(metadata.started_at_execs);
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clear progress for the stage this wrapper wraps.
|
||||||
|
pub fn clear_restart_progress<S>(&mut self, state: &mut S) -> Result<(), Error>
|
||||||
|
where
|
||||||
|
S: HasMetadata,
|
||||||
|
{
|
||||||
|
self.started_at_execs = None;
|
||||||
|
let _metadata = state.remove_metadata::<ExecutionCountRestartHelperMetadata>();
|
||||||
|
debug_assert!(_metadata.is_some(), "Called clear_restart_progress, but restart_progress_should_run was not called before (or did mutational stages get nested?)");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `Stage` Python bindings
|
/// `Stage` Python bindings
|
||||||
@ -344,6 +642,7 @@ where
|
|||||||
pub mod pybind {
|
pub mod pybind {
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
|
|
||||||
|
use libafl_bolts::Named;
|
||||||
use pyo3::prelude::*;
|
use pyo3::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
@ -352,7 +651,8 @@ pub mod pybind {
|
|||||||
executors::pybind::PythonExecutor,
|
executors::pybind::PythonExecutor,
|
||||||
fuzzer::pybind::{PythonStdFuzzer, PythonStdFuzzerWrapper},
|
fuzzer::pybind::{PythonStdFuzzer, PythonStdFuzzerWrapper},
|
||||||
stages::{
|
stages::{
|
||||||
mutational::pybind::PythonStdMutationalStage, HasCurrentStage, Stage, StagesTuple,
|
mutational::pybind::PythonStdMutationalStage, HasCurrentStage, RetryRestartHelper,
|
||||||
|
Stage, StagesTuple,
|
||||||
},
|
},
|
||||||
state::{
|
state::{
|
||||||
pybind::{PythonStdState, PythonStdStateWrapper},
|
pybind::{PythonStdState, PythonStdStateWrapper},
|
||||||
@ -377,9 +677,13 @@ pub mod pybind {
|
|||||||
type State = PythonStdState;
|
type State = PythonStdState;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PyObjectStage {
|
impl Named for PyObjectStage {
|
||||||
type Progress = (); // we don't support resumption in python, and maybe can't?
|
fn name(&self) -> &str {
|
||||||
|
"PyObjectStage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PyObjectStage {
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -410,6 +714,15 @@ pub mod pybind {
|
|||||||
})?;
|
})?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// we don't support resumption in python, and maybe can't?
|
||||||
|
RetryRestartHelper::restart_progress_should_run(state, self, 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -472,10 +785,13 @@ pub mod pybind {
|
|||||||
type State = PythonStdState;
|
type State = PythonStdState;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PythonStage {
|
impl Named for PythonStage {
|
||||||
// TODO if we implement resumption for StdMutational, we need to apply it here
|
fn name(&self) -> &str {
|
||||||
type Progress = ();
|
"PythonStage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Stage<PythonExecutor, PythonEventManager, PythonStdFuzzer> for PythonStage {
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::let_and_return)]
|
#[allow(clippy::let_and_return)]
|
||||||
fn perform(
|
fn perform(
|
||||||
@ -486,9 +802,24 @@ pub mod pybind {
|
|||||||
manager: &mut PythonEventManager,
|
manager: &mut PythonEventManager,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
unwrap_me_mut!(self.wrapper, s, {
|
unwrap_me_mut!(self.wrapper, s, {
|
||||||
s.perform(fuzzer, executor, state, manager)
|
s.perform_restartable(fuzzer, executor, state, manager)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(
|
||||||
|
&mut self,
|
||||||
|
state: &mut PythonStdState,
|
||||||
|
) -> Result<bool, Error> {
|
||||||
|
// TODO we need to apply MutationalStage-like resumption here.
|
||||||
|
// For now, make sure we don't get stuck crashing on a single test
|
||||||
|
RetryRestartHelper::restart_progress_should_run(state, self, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
@ -532,7 +863,7 @@ pub mod pybind {
|
|||||||
} else {
|
} else {
|
||||||
state.set_stage(i)?;
|
state.set_stage(i)?;
|
||||||
}
|
}
|
||||||
s.perform(fuzzer, executor, state, manager)?;
|
s.perform_restartable(fuzzer, executor, state, manager)?;
|
||||||
state.clear_stage()?;
|
state.clear_stage()?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -547,173 +878,17 @@ pub mod pybind {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for status tracking of stages which stash data to resume
|
|
||||||
pub trait StageProgress<S, ST>
|
|
||||||
where
|
|
||||||
ST: ?Sized,
|
|
||||||
{
|
|
||||||
/// Initialize the current status tracking for this stage, if it is not yet initialised
|
|
||||||
fn initialize_progress(state: &mut S, stage: &ST) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Clear the current status tracking of the associated stage
|
|
||||||
fn clear_progress(state: &mut S, stage: &ST) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Get the current status tracking of this stage
|
|
||||||
fn progress<'a>(state: &'a S, stage: &ST) -> Result<&'a Self, Error>;
|
|
||||||
|
|
||||||
/// Get the current status tracking of this stage, mutably
|
|
||||||
fn progress_mut<'a>(state: &'a mut S, stage: &ST) -> Result<&'a mut Self, Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, ST> StageProgress<S, ST> for () {
|
|
||||||
fn initialize_progress(_state: &mut S, _stage: &ST) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_progress(_state: &mut S, _stage: &ST) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn progress<'a>(_state: &'a S, _stage: &ST) -> Result<&'a Self, Error> {
|
|
||||||
unimplemented!("The empty tuple resumable stage status should never be queried")
|
|
||||||
}
|
|
||||||
|
|
||||||
fn progress_mut<'a>(_state: &'a mut S, _stage: &ST) -> Result<&'a mut Self, Error> {
|
|
||||||
unimplemented!("The empty tuple resumable stage status should never be queried")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Progress which permits a fixed amount of resumes per round of fuzzing. If this amount is ever
|
|
||||||
/// exceeded, the input will no longer be executed by this stage.
|
|
||||||
#[derive(Clone, Deserialize, Serialize, Debug)]
|
|
||||||
pub struct RetryProgress {
|
|
||||||
tries_remaining: Option<usize>,
|
|
||||||
skipped: HashSet<CorpusId>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl_serdeany!(RetryProgress);
|
|
||||||
|
|
||||||
/// Stage which specifies a certain amount of retries over which a scheduled input is attempted. To
|
|
||||||
/// be used in combination with [`RetryProgress`].
|
|
||||||
pub trait RetryingStage {
|
|
||||||
/// The number of times each testcase may be retries.
|
|
||||||
fn max_retries(&self) -> usize;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S, ST> StageProgress<S, ST> for RetryProgress
|
|
||||||
where
|
|
||||||
S: HasNamedMetadata,
|
|
||||||
ST: RetryingStage,
|
|
||||||
{
|
|
||||||
fn initialize_progress(state: &mut S, stage: &ST) -> Result<(), Error> {
|
|
||||||
if let Ok(metadata) = state.named_metadata_mut::<Self>(core::any::type_name_of_val(stage)) {
|
|
||||||
if let Some(ref mut remaining) = metadata.tries_remaining {
|
|
||||||
*remaining = remaining.checked_sub(1).ok_or_else(|| {
|
|
||||||
Error::illegal_state(
|
|
||||||
"Attempted further retries after we had already gotten to none remaining.",
|
|
||||||
)
|
|
||||||
})?;
|
|
||||||
} else {
|
|
||||||
metadata.tries_remaining = Some(stage.max_retries() + 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
state.add_named_metadata(
|
|
||||||
Self {
|
|
||||||
tries_remaining: Some(stage.max_retries() + 1),
|
|
||||||
skipped: HashSet::new(),
|
|
||||||
},
|
|
||||||
core::any::type_name_of_val(stage),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_progress(state: &mut S, stage: &ST) -> Result<(), Error> {
|
|
||||||
let metadata = state.named_metadata_mut::<Self>(core::any::type_name_of_val(stage))?;
|
|
||||||
metadata.tries_remaining = None;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn progress<'a>(state: &'a S, stage: &ST) -> Result<&'a Self, Error> {
|
|
||||||
state.named_metadata::<Self>(core::any::type_name_of_val(stage))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn progress_mut<'a>(state: &'a mut S, stage: &ST) -> Result<&'a mut Self, Error> {
|
|
||||||
state.named_metadata_mut::<Self>(core::any::type_name_of_val(stage))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RetryProgress {
|
|
||||||
/// Whether we should skip the provided corpus entry.
|
|
||||||
pub fn should_skip<S, ST>(
|
|
||||||
state: &mut S,
|
|
||||||
stage: &ST,
|
|
||||||
corpus_idx: CorpusId,
|
|
||||||
) -> Result<bool, Error>
|
|
||||||
where
|
|
||||||
S: HasNamedMetadata,
|
|
||||||
ST: RetryingStage,
|
|
||||||
{
|
|
||||||
let progress = Self::progress_mut(state, stage)?;
|
|
||||||
if progress.skipped.contains(&corpus_idx) {
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
let remaining = progress.tries_remaining.as_mut().ok_or_else(||
|
|
||||||
Error::illegal_state(
|
|
||||||
"Attempted to check if we should skip a testcase without having initialised the number of tries remaining.",
|
|
||||||
))?;
|
|
||||||
if *remaining == 0 {
|
|
||||||
progress.skipped.insert(corpus_idx);
|
|
||||||
return Ok(true);
|
|
||||||
}
|
|
||||||
Ok(false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for types which track the current stage
|
|
||||||
pub trait HasCurrentStage {
|
|
||||||
/// Set the current stage; we have started processing this stage
|
|
||||||
fn set_stage(&mut self, idx: usize) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Clear the current stage; we are done processing this stage
|
|
||||||
fn clear_stage(&mut self) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Fetch the current stage -- typically used after a state recovery or transfer
|
|
||||||
fn current_stage(&self) -> Result<Option<usize>, Error>;
|
|
||||||
|
|
||||||
/// Notify of a reset from which we may recover
|
|
||||||
fn on_restart(&mut self) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Trait for types which track nested stages. Stages which themselves contain stage tuples should
|
|
||||||
/// ensure that they constrain the state with this trait accordingly.
|
|
||||||
pub trait HasNestedStageStatus: HasCurrentStage {
|
|
||||||
/// Enter a stage scope, potentially resuming to an inner stage status. Returns Ok(true) if
|
|
||||||
/// resumed.
|
|
||||||
fn enter_inner_stage(&mut self) -> Result<(), Error>;
|
|
||||||
|
|
||||||
/// Exit a stage scope
|
|
||||||
fn exit_inner_stage(&mut self) -> Result<(), Error>;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use alloc::rc::Rc;
|
use core::marker::PhantomData;
|
||||||
use core::{cell::RefCell, marker::PhantomData};
|
|
||||||
|
|
||||||
use libafl_bolts::{impl_serdeany, Error};
|
use libafl_bolts::{impl_serdeany, Error, Named};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tuple_list::{tuple_list, tuple_list_type};
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, Testcase},
|
corpus::{Corpus, HasCurrentCorpusIdx, Testcase},
|
||||||
events::NopEventManager,
|
|
||||||
executors::test::NopExecutor,
|
|
||||||
fuzzer::test::NopFuzzer,
|
|
||||||
inputs::NopInput,
|
inputs::NopInput,
|
||||||
stages::{RetryProgress, RetryingStage, Stage, StageProgress, StagesTuple},
|
stages::{RetryRestartHelper, Stage},
|
||||||
state::{test::test_std_state, HasCorpus, HasMetadata, State, UsesState},
|
state::{test::test_std_state, HasCorpus, HasMetadata, State, UsesState},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -722,12 +897,6 @@ pub mod test {
|
|||||||
phantom: PhantomData<S>,
|
phantom: PhantomData<S>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct ResumeFailedStage<S> {
|
|
||||||
completed: Rc<RefCell<bool>>,
|
|
||||||
phantom: PhantomData<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct TestProgress {
|
pub struct TestProgress {
|
||||||
count: usize,
|
count: usize,
|
||||||
@ -735,34 +904,35 @@ pub mod test {
|
|||||||
|
|
||||||
impl_serdeany!(TestProgress);
|
impl_serdeany!(TestProgress);
|
||||||
|
|
||||||
impl<S, ST> StageProgress<S, ST> for TestProgress
|
impl TestProgress {
|
||||||
where
|
#[allow(clippy::unnecessary_wraps)]
|
||||||
S: HasMetadata,
|
fn restart_progress_should_run<S, ST>(state: &mut S, _stage: &ST) -> Result<bool, Error>
|
||||||
{
|
where
|
||||||
fn initialize_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
S: HasMetadata,
|
||||||
|
{
|
||||||
// check if we're resuming
|
// check if we're resuming
|
||||||
if !state.has_metadata::<Self>() {
|
let metadata = state.metadata_or_insert_with(|| Self { count: 0 });
|
||||||
state.add_metadata(Self { count: 0 });
|
|
||||||
}
|
metadata.count += 1;
|
||||||
Ok(())
|
assert!(
|
||||||
|
metadata.count == 1,
|
||||||
|
"Test failed; we resumed a succeeded stage!"
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_progress(state: &mut S, _stage: &ST) -> Result<(), Error> {
|
fn clear_restart_progress<S, ST>(state: &mut S, _stage: &ST) -> Result<(), Error>
|
||||||
if state.metadata_map_mut().remove::<Self>().is_none() {
|
where
|
||||||
|
S: HasMetadata,
|
||||||
|
{
|
||||||
|
if state.remove_metadata::<Self>().is_none() {
|
||||||
return Err(Error::illegal_state(
|
return Err(Error::illegal_state(
|
||||||
"attempted to clear status metadata when none was present",
|
"attempted to clear status metadata when none was present",
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn progress<'a>(state: &'a S, _stage: &ST) -> Result<&'a Self, Error> {
|
|
||||||
state.metadata()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn progress_mut<'a>(state: &'a mut S, _stage: &ST) -> Result<&'a mut Self, Error> {
|
|
||||||
state.metadata_mut()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S> UsesState for ResumeSucceededStage<S>
|
impl<S> UsesState for ResumeSucceededStage<S>
|
||||||
@ -779,139 +949,39 @@ pub mod test {
|
|||||||
Z: UsesState,
|
Z: UsesState,
|
||||||
Z::State: HasMetadata,
|
Z::State: HasMetadata,
|
||||||
{
|
{
|
||||||
type Progress = TestProgress;
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
_executor: &mut E,
|
_executor: &mut E,
|
||||||
state: &mut Self::State,
|
_state: &mut Self::State,
|
||||||
_manager: &mut EM,
|
_manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// metadata is attached by the status
|
|
||||||
let meta = Self::Progress::progress_mut(state, self)?;
|
|
||||||
meta.count += 1;
|
|
||||||
assert!(
|
|
||||||
meta.count == 1,
|
|
||||||
"Test failed; we resumed a succeeded stage!"
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> UsesState for ResumeFailedStage<S>
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
where
|
TestProgress::restart_progress_should_run(state, self)
|
||||||
S: State,
|
|
||||||
{
|
|
||||||
type State = S;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, EM, Z> Stage<E, EM, Z> for ResumeFailedStage<Z::State>
|
|
||||||
where
|
|
||||||
E: UsesState<State = Z::State>,
|
|
||||||
EM: UsesState<State = Z::State>,
|
|
||||||
Z: UsesState,
|
|
||||||
Z::State: HasMetadata,
|
|
||||||
{
|
|
||||||
type Progress = TestProgress;
|
|
||||||
|
|
||||||
fn perform(
|
|
||||||
&mut self,
|
|
||||||
_fuzzer: &mut Z,
|
|
||||||
_executor: &mut E,
|
|
||||||
state: &mut Self::State,
|
|
||||||
_manager: &mut EM,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
// metadata is attached by the status
|
|
||||||
let meta = Self::Progress::progress_mut(state, self)?;
|
|
||||||
meta.count += 1;
|
|
||||||
|
|
||||||
if meta.count == 1 {
|
|
||||||
return Err(Error::shutting_down());
|
|
||||||
} else if meta.count > 2 {
|
|
||||||
panic!("Resume was somehow corrupted?")
|
|
||||||
} else {
|
|
||||||
self.completed.replace(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
pub fn test_resume_stages<S>() -> (
|
|
||||||
Rc<RefCell<bool>>,
|
|
||||||
tuple_list_type!(ResumeSucceededStage<S>, ResumeFailedStage<S>),
|
|
||||||
) {
|
|
||||||
let completed = Rc::new(RefCell::new(false));
|
|
||||||
(
|
|
||||||
completed.clone(),
|
|
||||||
tuple_list!(
|
|
||||||
ResumeSucceededStage {
|
|
||||||
phantom: PhantomData
|
|
||||||
},
|
|
||||||
ResumeFailedStage {
|
|
||||||
completed,
|
|
||||||
phantom: PhantomData
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn test_resume<ST, S>(completed: &Rc<RefCell<bool>>, state: &mut S, mut stages: ST)
|
|
||||||
where
|
|
||||||
ST: StagesTuple<NopExecutor<S>, NopEventManager<S>, S, NopFuzzer<S>>,
|
|
||||||
S: State,
|
|
||||||
{
|
|
||||||
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
|
||||||
unsafe {
|
|
||||||
TestProgress::register();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut fuzzer = NopFuzzer::new();
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
let mut executor = NopExecutor::new();
|
TestProgress::clear_restart_progress(state, self)
|
||||||
let mut manager = NopEventManager::new();
|
|
||||||
|
|
||||||
for _ in 0..2 {
|
|
||||||
completed.replace(false);
|
|
||||||
let Err(e) = stages.perform_all(&mut fuzzer, &mut executor, state, &mut manager) else {
|
|
||||||
panic!("Test failed; stages should fail the first time.")
|
|
||||||
};
|
|
||||||
assert!(
|
|
||||||
matches!(e, Error::ShuttingDown),
|
|
||||||
"Unexpected error encountered."
|
|
||||||
);
|
|
||||||
assert!(!*completed.borrow(), "Unexpectedly complete?");
|
|
||||||
state
|
|
||||||
.on_restart()
|
|
||||||
.expect("Couldn't notify state of restart.");
|
|
||||||
assert!(
|
|
||||||
stages
|
|
||||||
.perform_all(&mut fuzzer, &mut executor, state, &mut manager)
|
|
||||||
.is_ok(),
|
|
||||||
"Test failed; stages should pass the second time."
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
*completed.borrow(),
|
|
||||||
"Test failed; we did not set completed."
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_tries_progress() -> Result<(), Error> {
|
fn test_tries_progress() -> Result<(), Error> {
|
||||||
|
// # Safety
|
||||||
|
// No concurrency per testcase
|
||||||
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
#[cfg(any(not(feature = "serdeany_autoreg"), miri))]
|
||||||
unsafe {
|
unsafe {
|
||||||
RetryProgress::register();
|
RetryRestartHelper::register();
|
||||||
}
|
}
|
||||||
|
|
||||||
struct StageWithOneTry;
|
struct StageWithOneTry;
|
||||||
|
|
||||||
impl RetryingStage for StageWithOneTry {
|
impl Named for StageWithOneTry {
|
||||||
fn max_retries(&self) -> usize {
|
fn name(&self) -> &str {
|
||||||
1
|
"TestStage"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -920,38 +990,48 @@ pub mod test {
|
|||||||
|
|
||||||
let corpus_idx = state.corpus_mut().add(Testcase::new(NopInput {}))?;
|
let corpus_idx = state.corpus_mut().add(Testcase::new(NopInput {}))?;
|
||||||
|
|
||||||
|
state.set_corpus_idx(corpus_idx)?;
|
||||||
|
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
// used normally, no retries means we never skip
|
// used normally, no retries means we never skip
|
||||||
RetryProgress::initialize_progress(&mut state, &stage)?;
|
assert!(RetryRestartHelper::restart_progress_should_run(
|
||||||
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
&mut state, &stage, 1
|
||||||
RetryProgress::clear_progress(&mut state, &stage)?;
|
)?);
|
||||||
|
RetryRestartHelper::clear_restart_progress(&mut state, &stage)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
for _ in 0..10 {
|
for _ in 0..10 {
|
||||||
// used normally, only one retry means we never skip
|
// used normally, only one retry means we never skip
|
||||||
RetryProgress::initialize_progress(&mut state, &stage)?;
|
assert!(RetryRestartHelper::restart_progress_should_run(
|
||||||
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
&mut state, &stage, 2
|
||||||
RetryProgress::initialize_progress(&mut state, &stage)?;
|
)?);
|
||||||
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
assert!(RetryRestartHelper::restart_progress_should_run(
|
||||||
RetryProgress::clear_progress(&mut state, &stage)?;
|
&mut state, &stage, 2
|
||||||
|
)?);
|
||||||
|
RetryRestartHelper::clear_restart_progress(&mut state, &stage)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
RetryProgress::initialize_progress(&mut state, &stage)?;
|
assert!(RetryRestartHelper::restart_progress_should_run(
|
||||||
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
&mut state, &stage, 2
|
||||||
|
)?);
|
||||||
// task failed, let's resume
|
// task failed, let's resume
|
||||||
RetryProgress::initialize_progress(&mut state, &stage)?;
|
|
||||||
// we still have one more try!
|
// we still have one more try!
|
||||||
assert!(!RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
assert!(RetryRestartHelper::restart_progress_should_run(
|
||||||
// task failed, let's resume
|
&mut state, &stage, 2
|
||||||
RetryProgress::initialize_progress(&mut state, &stage)?;
|
)?);
|
||||||
// out of retries, so now we skip
|
|
||||||
assert!(RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
// task failed, let's resume
|
||||||
RetryProgress::clear_progress(&mut state, &stage)?;
|
// out of retries, so now we skip
|
||||||
|
assert!(!RetryRestartHelper::restart_progress_should_run(
|
||||||
|
&mut state, &stage, 2
|
||||||
|
)?);
|
||||||
|
RetryRestartHelper::clear_restart_progress(&mut state, &stage)?;
|
||||||
|
|
||||||
RetryProgress::initialize_progress(&mut state, &stage)?;
|
|
||||||
// we previously exhausted this testcase's retries, so we skip
|
// we previously exhausted this testcase's retries, so we skip
|
||||||
assert!(RetryProgress::should_skip(&mut state, &stage, corpus_idx)?);
|
assert!(!RetryRestartHelper::restart_progress_should_run(
|
||||||
RetryProgress::clear_progress(&mut state, &stage)?;
|
&mut state, &stage, 2
|
||||||
|
)?);
|
||||||
|
RetryRestartHelper::clear_restart_progress(&mut state, &stage)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
//| The [`MutationalStage`] is the default stage used during fuzzing.
|
//| The [`MutationalStage`] is the default stage used during fuzzing.
|
||||||
//! For the current input, it will perform a range of random mutations, and then run them in the executor.
|
//! For the current input, it will perform a range of random mutations, and then run them in the executor.
|
||||||
|
|
||||||
use core::marker::PhantomData;
|
use core::{any::type_name, marker::PhantomData};
|
||||||
|
|
||||||
use libafl_bolts::rands::Rand;
|
use libafl_bolts::{rands::Rand, Named};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, Testcase},
|
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, Testcase},
|
||||||
@ -11,9 +11,9 @@ use crate::{
|
|||||||
inputs::Input,
|
inputs::Input,
|
||||||
mark_feature_time,
|
mark_feature_time,
|
||||||
mutators::{MultiMutator, MutationResult, Mutator},
|
mutators::{MultiMutator, MutationResult, Mutator},
|
||||||
stages::Stage,
|
stages::{ExecutionCountRestartHelper, RetryRestartHelper, Stage},
|
||||||
start_timer,
|
start_timer,
|
||||||
state::{HasCorpus, HasRand, UsesState},
|
state::{HasCorpus, HasExecutions, HasMetadata, HasNamedMetadata, HasRand, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
@ -104,7 +104,10 @@ where
|
|||||||
fn mutator_mut(&mut self) -> &mut M;
|
fn mutator_mut(&mut self) -> &mut M;
|
||||||
|
|
||||||
/// Gets the number of iterations this mutator should run for.
|
/// Gets the number of iterations this mutator should run for.
|
||||||
fn iterations(&self, state: &mut Z::State, corpus_idx: CorpusId) -> Result<u64, Error>;
|
fn iterations(&self, state: &mut Z::State) -> Result<u64, Error>;
|
||||||
|
|
||||||
|
/// Gets the number of executions this mutator already did since it got first called in this fuzz round.
|
||||||
|
fn execs_since_progress_start(&mut self, state: &mut Z::State) -> Result<u64, Error>;
|
||||||
|
|
||||||
/// Runs this (mutational) stage for the given testcase
|
/// Runs this (mutational) stage for the given testcase
|
||||||
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
|
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
|
||||||
@ -121,7 +124,7 @@ where
|
|||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
let num = self.iterations(state, corpus_idx)?;
|
let num = self.iterations(state)? - self.execs_since_progress_start(state)?;
|
||||||
|
|
||||||
start_timer!(state);
|
start_timer!(state);
|
||||||
let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut();
|
let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut();
|
||||||
@ -163,8 +166,12 @@ pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
|
|||||||
/// The default mutational stage
|
/// The default mutational stage
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct StdMutationalStage<E, EM, I, M, Z> {
|
pub struct StdMutationalStage<E, EM, I, M, Z> {
|
||||||
|
/// The mutator(s) to use
|
||||||
mutator: M,
|
mutator: M,
|
||||||
|
/// The maximum amount of iterations we should do each round
|
||||||
max_iterations: u64,
|
max_iterations: u64,
|
||||||
|
/// The progress helper for this mutational stage
|
||||||
|
restart_helper: ExecutionCountRestartHelper,
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
phantom: PhantomData<(E, EM, I, Z)>,
|
phantom: PhantomData<(E, EM, I, Z)>,
|
||||||
}
|
}
|
||||||
@ -175,7 +182,7 @@ where
|
|||||||
EM: UsesState<State = Z::State>,
|
EM: UsesState<State = Z::State>,
|
||||||
M: Mutator<I, Z::State>,
|
M: Mutator<I, Z::State>,
|
||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::State: HasCorpus + HasRand,
|
Z::State: HasCorpus + HasRand + HasExecutions + HasMetadata,
|
||||||
I: MutatedTransform<Self::Input, Self::State> + Clone,
|
I: MutatedTransform<Self::Input, Self::State> + Clone,
|
||||||
{
|
{
|
||||||
/// The mutator, added to this stage
|
/// The mutator, added to this stage
|
||||||
@ -191,9 +198,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the number of iterations as a random number
|
/// Gets the number of iterations as a random number
|
||||||
fn iterations(&self, state: &mut Z::State, _corpus_idx: CorpusId) -> Result<u64, Error> {
|
fn iterations(&self, state: &mut Z::State) -> Result<u64, Error> {
|
||||||
Ok(1 + state.rand_mut().below(self.max_iterations))
|
Ok(1 + state.rand_mut().below(self.max_iterations))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execs_since_progress_start(&mut self, state: &mut <Z>::State) -> Result<u64, Error> {
|
||||||
|
self.restart_helper.execs_since_progress_start(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, EM, I, M, Z> UsesState for StdMutationalStage<E, EM, I, M, Z>
|
impl<E, EM, I, M, Z> UsesState for StdMutationalStage<E, EM, I, M, Z>
|
||||||
@ -213,11 +224,9 @@ where
|
|||||||
EM: UsesState<State = Z::State>,
|
EM: UsesState<State = Z::State>,
|
||||||
M: Mutator<I, Z::State>,
|
M: Mutator<I, Z::State>,
|
||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::State: HasCorpus + HasRand,
|
Z::State: HasCorpus + HasRand + HasMetadata + HasExecutions,
|
||||||
I: MutatedTransform<Self::Input, Self::State> + Clone,
|
I: MutatedTransform<Self::Input, Self::State> + Clone,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO should this stage be resumed?
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::let_and_return)]
|
#[allow(clippy::let_and_return)]
|
||||||
fn perform(
|
fn perform(
|
||||||
@ -234,6 +243,14 @@ where
|
|||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
self.restart_helper.restart_progress_should_run(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
self.restart_helper.clear_restart_progress(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, EM, M, Z> StdMutationalStage<E, EM, Z::Input, M, Z>
|
impl<E, EM, M, Z> StdMutationalStage<E, EM, Z::Input, M, Z>
|
||||||
@ -273,12 +290,13 @@ where
|
|||||||
Self {
|
Self {
|
||||||
mutator,
|
mutator,
|
||||||
max_iterations,
|
max_iterations,
|
||||||
|
restart_helper: ExecutionCountRestartHelper::default(),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The default mutational stage
|
/// A mutational stage that operates on multiple inputs, as returned by [`MultiMutator::multi_mutate`].
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct MultiMutationalStage<E, EM, I, M, Z> {
|
pub struct MultiMutationalStage<E, EM, I, M, Z> {
|
||||||
mutator: M,
|
mutator: M,
|
||||||
@ -297,16 +315,39 @@ where
|
|||||||
type State = Z::State;
|
type State = Z::State;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, EM, I, M, Z> Stage<E, EM, Z> for MultiMutationalStage<E, EM, I, M, Z>
|
impl<E, EM, I, M, Z> Named for MultiMutationalStage<E, EM, I, M, Z>
|
||||||
where
|
where
|
||||||
E: UsesState<State = Z::State>,
|
E: UsesState<State = Z::State>,
|
||||||
EM: UsesState<State = Z::State>,
|
EM: UsesState<State = Z::State>,
|
||||||
M: MultiMutator<I, Z::State>,
|
M: MultiMutator<I, Z::State>,
|
||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::State: HasCorpus + HasRand,
|
Z::State: HasCorpus + HasRand,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
type_name::<Self>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, I, M, Z> Stage<E, EM, Z> for MultiMutationalStage<E, EM, I, M, Z>
|
||||||
|
where
|
||||||
|
E: UsesState<State = Z::State>,
|
||||||
|
EM: UsesState<State = Z::State>,
|
||||||
|
M: MultiMutator<I, Z::State>,
|
||||||
|
Z: Evaluator<E, EM>,
|
||||||
|
Z::State: HasCorpus + HasRand + HasNamedMetadata,
|
||||||
I: MutatedTransform<Self::Input, Self::State> + Clone,
|
I: MutatedTransform<Self::Input, Self::State> + Clone,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO implement resume
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// TODO: add proper crash/timeout handling
|
||||||
|
// For now, Make sure we don't get stuck crashing on a single testcase
|
||||||
|
RetryRestartHelper::restart_progress_should_run(state, self, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::let_and_return)]
|
#[allow(clippy::let_and_return)]
|
||||||
@ -353,7 +394,7 @@ where
|
|||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::State: HasCorpus + HasRand,
|
Z::State: HasCorpus + HasRand,
|
||||||
{
|
{
|
||||||
/// Creates a new default mutational stage
|
/// Creates a new [`MultiMutationalStage`]
|
||||||
pub fn new(mutator: M) -> Self {
|
pub fn new(mutator: M) -> Self {
|
||||||
Self::transforming(mutator)
|
Self::transforming(mutator)
|
||||||
}
|
}
|
||||||
|
@ -3,20 +3,22 @@
|
|||||||
use core::{fmt::Debug, marker::PhantomData};
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, CorpusId},
|
|
||||||
executors::{Executor, HasObservers},
|
executors::{Executor, HasObservers},
|
||||||
fuzzer::Evaluator,
|
fuzzer::Evaluator,
|
||||||
mutators::Mutator,
|
mutators::Mutator,
|
||||||
schedulers::{testcase_score::CorpusPowerTestcaseScore, TestcaseScore},
|
schedulers::{testcase_score::CorpusPowerTestcaseScore, TestcaseScore},
|
||||||
stages::{mutational::MutatedTransform, MutationalStage, Stage},
|
stages::{mutational::MutatedTransform, ExecutionCountRestartHelper, MutationalStage, Stage},
|
||||||
state::{HasCorpus, HasMetadata, HasRand, UsesState},
|
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasMetadata, HasRand, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The mutational stage using power schedules
|
/// The mutational stage using power schedules
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PowerMutationalStage<E, F, EM, I, M, Z> {
|
pub struct PowerMutationalStage<E, F, EM, I, M, Z> {
|
||||||
|
/// The mutators we use
|
||||||
mutator: M,
|
mutator: M,
|
||||||
|
/// Helper for restarts
|
||||||
|
restart_helper: ExecutionCountRestartHelper,
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
phantom: PhantomData<(E, F, EM, I, Z)>,
|
phantom: PhantomData<(E, F, EM, I, Z)>,
|
||||||
}
|
}
|
||||||
@ -34,7 +36,7 @@ where
|
|||||||
EM: UsesState<State = E::State>,
|
EM: UsesState<State = E::State>,
|
||||||
F: TestcaseScore<E::State>,
|
F: TestcaseScore<E::State>,
|
||||||
M: Mutator<I, E::State>,
|
M: Mutator<I, E::State>,
|
||||||
E::State: HasCorpus + HasMetadata + HasRand,
|
E::State: HasCorpus + HasMetadata + HasRand + HasExecutions,
|
||||||
Z: Evaluator<E, EM, State = E::State>,
|
Z: Evaluator<E, EM, State = E::State>,
|
||||||
I: MutatedTransform<E::Input, E::State> + Clone,
|
I: MutatedTransform<E::Input, E::State> + Clone,
|
||||||
{
|
{
|
||||||
@ -52,13 +54,17 @@ where
|
|||||||
|
|
||||||
/// Gets the number of iterations as a random number
|
/// Gets the number of iterations as a random number
|
||||||
#[allow(clippy::cast_sign_loss)]
|
#[allow(clippy::cast_sign_loss)]
|
||||||
fn iterations(&self, state: &mut E::State, corpus_idx: CorpusId) -> Result<u64, Error> {
|
fn iterations(&self, state: &mut E::State) -> Result<u64, Error> {
|
||||||
// Update handicap
|
// Update handicap
|
||||||
let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut();
|
let mut testcase = state.current_testcase_mut()?;
|
||||||
let score = F::compute(state, &mut *testcase)? as u64;
|
let score = F::compute(state, &mut testcase)? as u64;
|
||||||
|
|
||||||
Ok(score)
|
Ok(score)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execs_since_progress_start(&mut self, state: &mut <Z>::State) -> Result<u64, Error> {
|
||||||
|
self.restart_helper.execs_since_progress_start(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, F, EM, I, M, Z> Stage<E, EM, Z> for PowerMutationalStage<E, F, EM, I, M, Z>
|
impl<E, F, EM, I, M, Z> Stage<E, EM, Z> for PowerMutationalStage<E, F, EM, I, M, Z>
|
||||||
@ -67,12 +73,10 @@ where
|
|||||||
EM: UsesState<State = E::State>,
|
EM: UsesState<State = E::State>,
|
||||||
F: TestcaseScore<E::State>,
|
F: TestcaseScore<E::State>,
|
||||||
M: Mutator<I, E::State>,
|
M: Mutator<I, E::State>,
|
||||||
E::State: HasCorpus + HasMetadata + HasRand,
|
E::State: HasCorpus + HasMetadata + HasRand + HasExecutions,
|
||||||
Z: Evaluator<E, EM, State = E::State>,
|
Z: Evaluator<E, EM, State = E::State>,
|
||||||
I: MutatedTransform<E::Input, E::State> + Clone,
|
I: MutatedTransform<E::Input, E::State> + Clone,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO should we resume this stage?
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::let_and_return)]
|
#[allow(clippy::let_and_return)]
|
||||||
fn perform(
|
fn perform(
|
||||||
@ -85,6 +89,14 @@ where
|
|||||||
let ret = self.perform_mutational(fuzzer, executor, state, manager);
|
let ret = self.perform_mutational(fuzzer, executor, state, manager);
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
self.restart_helper.restart_progress_should_run(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
self.restart_helper.clear_restart_progress(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, F, EM, M, Z> PowerMutationalStage<E, F, EM, E::Input, M, Z>
|
impl<E, F, EM, M, Z> PowerMutationalStage<E, F, EM, E::Input, M, Z>
|
||||||
@ -116,6 +128,7 @@ where
|
|||||||
Self {
|
Self {
|
||||||
mutator,
|
mutator,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
|
restart_helper: ExecutionCountRestartHelper::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,8 +62,6 @@ where
|
|||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
E::State: HasImported + HasCorpus + HasMetadata,
|
E::State: HasImported + HasCorpus + HasMetadata,
|
||||||
{
|
{
|
||||||
type Progress = (); // this stage does not require resume
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
@ -133,6 +131,18 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// Not running the target so we wont't crash/timeout and, hence, don't need to restore anything
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
// Not running the target so we wont't crash/timeout and, hence, don't need to restore anything
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, EM, Z> AflStatsStage<E, EM, Z>
|
impl<E, EM, Z> AflStatsStage<E, EM, Z>
|
||||||
|
@ -104,8 +104,6 @@ where
|
|||||||
EM: UsesState<State = S>,
|
EM: UsesState<State = S>,
|
||||||
Z: UsesState<State = S>,
|
Z: UsesState<State = S>,
|
||||||
{
|
{
|
||||||
type Progress = (); // this stage does not need to be resumed
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
_fuzzer: &mut Z,
|
_fuzzer: &mut Z,
|
||||||
@ -132,4 +130,16 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// Stage does not run the target. No reset helper needed.
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
// Stage does not run the target. No reset helper needed.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ use std::{
|
|||||||
time::SystemTime,
|
time::SystemTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
use libafl_bolts::{current_time, shmem::ShMemProvider};
|
use libafl_bolts::{current_time, shmem::ShMemProvider, Named};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
@ -18,8 +18,8 @@ use crate::{
|
|||||||
executors::{Executor, ExitKind, HasObservers},
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
fuzzer::{Evaluator, EvaluatorObservers, ExecutionProcessor},
|
fuzzer::{Evaluator, EvaluatorObservers, ExecutionProcessor},
|
||||||
inputs::{Input, InputConverter, UsesInput},
|
inputs::{Input, InputConverter, UsesInput},
|
||||||
stages::Stage,
|
stages::{RetryRestartHelper, Stage},
|
||||||
state::{HasCorpus, HasExecutions, HasMetadata, HasRand, State, UsesState},
|
state::{HasCorpus, HasExecutions, HasMetadata, HasNamedMetadata, HasRand, State, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -59,16 +59,23 @@ where
|
|||||||
type State = E::State;
|
type State = E::State;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<CB, E, EM, Z> Named for SyncFromDiskStage<CB, E, EM, Z>
|
||||||
|
where
|
||||||
|
E: UsesState,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
self.sync_dir.to_str().unwrap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<CB, E, EM, Z> Stage<E, EM, Z> for SyncFromDiskStage<CB, E, EM, Z>
|
impl<CB, E, EM, Z> Stage<E, EM, Z> for SyncFromDiskStage<CB, E, EM, Z>
|
||||||
where
|
where
|
||||||
CB: FnMut(&mut Z, &mut Z::State, &Path) -> Result<<Z::State as UsesInput>::Input, Error>,
|
CB: FnMut(&mut Z, &mut Z::State, &Path) -> Result<<Z::State as UsesInput>::Input, Error>,
|
||||||
E: UsesState<State = Z::State>,
|
E: UsesState<State = Z::State>,
|
||||||
EM: UsesState<State = Z::State>,
|
EM: UsesState<State = Z::State>,
|
||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::State: HasCorpus + HasRand + HasMetadata,
|
Z::State: HasCorpus + HasRand + HasMetadata + HasNamedMetadata,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO load from directory should be resumed
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -103,6 +110,18 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// TODO: Needs proper crash handling for when an imported testcase crashes
|
||||||
|
// For now, Make sure we don't get stuck crashing on this testcase
|
||||||
|
RetryRestartHelper::restart_progress_should_run(state, self, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CB, E, EM, Z> SyncFromDiskStage<CB, E, EM, Z>
|
impl<CB, E, EM, Z> SyncFromDiskStage<CB, E, EM, Z>
|
||||||
@ -254,8 +273,6 @@ where
|
|||||||
ICB: InputConverter<From = DI, To = S::Input>,
|
ICB: InputConverter<From = DI, To = S::Input>,
|
||||||
DI: Input,
|
DI: Input,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO this should be resumed in the case that a testcase causes a crash
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -312,6 +329,18 @@ where
|
|||||||
state.introspection_monitor_mut().finish_stage();
|
state.introspection_monitor_mut().finish_stage();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn restart_progress_should_run(&mut self, _state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// No restart handling needed - does not execute the target.
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn clear_restart_progress(&mut self, _state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
// Not needed - does not execute the target.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<IC, ICB, DI, S, SP> SyncFromBrokerStage<IC, ICB, DI, S, SP>
|
impl<IC, ICB, DI, S, SP> SyncFromBrokerStage<IC, ICB, DI, S, SP>
|
||||||
|
@ -7,7 +7,7 @@ use ahash::RandomState;
|
|||||||
use libafl_bolts::{HasLen, Named};
|
use libafl_bolts::{HasLen, Named};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx, Testcase},
|
corpus::{Corpus, HasCurrentCorpusIdx, Testcase},
|
||||||
events::EventFirer,
|
events::EventFirer,
|
||||||
executors::{Executor, ExitKind, HasObservers},
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
feedbacks::{Feedback, FeedbackFactory, HasObserverName},
|
feedbacks::{Feedback, FeedbackFactory, HasObserverName},
|
||||||
@ -16,9 +16,12 @@ use crate::{
|
|||||||
mutators::{MutationResult, Mutator},
|
mutators::{MutationResult, Mutator},
|
||||||
observers::{MapObserver, ObserversTuple},
|
observers::{MapObserver, ObserversTuple},
|
||||||
schedulers::{RemovableScheduler, Scheduler},
|
schedulers::{RemovableScheduler, Scheduler},
|
||||||
stages::Stage,
|
stages::{ExecutionCountRestartHelper, Stage},
|
||||||
start_timer,
|
start_timer,
|
||||||
state::{HasCorpus, HasExecutions, HasMaxSize, HasSolutions, State, UsesState},
|
state::{
|
||||||
|
HasCorpus, HasCurrentTestcase, HasExecutions, HasMaxSize, HasMetadata, HasSolutions, State,
|
||||||
|
UsesState,
|
||||||
|
},
|
||||||
Error, ExecutesInput, ExecutionProcessor, HasFeedback, HasScheduler,
|
Error, ExecutesInput, ExecutionProcessor, HasFeedback, HasScheduler,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
@ -51,7 +54,7 @@ where
|
|||||||
fn mutator_mut(&mut self) -> &mut M;
|
fn mutator_mut(&mut self) -> &mut M;
|
||||||
|
|
||||||
/// Gets the number of iterations this mutator should run for.
|
/// Gets the number of iterations this mutator should run for.
|
||||||
fn iterations(&self, state: &mut CS::State, corpus_idx: CorpusId) -> Result<usize, Error>;
|
fn iterations(&self, state: &mut CS::State) -> Result<usize, Error>;
|
||||||
|
|
||||||
/// Runs this (mutational) stage for new objectives
|
/// Runs this (mutational) stage for new objectives
|
||||||
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
|
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
|
||||||
@ -70,10 +73,11 @@ where
|
|||||||
|
|
||||||
let orig_max_size = state.max_size();
|
let orig_max_size = state.max_size();
|
||||||
// basically copy-pasted from mutational.rs
|
// basically copy-pasted from mutational.rs
|
||||||
let num = self.iterations(state, base_corpus_idx)?;
|
let num = self.iterations(state)?
|
||||||
|
- usize::try_from(self.execs_since_progress_start(state)?).unwrap();
|
||||||
|
|
||||||
start_timer!(state);
|
start_timer!(state);
|
||||||
let mut base = state.corpus().cloned_input_for_id(base_corpus_idx)?;
|
let mut base = state.current_input_cloned()?;
|
||||||
let base_hash = RandomState::with_seeds(0, 0, 0, 0).hash_one(&base);
|
let base_hash = RandomState::with_seeds(0, 0, 0, 0).hash_one(&base);
|
||||||
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
||||||
|
|
||||||
@ -173,14 +177,22 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets the number of executions this mutator already did since it got first called in this fuzz round.
|
||||||
|
fn execs_since_progress_start(&mut self, state: &mut Z::State) -> Result<u64, Error>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The default corpus entry minimising mutational stage
|
/// The default corpus entry minimising mutational stage
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> {
|
pub struct StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z> {
|
||||||
|
/// The mutator(s) this stage uses
|
||||||
mutator: M,
|
mutator: M,
|
||||||
|
/// The factory
|
||||||
factory: FF,
|
factory: FF,
|
||||||
|
/// The runs (=iterations) we are supposed to do
|
||||||
runs: usize,
|
runs: usize,
|
||||||
|
/// The progress helper for this stage, keeping track of resumes after timeouts/crashes
|
||||||
|
restart_helper: ExecutionCountRestartHelper,
|
||||||
#[allow(clippy::type_complexity)]
|
#[allow(clippy::type_complexity)]
|
||||||
phantom: PhantomData<(CS, E, EM, F1, F2, OT, Z)>,
|
phantom: PhantomData<(CS, E, EM, F1, F2, OT, Z)>,
|
||||||
}
|
}
|
||||||
@ -200,7 +212,7 @@ impl<CS, E, EM, F1, F2, FF, M, OT, Z> Stage<E, EM, Z>
|
|||||||
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
|
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
|
||||||
where
|
where
|
||||||
CS: Scheduler + RemovableScheduler,
|
CS: Scheduler + RemovableScheduler,
|
||||||
CS::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize + HasCorpus,
|
CS::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize + HasCorpus + HasMetadata,
|
||||||
<CS::State as UsesInput>::Input: HasLen + Hash,
|
<CS::State as UsesInput>::Input: HasLen + Hash,
|
||||||
E: Executor<EM, Z> + HasObservers<Observers = OT, State = CS::State>,
|
E: Executor<EM, Z> + HasObservers<Observers = OT, State = CS::State>,
|
||||||
EM: EventFirer<State = CS::State>,
|
EM: EventFirer<State = CS::State>,
|
||||||
@ -214,8 +226,6 @@ where
|
|||||||
+ HasFeedback<Feedback = F1>
|
+ HasFeedback<Feedback = F1>
|
||||||
+ HasScheduler<Scheduler = CS>,
|
+ HasScheduler<Scheduler = CS>,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO this stage desperately needs a resume
|
|
||||||
|
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
@ -230,6 +240,14 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
self.restart_helper.restart_progress_should_run(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
self.restart_helper.clear_restart_progress(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS, E, EM, F1, F2, FF, M, OT, Z> FeedbackFactory<F2, Z::State, OT>
|
impl<CS, E, EM, F1, F2, FF, M, OT, Z> FeedbackFactory<F2, Z::State, OT>
|
||||||
@ -256,7 +274,7 @@ where
|
|||||||
<CS::State as UsesInput>::Input: HasLen + Hash,
|
<CS::State as UsesInput>::Input: HasLen + Hash,
|
||||||
M: Mutator<CS::Input, CS::State>,
|
M: Mutator<CS::Input, CS::State>,
|
||||||
OT: ObserversTuple<CS::State>,
|
OT: ObserversTuple<CS::State>,
|
||||||
CS::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize,
|
CS::State: HasCorpus + HasSolutions + HasExecutions + HasMaxSize + HasMetadata,
|
||||||
Z: ExecutionProcessor<OT, State = CS::State>
|
Z: ExecutionProcessor<OT, State = CS::State>
|
||||||
+ ExecutesInput<E, EM>
|
+ ExecutesInput<E, EM>
|
||||||
+ HasFeedback<Feedback = F1>
|
+ HasFeedback<Feedback = F1>
|
||||||
@ -275,9 +293,13 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Gets the number of iterations from a fixed number of runs
|
/// Gets the number of iterations from a fixed number of runs
|
||||||
fn iterations(&self, _state: &mut CS::State, _corpus_idx: CorpusId) -> Result<usize, Error> {
|
fn iterations(&self, _state: &mut CS::State) -> Result<usize, Error> {
|
||||||
Ok(self.runs)
|
Ok(self.runs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execs_since_progress_start(&mut self, state: &mut <Z>::State) -> Result<u64, Error> {
|
||||||
|
self.restart_helper.execs_since_progress_start(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<CS, E, EM, F1, F2, FF, M, OT, Z> StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
|
impl<CS, E, EM, F1, F2, FF, M, OT, Z> StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
|
||||||
@ -287,12 +309,13 @@ where
|
|||||||
Z: ExecutionProcessor<OT, State = CS::State>,
|
Z: ExecutionProcessor<OT, State = CS::State>,
|
||||||
CS::State: HasCorpus,
|
CS::State: HasCorpus,
|
||||||
{
|
{
|
||||||
/// Creates a new minimising mutational stage that will minimize provided corpus entries
|
/// Creates a new minimizing mutational stage that will minimize provided corpus entries
|
||||||
pub fn new(mutator: M, factory: FF, runs: usize) -> Self {
|
pub fn new(mutator: M, factory: FF, runs: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
mutator,
|
mutator,
|
||||||
factory,
|
factory,
|
||||||
runs,
|
runs,
|
||||||
|
restart_helper: ExecutionCountRestartHelper::default(),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,14 +2,15 @@
|
|||||||
|
|
||||||
use core::{fmt::Debug, marker::PhantomData};
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
|
use libafl_bolts::Named;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx},
|
|
||||||
executors::{Executor, HasObservers, ShadowExecutor},
|
executors::{Executor, HasObservers, ShadowExecutor},
|
||||||
mark_feature_time,
|
mark_feature_time,
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
stages::{RetryProgress, RetryingStage, Stage},
|
stages::{RetryRestartHelper, Stage},
|
||||||
start_timer,
|
start_timer,
|
||||||
state::{HasCorpus, HasExecutions, HasNamedMetadata, State, UsesState},
|
state::{HasCorpus, HasCurrentTestcase, HasExecutions, HasNamedMetadata, State, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
@ -38,18 +39,17 @@ where
|
|||||||
EM: UsesState<State = TE::State>,
|
EM: UsesState<State = TE::State>,
|
||||||
Z: UsesState<State = TE::State>,
|
Z: UsesState<State = TE::State>,
|
||||||
{
|
{
|
||||||
/// Perform tracing on the given [`CorpusId`]. Useful for if wrapping [`TracingStage`] with your
|
/// Perform tracing on the given `CorpusId`. Useful for if wrapping [`TracingStage`] with your
|
||||||
/// own stage and you need to manage [`super::StageProgress`] differently; see
|
/// own stage and you need to manage [`super::NestedStageRestartHelper`] differently; see
|
||||||
/// [`super::ConcolicTracingStage`]'s implementation as an example of usage.
|
/// [`super::ConcolicTracingStage`]'s implementation as an example of usage.
|
||||||
pub fn trace(
|
pub fn trace(
|
||||||
&mut self,
|
&mut self,
|
||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
state: &mut TE::State,
|
state: &mut TE::State,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
corpus_idx: CorpusId,
|
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
start_timer!(state);
|
start_timer!(state);
|
||||||
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
let input = state.current_input_cloned()?;
|
||||||
|
|
||||||
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
||||||
|
|
||||||
@ -85,8 +85,6 @@ where
|
|||||||
EM: UsesState<State = TE::State>,
|
EM: UsesState<State = TE::State>,
|
||||||
Z: UsesState<State = TE::State>,
|
Z: UsesState<State = TE::State>,
|
||||||
{
|
{
|
||||||
type Progress = RetryProgress;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -95,24 +93,21 @@ where
|
|||||||
state: &mut TE::State,
|
state: &mut TE::State,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
self.trace(fuzzer, state, manager)
|
||||||
return Err(Error::illegal_state(
|
}
|
||||||
"state is not currently processing a corpus index",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
if Self::Progress::should_skip(state, self, corpus_idx)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
self.trace(fuzzer, state, manager, corpus_idx)?;
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
RetryRestartHelper::restart_progress_should_run(state, self, self.max_retries)
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EM, TE, Z> RetryingStage for TracingStage<EM, TE, Z> {
|
impl<EM, TE, Z> Named for TracingStage<EM, TE, Z> {
|
||||||
fn max_retries(&self) -> usize {
|
fn name(&self) -> &str {
|
||||||
self.max_retries
|
"TracingStage"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,6 +155,15 @@ where
|
|||||||
type State = E::State;
|
type State = E::State;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E, EM, SOT, Z> Named for ShadowTracingStage<E, EM, SOT, Z>
|
||||||
|
where
|
||||||
|
E: UsesState,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"ShadowTracingStage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E, EM, SOT, Z> Stage<ShadowExecutor<E, SOT>, EM, Z> for ShadowTracingStage<E, EM, SOT, Z>
|
impl<E, EM, SOT, Z> Stage<ShadowExecutor<E, SOT>, EM, Z> for ShadowTracingStage<E, EM, SOT, Z>
|
||||||
where
|
where
|
||||||
E: Executor<EM, Z> + HasObservers,
|
E: Executor<EM, Z> + HasObservers,
|
||||||
@ -168,8 +172,6 @@ where
|
|||||||
Z: UsesState<State = E::State>,
|
Z: UsesState<State = E::State>,
|
||||||
E::State: State + HasExecutions + HasCorpus + HasNamedMetadata + Debug,
|
E::State: State + HasExecutions + HasCorpus + HasNamedMetadata + Debug,
|
||||||
{
|
{
|
||||||
type Progress = RetryProgress;
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -178,17 +180,8 @@ where
|
|||||||
state: &mut E::State,
|
state: &mut E::State,
|
||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let Some(corpus_idx) = state.current_corpus_idx()? else {
|
|
||||||
return Err(Error::illegal_state(
|
|
||||||
"state is not currently processing a corpus index",
|
|
||||||
));
|
|
||||||
};
|
|
||||||
if Self::Progress::should_skip(state, self, corpus_idx)? {
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
start_timer!(state);
|
start_timer!(state);
|
||||||
let input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
let input = state.current_input_cloned()?;
|
||||||
|
|
||||||
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
|
||||||
|
|
||||||
@ -216,11 +209,13 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, EM, SOT, Z> RetryingStage for ShadowTracingStage<E, EM, SOT, Z> {
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
fn max_retries(&self) -> usize {
|
RetryRestartHelper::restart_progress_should_run(state, self, self.max_retries)
|
||||||
self.max_retries
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,15 +7,15 @@ use libafl_bolts::{current_time, impl_serdeany, rands::Rand};
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, CorpusId, HasCurrentCorpusIdx},
|
corpus::{Corpus, HasCurrentCorpusIdx},
|
||||||
mark_feature_time,
|
mark_feature_time,
|
||||||
mutators::{MutationResult, Mutator},
|
mutators::{MutationResult, Mutator},
|
||||||
stages::{
|
stages::{
|
||||||
mutational::{MutatedTransform, MutatedTransformPost, DEFAULT_MUTATIONAL_MAX_ITERATIONS},
|
mutational::{MutatedTransform, MutatedTransformPost, DEFAULT_MUTATIONAL_MAX_ITERATIONS},
|
||||||
MutationalStage, Stage,
|
ExecutionCountRestartHelper, MutationalStage, Stage,
|
||||||
},
|
},
|
||||||
start_timer,
|
start_timer,
|
||||||
state::{HasCorpus, HasMetadata, HasNamedMetadata, HasRand, UsesState},
|
state::{HasCorpus, HasExecutions, HasMetadata, HasNamedMetadata, HasRand, UsesState},
|
||||||
Error, Evaluator,
|
Error, Evaluator,
|
||||||
};
|
};
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
@ -150,8 +150,12 @@ where
|
|||||||
/// A [`crate::stages::MutationalStage`] where the mutator iteration can be tuned at runtime
|
/// A [`crate::stages::MutationalStage`] where the mutator iteration can be tuned at runtime
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct TuneableMutationalStage<E, EM, I, M, Z> {
|
pub struct TuneableMutationalStage<E, EM, I, M, Z> {
|
||||||
|
/// The mutator we use
|
||||||
mutator: M,
|
mutator: M,
|
||||||
|
/// The name of this stage
|
||||||
name: String,
|
name: String,
|
||||||
|
/// The progress helper we use to keep track of progress across restarts
|
||||||
|
restart_helper: ExecutionCountRestartHelper,
|
||||||
phantom: PhantomData<(E, EM, I, Z)>,
|
phantom: PhantomData<(E, EM, I, Z)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +165,7 @@ where
|
|||||||
EM: UsesState<State = Z::State>,
|
EM: UsesState<State = Z::State>,
|
||||||
M: Mutator<I, Z::State>,
|
M: Mutator<I, Z::State>,
|
||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::State: HasCorpus + HasRand + HasNamedMetadata + HasMetadata,
|
Z::State: HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions,
|
||||||
I: MutatedTransform<Z::Input, Z::State> + Clone,
|
I: MutatedTransform<Z::Input, Z::State> + Clone,
|
||||||
{
|
{
|
||||||
/// Runs this (mutational) stage for the given `testcase`
|
/// Runs this (mutational) stage for the given `testcase`
|
||||||
@ -222,7 +226,7 @@ where
|
|||||||
}
|
}
|
||||||
(None, None) => {
|
(None, None) => {
|
||||||
// fall back to random
|
// fall back to random
|
||||||
let iters = self.iterations(state, corpus_idx)?;
|
let iters = self.iterations(state)? - self.execs_since_progress_start(state)?;
|
||||||
for i in 1..=iters {
|
for i in 1..=iters {
|
||||||
self.perform_mutation(fuzzer, executor, state, manager, &input, i)?;
|
self.perform_mutation(fuzzer, executor, state, manager, &input, i)?;
|
||||||
}
|
}
|
||||||
@ -245,12 +249,16 @@ where
|
|||||||
|
|
||||||
/// Gets the number of iterations as a random number
|
/// Gets the number of iterations as a random number
|
||||||
#[allow(clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
fn iterations(&self, state: &mut Z::State, _corpus_idx: CorpusId) -> Result<u64, Error> {
|
fn iterations(&self, state: &mut Z::State) -> Result<u64, Error> {
|
||||||
Ok(
|
Ok(
|
||||||
// fall back to random
|
// fall back to random
|
||||||
1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS),
|
1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execs_since_progress_start(&mut self, state: &mut <Z>::State) -> Result<u64, Error> {
|
||||||
|
self.restart_helper.execs_since_progress_start(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, EM, I, M, Z> UsesState for TuneableMutationalStage<E, EM, I, M, Z>
|
impl<E, EM, I, M, Z> UsesState for TuneableMutationalStage<E, EM, I, M, Z>
|
||||||
@ -259,7 +267,7 @@ where
|
|||||||
EM: UsesState<State = Z::State>,
|
EM: UsesState<State = Z::State>,
|
||||||
M: Mutator<I, Z::State>,
|
M: Mutator<I, Z::State>,
|
||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::State: HasCorpus + HasRand,
|
Z::State: HasCorpus + HasRand + HasExecutions,
|
||||||
I: MutatedTransform<Z::Input, Z::State> + Clone,
|
I: MutatedTransform<Z::Input, Z::State> + Clone,
|
||||||
{
|
{
|
||||||
type State = Z::State;
|
type State = Z::State;
|
||||||
@ -271,11 +279,9 @@ where
|
|||||||
EM: UsesState<State = Z::State>,
|
EM: UsesState<State = Z::State>,
|
||||||
M: Mutator<I, Z::State>,
|
M: Mutator<I, Z::State>,
|
||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::State: HasCorpus + HasRand + HasNamedMetadata + HasMetadata,
|
Z::State: HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions,
|
||||||
I: MutatedTransform<Z::Input, Z::State> + Clone,
|
I: MutatedTransform<Z::Input, Z::State> + Clone,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO should this stage be resumed?
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(clippy::let_and_return)]
|
#[allow(clippy::let_and_return)]
|
||||||
fn perform(
|
fn perform(
|
||||||
@ -292,6 +298,14 @@ where
|
|||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
self.restart_helper.restart_progress_should_run(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
self.restart_helper.clear_restart_progress(state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<E, EM, I, M, Z> TuneableMutationalStage<E, EM, I, M, Z>
|
impl<E, EM, I, M, Z> TuneableMutationalStage<E, EM, I, M, Z>
|
||||||
@ -300,7 +314,7 @@ where
|
|||||||
EM: UsesState<State = Z::State>,
|
EM: UsesState<State = Z::State>,
|
||||||
M: Mutator<I, Z::State>,
|
M: Mutator<I, Z::State>,
|
||||||
Z: Evaluator<E, EM>,
|
Z: Evaluator<E, EM>,
|
||||||
Z::State: HasCorpus + HasRand + HasNamedMetadata + HasMetadata,
|
Z::State: HasCorpus + HasRand + HasNamedMetadata + HasMetadata + HasExecutions,
|
||||||
I: MutatedTransform<Z::Input, Z::State> + Clone,
|
I: MutatedTransform<Z::Input, Z::State> + Clone,
|
||||||
{
|
{
|
||||||
/// Creates a new default tuneable mutational stage
|
/// Creates a new default tuneable mutational stage
|
||||||
@ -473,12 +487,11 @@ where
|
|||||||
/// Creates a new tranforming mutational stage
|
/// Creates a new tranforming mutational stage
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn transforming(state: &mut Z::State, mutator: M, name: &str) -> Self {
|
pub fn transforming(state: &mut Z::State, mutator: M, name: &str) -> Self {
|
||||||
if !state.has_named_metadata::<TuneableMutationalStageMetadata>(name) {
|
let _ = state.named_metadata_or_insert_with(name, TuneableMutationalStageMetadata::default);
|
||||||
state.add_named_metadata(TuneableMutationalStageMetadata::default(), name);
|
|
||||||
}
|
|
||||||
Self {
|
Self {
|
||||||
mutator,
|
mutator,
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
restart_helper: ExecutionCountRestartHelper::default(),
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
//! The fuzzer, and state are the core pieces of every good fuzzer
|
//! The fuzzer, and state are the core pieces of every good fuzzer
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::{boxed::Box, vec::Vec};
|
||||||
use core::{
|
use core::{
|
||||||
|
borrow::BorrowMut,
|
||||||
cell::{Ref, RefMut},
|
cell::{Ref, RefMut},
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
@ -170,7 +171,27 @@ pub trait HasMetadata {
|
|||||||
self.metadata_map_mut().insert(meta);
|
self.metadata_map_mut().insert(meta);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Gets metadata, or inserts it using the given construction function `default`
|
||||||
|
fn metadata_or_insert_with<M>(&mut self, default: impl FnOnce() -> M) -> &mut M
|
||||||
|
where
|
||||||
|
M: SerdeAny,
|
||||||
|
{
|
||||||
|
self.metadata_map_mut().or_insert_with::<M>(default)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove a metadata from the metadata map
|
||||||
|
#[inline]
|
||||||
|
fn remove_metadata<M>(&mut self) -> Option<Box<M>>
|
||||||
|
where
|
||||||
|
M: SerdeAny,
|
||||||
|
{
|
||||||
|
self.metadata_map_mut().remove::<M>()
|
||||||
|
}
|
||||||
|
|
||||||
/// Check for a metadata
|
/// Check for a metadata
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// For performance reasons, you likely want to use [`Self::metadata_or_insert_with`] instead
|
||||||
#[inline]
|
#[inline]
|
||||||
fn has_metadata<M>(&self) -> bool
|
fn has_metadata<M>(&self) -> bool
|
||||||
where
|
where
|
||||||
@ -211,14 +232,39 @@ pub trait HasNamedMetadata {
|
|||||||
|
|
||||||
/// Add a metadata to the metadata map
|
/// Add a metadata to the metadata map
|
||||||
#[inline]
|
#[inline]
|
||||||
fn add_named_metadata<M>(&mut self, meta: M, name: &str)
|
fn add_named_metadata<M>(&mut self, name: &str, meta: M)
|
||||||
where
|
where
|
||||||
M: SerdeAny,
|
M: SerdeAny,
|
||||||
{
|
{
|
||||||
self.named_metadata_map_mut().insert(meta, name);
|
self.named_metadata_map_mut().insert(name, meta);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a metadata to the metadata map
|
||||||
|
#[inline]
|
||||||
|
fn remove_named_metadata<M>(&mut self, name: &str) -> Option<Box<M>>
|
||||||
|
where
|
||||||
|
M: SerdeAny,
|
||||||
|
{
|
||||||
|
self.named_metadata_map_mut().remove::<M>(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets metadata, or inserts it using the given construction function `default`
|
||||||
|
fn named_metadata_or_insert_with<M>(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
default: impl FnOnce() -> M,
|
||||||
|
) -> &mut M
|
||||||
|
where
|
||||||
|
M: SerdeAny,
|
||||||
|
{
|
||||||
|
self.named_metadata_map_mut()
|
||||||
|
.or_insert_with::<M>(name, default)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Check for a metadata
|
/// Check for a metadata
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// You likely want to use [`Self::named_metadata_or_insert_with`] for performance reasons.
|
||||||
#[inline]
|
#[inline]
|
||||||
fn has_named_metadata<M>(&self, name: &str) -> bool
|
fn has_named_metadata<M>(&self, name: &str) -> bool
|
||||||
where
|
where
|
||||||
@ -255,10 +301,10 @@ pub trait HasNamedMetadata {
|
|||||||
/// Trait for the execution counter
|
/// Trait for the execution counter
|
||||||
pub trait HasExecutions {
|
pub trait HasExecutions {
|
||||||
/// The executions counter
|
/// The executions counter
|
||||||
fn executions(&self) -> &usize;
|
fn executions(&self) -> &u64;
|
||||||
|
|
||||||
/// The executions counter (mutable)
|
/// The executions counter (mutable)
|
||||||
fn executions_mut(&mut self) -> &mut usize;
|
fn executions_mut(&mut self) -> &mut u64;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trait for some stats of AFL
|
/// Trait for some stats of AFL
|
||||||
@ -301,7 +347,7 @@ pub struct StdState<I, C, R, SC> {
|
|||||||
/// RNG instance
|
/// RNG instance
|
||||||
rand: R,
|
rand: R,
|
||||||
/// How many times the executor ran the harness/target
|
/// How many times the executor ran the harness/target
|
||||||
executions: usize,
|
executions: u64,
|
||||||
/// At what time the fuzzing started
|
/// At what time the fuzzing started
|
||||||
start_time: Duration,
|
start_time: Duration,
|
||||||
/// the number of new paths that imported from other fuzzers
|
/// the number of new paths that imported from other fuzzers
|
||||||
@ -406,7 +452,10 @@ where
|
|||||||
R: Rand,
|
R: Rand,
|
||||||
{
|
{
|
||||||
/// To get the testcase
|
/// To get the testcase
|
||||||
fn testcase(&self, id: CorpusId) -> Result<Ref<Testcase<<Self as UsesInput>::Input>>, Error> {
|
fn testcase(
|
||||||
|
&self,
|
||||||
|
id: CorpusId,
|
||||||
|
) -> Result<Ref<'_, Testcase<<Self as UsesInput>::Input>>, Error> {
|
||||||
Ok(self.corpus().get(id)?.borrow())
|
Ok(self.corpus().get(id)?.borrow())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -414,7 +463,7 @@ where
|
|||||||
fn testcase_mut(
|
fn testcase_mut(
|
||||||
&self,
|
&self,
|
||||||
id: CorpusId,
|
id: CorpusId,
|
||||||
) -> Result<RefMut<Testcase<<Self as UsesInput>::Input>>, Error> {
|
) -> Result<RefMut<'_, Testcase<<Self as UsesInput>::Input>>, Error> {
|
||||||
Ok(self.corpus().get(id)?.borrow_mut())
|
Ok(self.corpus().get(id)?.borrow_mut())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -470,13 +519,13 @@ impl<I, C, R, SC> HasNamedMetadata for StdState<I, C, R, SC> {
|
|||||||
impl<I, C, R, SC> HasExecutions for StdState<I, C, R, SC> {
|
impl<I, C, R, SC> HasExecutions for StdState<I, C, R, SC> {
|
||||||
/// The executions counter
|
/// The executions counter
|
||||||
#[inline]
|
#[inline]
|
||||||
fn executions(&self) -> &usize {
|
fn executions(&self) -> &u64 {
|
||||||
&self.executions
|
&self.executions
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The executions counter (mutable)
|
/// The executions counter (mutable)
|
||||||
#[inline]
|
#[inline]
|
||||||
fn executions_mut(&mut self) -> &mut usize {
|
fn executions_mut(&mut self) -> &mut u64 {
|
||||||
&mut self.executions
|
&mut self.executions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -549,6 +598,64 @@ impl<I, C, R, SC> HasCurrentCorpusIdx for StdState<I, C, R, SC> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Has information about the current [`Testcase`] we are fuzzing
|
||||||
|
pub trait HasCurrentTestcase<I>
|
||||||
|
where
|
||||||
|
I: Input,
|
||||||
|
{
|
||||||
|
/// Gets the current [`Testcase`] we are fuzzing
|
||||||
|
///
|
||||||
|
/// Will return [`Error::key_not_found`] if no `corpus_idx` is currently set.
|
||||||
|
fn current_testcase(&self) -> Result<Ref<'_, Testcase<I>>, Error>;
|
||||||
|
//fn current_testcase(&self) -> Result<&Testcase<I>, Error>;
|
||||||
|
|
||||||
|
/// Gets the current [`Testcase`] we are fuzzing (mut)
|
||||||
|
///
|
||||||
|
/// Will return [`Error::key_not_found`] if no `corpus_idx` is currently set.
|
||||||
|
fn current_testcase_mut(&self) -> Result<RefMut<'_, Testcase<I>>, Error>;
|
||||||
|
//fn current_testcase_mut(&self) -> Result<&mut Testcase<I>, Error>;
|
||||||
|
|
||||||
|
/// Gets a cloned representation of the current [`Testcase`].
|
||||||
|
///
|
||||||
|
/// Will return [`Error::key_not_found`] if no `corpus_idx` is currently set.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
/// This allocates memory and copies the contents!
|
||||||
|
/// For performance reasons, if you just need to access the testcase, use [`Self::current_testcase`] instead.
|
||||||
|
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> {
|
impl<I, C, R, SC> HasCurrentStage for StdState<I, C, R, SC> {
|
||||||
fn set_stage(&mut self, idx: usize) -> Result<(), Error> {
|
fn set_stage(&mut self, idx: usize) -> Result<(), Error> {
|
||||||
// ensure we are in the right frame
|
// ensure we are in the right frame
|
||||||
@ -1115,7 +1222,7 @@ impl<I, C, R, SC> HasScalabilityMonitor for StdState<I, C, R, SC> {
|
|||||||
#[derive(Debug, Serialize, Deserialize, Default)]
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
||||||
pub struct NopState<I> {
|
pub struct NopState<I> {
|
||||||
metadata: SerdeAnyMap,
|
metadata: SerdeAnyMap,
|
||||||
execution: usize,
|
execution: u64,
|
||||||
rand: StdRand,
|
rand: StdRand,
|
||||||
phantom: PhantomData<I>,
|
phantom: PhantomData<I>,
|
||||||
}
|
}
|
||||||
@ -1141,11 +1248,11 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<I> HasExecutions for NopState<I> {
|
impl<I> HasExecutions for NopState<I> {
|
||||||
fn executions(&self) -> &usize {
|
fn executions(&self) -> &u64 {
|
||||||
&self.execution
|
&self.execution
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executions_mut(&mut self) -> &mut usize {
|
fn executions_mut(&mut self) -> &mut u64 {
|
||||||
&mut self.execution
|
&mut self.execution
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1239,11 +1346,7 @@ pub mod test {
|
|||||||
use libafl_bolts::rands::StdRand;
|
use libafl_bolts::rands::StdRand;
|
||||||
|
|
||||||
use super::StdState;
|
use super::StdState;
|
||||||
use crate::{
|
use crate::{corpus::InMemoryCorpus, inputs::Input};
|
||||||
corpus::InMemoryCorpus,
|
|
||||||
inputs::{Input, NopInput},
|
|
||||||
stages::test::{test_resume, test_resume_stages},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn test_std_state<I: Input>() -> StdState<I, InMemoryCorpus<I>, StdRand, InMemoryCorpus<I>>
|
pub fn test_std_state<I: Input>() -> StdState<I, InMemoryCorpus<I>, StdRand, InMemoryCorpus<I>>
|
||||||
@ -1257,14 +1360,6 @@ pub mod test {
|
|||||||
)
|
)
|
||||||
.expect("couldn't instantiate the test state")
|
.expect("couldn't instantiate the test state")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn resume_simple() {
|
|
||||||
let mut state = test_std_state::<NopInput>();
|
|
||||||
let (completed, stages) = test_resume_stages();
|
|
||||||
|
|
||||||
test_resume(&completed, &mut state, stages);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "python")]
|
#[cfg(feature = "python")]
|
||||||
@ -1360,7 +1455,7 @@ pub mod pybind {
|
|||||||
self.inner.as_ref().solutions().clone()
|
self.inner.as_ref().solutions().clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn executions(&self) -> usize {
|
fn executions(&self) -> u64 {
|
||||||
*self.inner.as_ref().executions()
|
*self.inner.as_ref().executions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -442,7 +442,7 @@ impl Display for Error {
|
|||||||
display_error_backtrace(f, b)
|
display_error_backtrace(f, b)
|
||||||
}
|
}
|
||||||
Self::KeyNotFound(s, b) => {
|
Self::KeyNotFound(s, b) => {
|
||||||
write!(f, "Key `{0}` not in Corpus", &s)?;
|
write!(f, "Key: `{0}` - not found", &s)?;
|
||||||
display_error_backtrace(f, b)
|
display_error_backtrace(f, b)
|
||||||
}
|
}
|
||||||
Self::Empty(s, b) => {
|
Self::Empty(s, b) => {
|
||||||
|
@ -67,7 +67,7 @@ where
|
|||||||
pub mod serdeany_registry {
|
pub mod serdeany_registry {
|
||||||
|
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
use core::{any::TypeId, fmt};
|
use core::{any::TypeId, fmt, hash::BuildHasherDefault};
|
||||||
|
|
||||||
use hashbrown::{
|
use hashbrown::{
|
||||||
hash_map::{Keys, Values, ValuesMut},
|
hash_map::{Keys, Values, ValuesMut},
|
||||||
@ -75,6 +75,7 @@ pub mod serdeany_registry {
|
|||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::SerdeAny;
|
||||||
use crate::{
|
use crate::{
|
||||||
anymap::{pack_type_id, unpack_type_id},
|
anymap::{pack_type_id, unpack_type_id},
|
||||||
hash_std,
|
hash_std,
|
||||||
@ -180,7 +181,7 @@ pub mod serdeany_registry {
|
|||||||
#[allow(clippy::unsafe_derive_deserialize)]
|
#[allow(clippy::unsafe_derive_deserialize)]
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
pub struct SerdeAnyMap {
|
pub struct SerdeAnyMap {
|
||||||
map: HashMap<u128, Box<dyn crate::serdeany::SerdeAny>>,
|
map: HashMap<u128, Box<dyn SerdeAny>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cloning by serializing and deserializing. It ain't fast, but it's honest work.
|
// Cloning by serializing and deserializing. It ain't fast, but it's honest work.
|
||||||
@ -257,7 +258,24 @@ pub mod serdeany_registry {
|
|||||||
|
|
||||||
/// Insert a boxed element into the map.
|
/// Insert a boxed element into the map.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn insert_boxed<T>(&mut self, t: Box<T>)
|
pub fn insert_boxed<T>(&mut self, value: Box<T>)
|
||||||
|
where
|
||||||
|
T: crate::serdeany::SerdeAny,
|
||||||
|
{
|
||||||
|
self.entry::<T>().insert(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an entry to an element in this map.
|
||||||
|
#[inline]
|
||||||
|
#[allow(unused_qualifications)]
|
||||||
|
pub fn entry<T>(
|
||||||
|
&mut self,
|
||||||
|
) -> hashbrown::hash_map::Entry<
|
||||||
|
'_,
|
||||||
|
u128,
|
||||||
|
Box<dyn SerdeAny + 'static>,
|
||||||
|
BuildHasherDefault<ahash::AHasher>,
|
||||||
|
>
|
||||||
where
|
where
|
||||||
T: crate::serdeany::SerdeAny,
|
T: crate::serdeany::SerdeAny,
|
||||||
{
|
{
|
||||||
@ -271,11 +289,28 @@ pub mod serdeany_registry {
|
|||||||
.get(&id)
|
.get(&id)
|
||||||
.is_some()
|
.is_some()
|
||||||
},
|
},
|
||||||
"Type {} was inserted without registration! Call {}::register or use serde_autoreg.",
|
"Type {} was inserted without registration! Call RegistryBuilder::register::<{}>() or use serde_autoreg.",
|
||||||
core::any::type_name::<T>(),
|
core::any::type_name::<T>(),
|
||||||
core::any::type_name::<T>()
|
core::any::type_name::<T>()
|
||||||
);
|
);
|
||||||
self.map.insert(id, t);
|
self.map.entry(id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a value by type, or inserts it using the given construction function `default`
|
||||||
|
pub fn or_insert_with<T>(&mut self, default: impl FnOnce() -> T) -> &mut T
|
||||||
|
where
|
||||||
|
T: SerdeAny,
|
||||||
|
{
|
||||||
|
self.or_insert_with_boxed::<T>(|| Box::new(default()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a value by type, or inserts it using the given construction function `default` (returning a boxed value)
|
||||||
|
pub fn or_insert_with_boxed<T>(&mut self, default: impl FnOnce() -> Box<T>) -> &mut T
|
||||||
|
where
|
||||||
|
T: SerdeAny + 'static,
|
||||||
|
{
|
||||||
|
let ret = self.entry::<T>().or_insert_with(|| default());
|
||||||
|
ret.as_mut().as_any_mut().downcast_mut::<T>().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the count of elements in this map.
|
/// Returns the count of elements in this map.
|
||||||
@ -350,6 +385,21 @@ pub mod serdeany_registry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remove an element by type and name
|
||||||
|
#[must_use]
|
||||||
|
#[inline]
|
||||||
|
pub fn remove<T>(&mut self, name: &str) -> Option<Box<T>>
|
||||||
|
where
|
||||||
|
T: crate::serdeany::SerdeAny,
|
||||||
|
{
|
||||||
|
match self.map.get_mut(&unpack_type_id(TypeId::of::<T>())) {
|
||||||
|
None => None,
|
||||||
|
Some(h) => h
|
||||||
|
.remove(&hash_std(name.as_bytes()))
|
||||||
|
.map(|x| x.as_any_boxed().downcast::<T>().unwrap()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Get an element of a given type contained in this map by [`TypeId`].
|
/// Get an element of a given type contained in this map by [`TypeId`].
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
@ -532,7 +582,25 @@ pub mod serdeany_registry {
|
|||||||
/// Insert an element into this map.
|
/// Insert an element into this map.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[allow(unused_qualifications)]
|
#[allow(unused_qualifications)]
|
||||||
pub fn insert<T>(&mut self, val: T, name: &str)
|
pub fn insert<T>(&mut self, name: &str, val: T)
|
||||||
|
where
|
||||||
|
T: crate::serdeany::SerdeAny,
|
||||||
|
{
|
||||||
|
self.entry::<T>(name).insert(Box::new(val));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get an entry to an element into this map.
|
||||||
|
#[inline]
|
||||||
|
#[allow(unused_qualifications)]
|
||||||
|
pub fn entry<T>(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
) -> hashbrown::hash_map::Entry<
|
||||||
|
'_,
|
||||||
|
u64,
|
||||||
|
Box<dyn SerdeAny + 'static>,
|
||||||
|
BuildHasherDefault<ahash::AHasher>,
|
||||||
|
>
|
||||||
where
|
where
|
||||||
T: crate::serdeany::SerdeAny,
|
T: crate::serdeany::SerdeAny,
|
||||||
{
|
{
|
||||||
@ -546,17 +614,36 @@ pub mod serdeany_registry {
|
|||||||
.get(&id)
|
.get(&id)
|
||||||
.is_some()
|
.is_some()
|
||||||
},
|
},
|
||||||
"Type {} was inserted without registration! Call {}::register or use serde_autoreg.",
|
"Type {} was inserted without registration! Call RegistryBuilder::register::<{}>() or use serde_autoreg.",
|
||||||
core::any::type_name::<T>(),
|
core::any::type_name::<T>(),
|
||||||
core::any::type_name::<T>()
|
core::any::type_name::<T>()
|
||||||
);
|
);
|
||||||
if !self.map.contains_key(&id) {
|
|
||||||
self.map.insert(id, HashMap::default());
|
|
||||||
}
|
|
||||||
self.map
|
self.map
|
||||||
.get_mut(&id)
|
.entry(id)
|
||||||
.unwrap()
|
.or_default()
|
||||||
.insert(hash_std(name.as_bytes()), Box::new(val));
|
.entry(hash_std(name.as_bytes()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a value by name, or inserts it using the given construction function `default`
|
||||||
|
pub fn or_insert_with<T>(&mut self, name: &str, default: impl FnOnce() -> T) -> &mut T
|
||||||
|
where
|
||||||
|
T: SerdeAny,
|
||||||
|
{
|
||||||
|
let ret = self.entry::<T>(name).or_insert_with(|| Box::new(default()));
|
||||||
|
ret.as_mut().as_any_mut().downcast_mut::<T>().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets a value by name, or inserts it using the given construction function `default` (returning a boxed value)
|
||||||
|
pub fn or_insert_with_boxed<T>(
|
||||||
|
&mut self,
|
||||||
|
name: &str,
|
||||||
|
default: impl FnOnce() -> Box<T>,
|
||||||
|
) -> &mut T
|
||||||
|
where
|
||||||
|
T: SerdeAny + 'static,
|
||||||
|
{
|
||||||
|
let ret = self.entry::<T>(name).or_insert_with(|| default());
|
||||||
|
ret.as_mut().as_any_mut().downcast_mut::<T>().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the `len` of this map.
|
/// Returns the `len` of this map.
|
||||||
|
@ -30,8 +30,7 @@
|
|||||||
#![allow(
|
#![allow(
|
||||||
clippy::module_name_repetitions,
|
clippy::module_name_repetitions,
|
||||||
clippy::missing_panics_doc,
|
clippy::missing_panics_doc,
|
||||||
clippy::pub_underscore_fields,
|
clippy::pub_underscore_fields
|
||||||
clippy::mixed_attributes_style
|
|
||||||
)]
|
)]
|
||||||
|
|
||||||
pub mod filter;
|
pub mod filter;
|
||||||
@ -40,6 +39,7 @@ pub mod tracing;
|
|||||||
// The following exports are used by the `export_runtime` macro. They are therefore exported, but hidden from docs, as they are not supposed to be used directly by the user.
|
// The following exports are used by the `export_runtime` macro. They are therefore exported, but hidden from docs, as they are not supposed to be used directly by the user.
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
#[allow(clippy::mixed_attributes_style)]
|
||||||
pub mod cpp_runtime {
|
pub mod cpp_runtime {
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
@ -498,13 +498,7 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
let state = state.expect("The gen_unique_edge_ids hook works only for in-process fuzzing");
|
let state = state.expect("The gen_unique_edge_ids hook works only for in-process fuzzing");
|
||||||
if state.metadata_map().get::<QemuEdgesMapMetadata>().is_none() {
|
let meta = state.metadata_or_insert_with(QemuEdgesMapMetadata::new);
|
||||||
state.add_metadata(QemuEdgesMapMetadata::new());
|
|
||||||
}
|
|
||||||
let meta = state
|
|
||||||
.metadata_map_mut()
|
|
||||||
.get_mut::<QemuEdgesMapMetadata>()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
match meta.map.entry((src, dest)) {
|
match meta.map.entry((src, dest)) {
|
||||||
Entry::Occupied(e) => {
|
Entry::Occupied(e) => {
|
||||||
|
@ -36,12 +36,14 @@ pyo3-build-config = { version = "0.18", optional = true }
|
|||||||
libafl = { path = "../libafl", version = "0.11.2" }
|
libafl = { path = "../libafl", version = "0.11.2" }
|
||||||
libafl_bolts = { path = "../libafl_bolts", version = "0.11.2" }
|
libafl_bolts = { path = "../libafl_bolts", version = "0.11.2" }
|
||||||
libafl_targets = { path = "../libafl_targets", version = "0.11.2" }
|
libafl_targets = { path = "../libafl_targets", version = "0.11.2" }
|
||||||
libafl_qemu = { path = "../libafl_qemu", version = "0.11.2" }
|
|
||||||
|
|
||||||
typed-builder = "0.16" # Implement the builder pattern at compiletime
|
typed-builder = "0.16" # Implement the builder pattern at compiletime
|
||||||
pyo3 = { version = "0.18", optional = true }
|
pyo3 = { version = "0.18", optional = true }
|
||||||
log = "0.4.20"
|
log = "0.4.20"
|
||||||
|
|
||||||
|
[target.'cfg(target_os = "linux")'.dependencies]
|
||||||
|
libafl_qemu = { path = "../libafl_qemu", version = "0.11.2" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "libafl_sugar"
|
name = "libafl_sugar"
|
||||||
crate-type = ["cdylib", "rlib"]
|
crate-type = ["cdylib", "rlib"]
|
||||||
|
@ -4,15 +4,16 @@ use core::marker::PhantomData;
|
|||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
use libafl::state::HasClientPerfMonitor;
|
use libafl::state::HasClientPerfMonitor;
|
||||||
use libafl::{
|
use libafl::{
|
||||||
corpus::{Corpus, HasCurrentCorpusIdx},
|
|
||||||
executors::{Executor, HasObservers},
|
executors::{Executor, HasObservers},
|
||||||
inputs::{BytesInput, UsesInput},
|
inputs::{BytesInput, UsesInput},
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
stages::{colorization::TaintMetadata, Stage},
|
stages::{colorization::TaintMetadata, RetryRestartHelper, Stage},
|
||||||
state::{HasCorpus, HasExecutions, HasMetadata, UsesState},
|
state::{
|
||||||
|
HasCorpus, HasCurrentTestcase, HasExecutions, HasMetadata, HasNamedMetadata, UsesState,
|
||||||
|
},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
use libafl_bolts::tuples::MatchName;
|
use libafl_bolts::{tuples::MatchName, Named};
|
||||||
|
|
||||||
use crate::cmps::observers::AFLppCmpLogObserver;
|
use crate::cmps::observers::AFLppCmpLogObserver;
|
||||||
|
|
||||||
@ -32,16 +33,21 @@ where
|
|||||||
type State = TE::State;
|
type State = TE::State;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<EM, TE, Z> Named for AFLppCmplogTracingStage<EM, TE, Z> {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"AFLppCmplogTracingStage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E, EM, TE, Z> Stage<E, EM, Z> for AFLppCmplogTracingStage<EM, TE, Z>
|
impl<E, EM, TE, Z> Stage<E, EM, Z> for AFLppCmplogTracingStage<EM, TE, Z>
|
||||||
where
|
where
|
||||||
E: UsesState<State = TE::State>,
|
E: UsesState<State = TE::State>,
|
||||||
TE: Executor<EM, Z> + HasObservers,
|
TE: Executor<EM, Z> + HasObservers,
|
||||||
TE::State: HasExecutions + HasCorpus + HasMetadata + UsesInput<Input = BytesInput>,
|
TE::State:
|
||||||
|
HasExecutions + HasCorpus + HasMetadata + UsesInput<Input = BytesInput> + HasNamedMetadata,
|
||||||
EM: UsesState<State = TE::State>,
|
EM: UsesState<State = TE::State>,
|
||||||
Z: UsesState<State = TE::State>,
|
Z: UsesState<State = TE::State>,
|
||||||
{
|
{
|
||||||
type Progress = (); // TODO this needs resumption
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn perform(
|
fn perform(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -51,11 +57,7 @@ where
|
|||||||
manager: &mut EM,
|
manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
// First run with the un-mutated input
|
// First run with the un-mutated input
|
||||||
let corpus_idx = state.current_corpus_idx()?.ok_or_else(|| {
|
let unmutated_input = state.current_input_cloned()?;
|
||||||
Error::illegal_state("state is not currently processing a corpus index")
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let unmutated_input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
|
||||||
|
|
||||||
if let Some(name) = &self.cmplog_observer_name {
|
if let Some(name) = &self.cmplog_observer_name {
|
||||||
if let Some(ob) = self
|
if let Some(ob) = self
|
||||||
@ -121,6 +123,16 @@ where
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
||||||
|
// TODO: this may need better resumption? (Or is it always used with a forkserver?)
|
||||||
|
RetryRestartHelper::restart_progress_should_run(state, self, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
||||||
|
// TODO: this may need better resumption? (Or is it always used with a forkserver?)
|
||||||
|
RetryRestartHelper::clear_restart_progress(state, self)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<EM, TE, Z> AFLppCmplogTracingStage<EM, TE, Z> {
|
impl<EM, TE, Z> AFLppCmplogTracingStage<EM, TE, Z> {
|
||||||
|
@ -14,8 +14,7 @@
|
|||||||
clippy::missing_panics_doc,
|
clippy::missing_panics_doc,
|
||||||
clippy::missing_docs_in_private_items,
|
clippy::missing_docs_in_private_items,
|
||||||
clippy::module_name_repetitions,
|
clippy::module_name_repetitions,
|
||||||
clippy::pub_underscore_fields,
|
clippy::pub_underscore_fields
|
||||||
clippy::mixed_attributes_style
|
|
||||||
)]
|
)]
|
||||||
#![cfg_attr(not(test), warn(
|
#![cfg_attr(not(test), warn(
|
||||||
missing_debug_implementations,
|
missing_debug_implementations,
|
||||||
@ -90,6 +89,7 @@ pub use sancov_cmp::*;
|
|||||||
|
|
||||||
/// Module containing bindings to the various sanitizer interface headers
|
/// Module containing bindings to the various sanitizer interface headers
|
||||||
#[cfg(feature = "sanitizer_interfaces")]
|
#[cfg(feature = "sanitizer_interfaces")]
|
||||||
|
#[allow(clippy::mixed_attributes_style)]
|
||||||
pub mod sanitizer_ifaces {
|
pub mod sanitizer_ifaces {
|
||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
|
@ -7,12 +7,17 @@ use core::simd::num::SimdUint;
|
|||||||
#[cfg(any(feature = "sancov_ngram4", feature = "sancov_ctx"))]
|
#[cfg(any(feature = "sancov_ngram4", feature = "sancov_ctx"))]
|
||||||
use libafl::executors::{hooks::ExecutorHook, HasObservers};
|
use libafl::executors::{hooks::ExecutorHook, HasObservers};
|
||||||
|
|
||||||
|
#[cfg(any(
|
||||||
|
feature = "pointer_maps",
|
||||||
|
feature = "sancov_pcguard_edges",
|
||||||
|
feature = "sancov_pcguard_hitcounts"
|
||||||
|
))]
|
||||||
|
use crate::coverage::EDGES_MAP;
|
||||||
|
use crate::coverage::MAX_EDGES_NUM;
|
||||||
#[cfg(feature = "pointer_maps")]
|
#[cfg(feature = "pointer_maps")]
|
||||||
use crate::coverage::{EDGES_MAP_PTR, EDGES_MAP_PTR_NUM};
|
use crate::coverage::{EDGES_MAP_PTR, EDGES_MAP_PTR_NUM};
|
||||||
use crate::{
|
#[cfg(feature = "sancov_ngram4")]
|
||||||
coverage::{EDGES_MAP, MAX_EDGES_NUM},
|
use crate::EDGES_MAP_SIZE;
|
||||||
EDGES_MAP_SIZE,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(all(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))]
|
#[cfg(all(feature = "sancov_pcguard_edges", feature = "sancov_pcguard_hitcounts"))]
|
||||||
#[cfg(not(any(doc, feature = "clippy")))]
|
#[cfg(not(any(doc, feature = "clippy")))]
|
||||||
@ -160,6 +165,7 @@ extern "C" {
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[allow(unused_assignments)]
|
#[allow(unused_assignments)]
|
||||||
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
|
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: *mut u32) {
|
||||||
|
#[allow(unused_mut)]
|
||||||
let mut pos = *guard as usize;
|
let mut pos = *guard as usize;
|
||||||
|
|
||||||
#[cfg(any(feature = "sancov_ngram4", feature = "sancov_ngram8"))]
|
#[cfg(any(feature = "sancov_ngram4", feature = "sancov_ngram8"))]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user