use alloc::{borrow::ToOwned, string::ToString};
use core::{hash::Hash, marker::PhantomData};
pub mod testcase_score;
pub use testcase_score::{LenTimeMulTestcasePenalty, TestcasePenalty, TestcaseScore};
pub mod queue;
pub use queue::QueueScheduler;
pub mod minimizer;
pub use minimizer::{
IndexesLenTimeMinimizerScheduler, LenTimeMinimizerScheduler, MinimizerScheduler,
};
pub mod powersched;
pub use powersched::{PowerQueueScheduler, SchedulerMetadata};
pub mod probabilistic_sampling;
pub use probabilistic_sampling::ProbabilitySamplingScheduler;
pub mod accounting;
pub use accounting::CoverageAccountingScheduler;
pub mod weighted;
pub use weighted::{StdWeightedScheduler, WeightedScheduler};
pub mod tuneable;
use libafl_bolts::{
generic_hash_std,
rands::Rand,
tuples::{Handle, MatchName, MatchNameRef},
};
pub use tuneable::*;
use crate::{
Error, HasMetadata,
corpus::{Corpus, CorpusId, HasTestcase, SchedulerTestcaseMetadata, Testcase},
random_corpus_id,
state::{HasCorpus, HasRand},
};
pub trait RemovableScheduler<I, S> {
fn on_remove(
&mut self,
_state: &mut S,
_id: CorpusId,
_testcase: &Option<Testcase<I>>,
) -> Result<(), Error> {
Ok(())
}
fn on_replace(
&mut self,
_state: &mut S,
_id: CorpusId,
_prev: &Testcase<I>,
) -> Result<(), Error> {
Ok(())
}
}
pub fn on_add_metadata_default<CS, I, S>(
scheduler: &mut CS,
state: &mut S,
id: CorpusId,
) -> Result<(), Error>
where
CS: AflScheduler,
S: HasTestcase<I> + HasCorpus<I>,
{
let current_id = *state.corpus().current();
let mut depth = match current_id {
Some(parent_idx) => state
.testcase(parent_idx)?
.metadata::<SchedulerTestcaseMetadata>()?
.depth(),
None => 0,
};
depth += 1;
let mut testcase = state.testcase_mut(id)?;
testcase.add_metadata(SchedulerTestcaseMetadata::with_n_fuzz_entry(
depth,
scheduler.last_hash(),
));
testcase.set_parent_id_optional(current_id);
Ok(())
}
pub fn on_evaluation_metadata_default<CS, O, OT, S>(
scheduler: &mut CS,
state: &mut S,
observers: &OT,
) -> Result<(), Error>
where
CS: AflScheduler,
CS::ObserverRef: AsRef<O>,
S: HasMetadata,
O: Hash,
OT: MatchName,
{
let observer = observers
.get(scheduler.observer_handle())
.ok_or_else(|| Error::key_not_found("Observer not found".to_string()))?
.as_ref();
let mut hash = generic_hash_std(observer) as usize;
let psmeta = state.metadata_mut::<SchedulerMetadata>()?;
hash %= psmeta.n_fuzz().len();
psmeta.n_fuzz_mut()[hash] = psmeta.n_fuzz()[hash].saturating_add(1);
scheduler.set_last_hash(hash);
Ok(())
}
pub fn on_next_metadata_default<I, S>(state: &mut S) -> Result<(), Error>
where
S: HasCorpus<I> + HasTestcase<I>,
{
let current_id = *state.corpus().current();
if let Some(id) = current_id {
let mut testcase = state.testcase_mut(id)?;
let tcmeta = testcase.metadata_mut::<SchedulerTestcaseMetadata>()?;
if tcmeta.handicap() >= 4 {
tcmeta.set_handicap(tcmeta.handicap() - 4);
} else if tcmeta.handicap() > 0 {
tcmeta.set_handicap(tcmeta.handicap() - 1);
}
}
Ok(())
}
pub trait AflScheduler {
type ObserverRef;
fn last_hash(&self) -> usize;
fn set_last_hash(&mut self, value: usize);
fn observer_handle(&self) -> &Handle<Self::ObserverRef>;
}
pub trait HasQueueCycles {
fn queue_cycles(&self) -> u64;
}
pub trait Scheduler<I, S> {
fn on_add(&mut self, _state: &mut S, _id: CorpusId) -> Result<(), Error>;
fn on_evaluation<OT>(
&mut self,
_state: &mut S,
_input: &I,
_observers: &OT,
) -> Result<(), Error>
where
OT: MatchName,
{
Ok(())
}
fn next(&mut self, state: &mut S) -> Result<CorpusId, Error>;
fn set_current_scheduled(
&mut self,
state: &mut S,
next_id: Option<CorpusId>,
) -> Result<(), Error>;
}
#[derive(Debug, Clone)]
pub struct RandScheduler<S> {
phantom: PhantomData<S>,
}
impl<I, S> Scheduler<I, S> for RandScheduler<S>
where
S: HasCorpus<I> + HasRand,
{
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
let current_id = *state.corpus().current();
state
.corpus()
.get(id)?
.borrow_mut()
.set_parent_id_optional(current_id);
Ok(())
}
fn next(&mut self, state: &mut S) -> Result<CorpusId, Error> {
if state.corpus().count() == 0 {
Err(Error::empty(
"No entries in corpus. This often implies the target is not properly instrumented."
.to_owned(),
))
} else {
let id = random_corpus_id!(state.corpus(), state.rand_mut());
<Self as Scheduler<I, S>>::set_current_scheduled(self, state, Some(id))?;
Ok(id)
}
}
fn set_current_scheduled(
&mut self,
state: &mut S,
next_id: Option<CorpusId>,
) -> Result<(), Error> {
*state.corpus_mut().current_mut() = next_id;
Ok(())
}
}
impl<S> RandScheduler<S> {
#[must_use]
pub fn new() -> Self {
Self {
phantom: PhantomData,
}
}
}
impl<S> Default for RandScheduler<S> {
fn default() -> Self {
Self::new()
}
}
pub type StdScheduler<S> = RandScheduler<S>;