use alloc::vec::Vec;
use core::{hash::Hash, marker::PhantomData, time::Duration};
use libafl_bolts::{
Named,
tuples::{Handle, Handled, MatchName},
};
use serde::{Deserialize, Serialize};
use crate::{
Error, HasMetadata,
corpus::{Corpus, CorpusId, HasTestcase, Testcase},
schedulers::{
AflScheduler, HasQueueCycles, RemovableScheduler, Scheduler, on_add_metadata_default,
on_evaluation_metadata_default, on_next_metadata_default,
},
state::HasCorpus,
};
pub const N_FUZZ_SIZE: usize = 1 << 21;
libafl_bolts::impl_serdeany!(SchedulerMetadata);
#[derive(Serialize, Deserialize, Debug, Clone)]
#[cfg_attr(
any(not(feature = "serdeany_autoreg"), miri),
expect(clippy::unsafe_derive_deserialize)
)] pub struct SchedulerMetadata {
strat: Option<PowerSchedule>,
exec_time: Duration,
cycles: u64,
bitmap_size: u64,
bitmap_size_log: f64,
bitmap_entries: u64,
queue_cycles: u64,
n_fuzz: Vec<u32>,
}
impl SchedulerMetadata {
#[must_use]
pub fn new(strat: Option<PowerSchedule>) -> Self {
Self {
strat,
exec_time: Duration::from_millis(0),
cycles: 0,
bitmap_size: 0,
bitmap_size_log: 0.0,
bitmap_entries: 0,
queue_cycles: 0,
n_fuzz: vec![0; N_FUZZ_SIZE],
}
}
#[must_use]
pub fn strat(&self) -> Option<PowerSchedule> {
self.strat
}
pub fn set_strat(&mut self, strat: Option<PowerSchedule>) {
self.strat = strat;
}
#[must_use]
pub fn exec_time(&self) -> Duration {
self.exec_time
}
pub fn set_exec_time(&mut self, time: Duration) {
self.exec_time = time;
}
#[must_use]
pub fn cycles(&self) -> u64 {
self.cycles
}
pub fn set_cycles(&mut self, val: u64) {
self.cycles = val;
}
#[must_use]
pub fn bitmap_size(&self) -> u64 {
self.bitmap_size
}
pub fn set_bitmap_size(&mut self, val: u64) {
self.bitmap_size = val;
}
#[must_use]
pub fn bitmap_size_log(&self) -> f64 {
self.bitmap_size_log
}
pub fn set_bitmap_size_log(&mut self, val: f64) {
self.bitmap_size_log = val;
}
#[must_use]
pub fn bitmap_entries(&self) -> u64 {
self.bitmap_entries
}
pub fn set_bitmap_entries(&mut self, val: u64) {
self.bitmap_entries = val;
}
#[must_use]
pub fn queue_cycles(&self) -> u64 {
self.queue_cycles
}
pub fn set_queue_cycles(&mut self, val: u64) {
self.queue_cycles = val;
}
#[must_use]
pub fn n_fuzz(&self) -> &[u32] {
&self.n_fuzz
}
#[must_use]
pub fn n_fuzz_mut(&mut self) -> &mut [u32] {
&mut self.n_fuzz
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Copy)]
pub struct PowerSchedule {
base: BaseSchedule,
avoid_crash: bool,
}
impl PowerSchedule {
#[must_use]
pub fn new(base: BaseSchedule) -> Self {
Self {
base,
avoid_crash: false,
}
}
#[must_use]
pub fn explore() -> Self {
Self {
base: BaseSchedule::EXPLORE,
avoid_crash: false,
}
}
#[must_use]
pub fn exploit() -> Self {
Self {
base: BaseSchedule::EXPLOIT,
avoid_crash: false,
}
}
#[must_use]
pub fn fast() -> Self {
Self {
base: BaseSchedule::FAST,
avoid_crash: false,
}
}
#[must_use]
pub fn coe() -> Self {
Self {
base: BaseSchedule::COE,
avoid_crash: false,
}
}
#[must_use]
pub fn lin() -> Self {
Self {
base: BaseSchedule::LIN,
avoid_crash: false,
}
}
#[must_use]
pub fn quad() -> Self {
Self {
base: BaseSchedule::QUAD,
avoid_crash: false,
}
}
#[must_use]
pub fn avoid_crash(&self) -> bool {
self.avoid_crash
}
pub fn set_avoid_crash(&mut self) {
self.avoid_crash = true;
}
#[must_use]
pub fn base(&self) -> &BaseSchedule {
&self.base
}
pub fn set_base(&mut self, base: BaseSchedule) {
self.base = base;
}
}
#[derive(Serialize, Deserialize, Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "clap", derive(clap::ValueEnum))]
pub enum BaseSchedule {
EXPLORE,
EXPLOIT,
FAST,
COE,
LIN,
QUAD,
}
#[derive(Debug, Clone)]
pub struct PowerQueueScheduler<C, O> {
queue_cycles: u64,
strat: PowerSchedule,
observer_handle: Handle<C>,
last_hash: usize,
phantom: PhantomData<O>,
}
impl<C, I, O, S> RemovableScheduler<I, S> for PowerQueueScheduler<C, O> {
fn on_remove(
&mut self,
_state: &mut S,
_id: CorpusId,
_prev: &Option<Testcase<I>>,
) -> Result<(), Error> {
Ok(())
}
fn on_replace(
&mut self,
_state: &mut S,
_id: CorpusId,
_prev: &Testcase<I>,
) -> Result<(), Error> {
Ok(())
}
}
impl<C, O> AflScheduler for PowerQueueScheduler<C, O> {
type ObserverRef = C;
fn last_hash(&self) -> usize {
self.last_hash
}
fn set_last_hash(&mut self, hash: usize) {
self.last_hash = hash;
}
fn observer_handle(&self) -> &Handle<C> {
&self.observer_handle
}
}
impl<C, O> HasQueueCycles for PowerQueueScheduler<C, O> {
fn queue_cycles(&self) -> u64 {
self.queue_cycles
}
}
impl<C, I, O, S> Scheduler<I, S> for PowerQueueScheduler<C, O>
where
S: HasCorpus<I> + HasMetadata + HasTestcase<I>,
O: Hash,
C: AsRef<O>,
{
fn on_add(&mut self, state: &mut S, id: CorpusId) -> Result<(), Error> {
on_add_metadata_default(self, state, id)
}
fn on_evaluation<OT>(&mut self, state: &mut S, _input: &I, observers: &OT) -> Result<(), Error>
where
OT: MatchName,
{
on_evaluation_metadata_default(self, state, observers)
}
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.",
))
} else {
let id = match state.corpus().current() {
Some(cur) => {
if let Some(next) = state.corpus().next(*cur) {
next
} else {
self.queue_cycles += 1;
let psmeta = state.metadata_mut::<SchedulerMetadata>()?;
psmeta.set_queue_cycles(self.queue_cycles());
state.corpus().first().unwrap()
}
}
None => state.corpus().first().unwrap(),
};
<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> {
on_next_metadata_default(state)?;
*state.corpus_mut().current_mut() = next_id;
Ok(())
}
}
impl<C, O> PowerQueueScheduler<C, O>
where
O: Hash,
C: AsRef<O> + Named,
{
#[must_use]
pub fn new<S>(state: &mut S, observer: &C, strat: PowerSchedule) -> Self
where
S: HasMetadata,
{
if !state.has_metadata::<SchedulerMetadata>() {
state.add_metadata::<SchedulerMetadata>(SchedulerMetadata::new(Some(strat)));
}
PowerQueueScheduler {
queue_cycles: 0,
strat,
observer_handle: observer.handle(),
last_hash: 0,
phantom: PhantomData,
}
}
#[must_use]
pub fn strat(&self) -> &PowerSchedule {
&self.strat
}
}