Compare commits

..

2 Commits

Author SHA1 Message Date
798aa2ceb9 remove dead code 2023-06-02 10:00:13 +02:00
183ff32beb igonre archives 2023-06-02 08:32:23 +02:00
9 changed files with 13 additions and 2214 deletions

View File

@ -8,3 +8,5 @@ mnt
*.pdf *.pdf
bins bins
.snakemake .snakemake
*.zip
*.tar.*

View File

@ -235,15 +235,6 @@ rule trace2gantt:
shell: shell:
"Rscript --vanilla $(pwd)/../../../../state2gantt/gantt.R {input}" "Rscript --vanilla $(pwd)/../../../../state2gantt/gantt.R {input}"
rule all_bins:
input:
"bins/target_random",
"bins/target_feedlongest",
"bins/target_feedaflnolongest",
"bins/target_afl",
"bins/target_state",
"bins/target_graph"
rule all_main: rule all_main:
input: input:
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3)) expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3))
@ -284,3 +275,7 @@ rule clusterfuzz:
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1_int','feedgeneration10_int','feedgeneration100_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1_int','feedgeneration10_int','feedgeneration100_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B),
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters','watersv2'],num=MY_RANGE_B), expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters','watersv2'],num=MY_RANGE_B),
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B), expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B),
rule all_bins:
input:
expand("bins/target_{target}{flag}",target=['random','afl','frafl','state','feedgeneration100'],flag=['','_int'])

View File

@ -37,10 +37,9 @@ use rand::{SeedableRng, StdRng, Rng};
use crate::{ use crate::{
clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist, FUZZ_START_TIMESTAMP}, clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist, FUZZ_START_TIMESTAMP},
qemustate::QemuStateRestoreHelper, qemustate::QemuStateRestoreHelper,
systemstate::{mutators::{MINIMUM_INTER_ARRIVAL_TIME}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback}, systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback},
mutational::MyStateStage, mutational::MyStateStage,
// systemstate::mutation::scheduled::{havoc_mutations, StdScheduledMutator}, mutational::{MINIMUM_INTER_ARRIVAL_TIME},
// mutational::StdMutationalStage
}; };
use std::time::{SystemTime, UNIX_EPOCH}; use std::time::{SystemTime, UNIX_EPOCH};

View File

@ -12,157 +12,11 @@ use libafl::{
stages::{Stage}, stages::{Stage},
start_timer, start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata}, state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata},
Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult}, Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult, Mutator},
}; };
use crate::{systemstate::{mutation::Mutator, mutators::{InterruptShifterMutator, MINIMUM_INTER_ARRIVAL_TIME}, FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist}; use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist};
// TODO multi mutators stage
/// A Mutational stage is the stage in a fuzzing run that mutates inputs.
/// Mutational stages will usually have a range of mutations that are
/// being applied to the input one by one, between executions.
pub trait MutationalStage<E, EM, M, Z>: Stage<E, EM, Z>
where
E: UsesState<State = Self::State>,
M: Mutator<Self::State>,
EM: UsesState<State = Self::State>,
Z: Evaluator<E, EM, State = Self::State>,
Self::State: HasClientPerfMonitor + HasCorpus,
{
/// The mutator registered for this stage
fn mutator(&self) -> &M;
/// The mutator registered for this stage (mutable)
fn mutator_mut(&mut self) -> &mut M;
/// Gets the number of iterations this mutator should run for.
fn iterations(&self, state: &mut Z::State, corpus_idx: usize) -> Result<u64, Error>;
/// Runs this (mutational) stage for the given testcase
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
fn perform_mutational(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Z::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
let num = self.iterations(state, corpus_idx)?;
for i in 0..num {
start_timer!(state);
let mut input = state
.corpus()
.get(corpus_idx)?
.borrow_mut()
.clone();
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
start_timer!(state);
self.mutator_mut().mutate(state, &mut input, i as i32)?;
mark_feature_time!(state, PerfFeature::Mutate);
// Time is measured directly the `evaluate_input` function
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input.load_input()?.clone())?;
start_timer!(state);
self.mutator_mut().post_exec(state, i as i32, corpus_idx)?;
mark_feature_time!(state, PerfFeature::MutatePostExec);
}
Ok(())
}
}
/// Default value, how many iterations each stage gets, as an upper bound.
/// It may randomly continue earlier.
pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct StdMutationalStage<E, EM, M, Z> {
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, Z)>,
}
impl<E, EM, M, Z> MutationalStage<E, EM, M, Z> for StdMutationalStage<E, EM, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &M {
&self.mutator
}
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator
}
/// Gets the number of iterations as a random number
fn iterations(&self, state: &mut Z::State, _corpus_idx: usize) -> Result<u64, Error> {
Ok(1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS))
}
}
impl<E, EM, M, Z> UsesState for StdMutationalStage<E, EM, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
type State = Z::State;
}
impl<E, EM, M, Z> Stage<E, EM, Z> for StdMutationalStage<E, EM, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
#[inline]
#[allow(clippy::let_and_return)]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Z::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
let ret = self.perform_mutational(fuzzer, executor, state, manager, corpus_idx);
ret
}
}
impl<E, EM, M, Z> StdMutationalStage<E, EM, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
/// Creates a new default mutational stage
pub fn new(mutator: M) -> Self {
Self {
mutator,
phantom: PhantomData,
}
}
}
pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4);
//======================= Custom mutator //======================= Custom mutator
@ -201,7 +55,6 @@ where
manager: &mut EM, manager: &mut EM,
corpus_idx: usize, corpus_idx: usize,
) -> Result<(), Error> { ) -> Result<(), Error> {
let mut mymut : InterruptShifterMutator<Z::State> = InterruptShifterMutator::new();
let mut _input = state let mut _input = state
.corpus() .corpus()
.get(corpus_idx)? .get(corpus_idx)?

View File

@ -15,8 +15,6 @@ pub mod observers;
pub mod feedbacks; pub mod feedbacks;
pub mod graph; pub mod graph;
pub mod schedulers; pub mod schedulers;
pub mod mutation;
pub mod mutators;
// #[cfg(feature = "fuzz_interrupt")] // #[cfg(feature = "fuzz_interrupt")]
// pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes // pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes

View File

@ -1,185 +0,0 @@
//! Mutators mutate input during fuzzing.
pub mod scheduled;
pub use scheduled::*;
pub mod mutations;
pub use mutations::*;
use libafl::{
bolts::tuples::{HasConstLen, Named},
inputs::UsesInput,
Error, prelude::Testcase,
mutators::{MutationResult},
};
// TODO mutator stats method that produces something that can be sent with the NewTestcase event
// We can use it to report which mutations generated the testcase in the broker logs
/// A mutator takes input, and mutates it.
/// Simple as that.
pub trait Mutator<S>
where
S: UsesInput,
{
/// Mutate a given input
fn mutate(
&mut self,
state: &mut S,
input: &mut Testcase<S::Input>,
stage_idx: i32,
) -> Result<MutationResult, Error>;
/// Post-process given the outcome of the execution
fn post_exec(
&mut self,
_state: &mut S,
_stage_idx: i32,
_corpus_idx: Option<usize>,
) -> Result<(), Error> {
Ok(())
}
}
/// A `Tuple` of `Mutators` that can execute multiple `Mutators` in a row.
pub trait MutatorsTuple<S>: HasConstLen
where
S: UsesInput,
{
/// Runs the `mutate` function on all `Mutators` in this `Tuple`.
fn mutate_all(
&mut self,
state: &mut S,
input: &mut Testcase<S::Input>,
stage_idx: i32,
) -> Result<MutationResult, Error>;
/// Runs the `post_exec` function on all `Mutators` in this `Tuple`.
fn post_exec_all(
&mut self,
state: &mut S,
stage_idx: i32,
corpus_idx: Option<usize>,
) -> Result<(), Error>;
/// Gets the [`Mutator`] at the given index and runs the `mutate` function on it.
fn get_and_mutate(
&mut self,
index: usize,
state: &mut S,
input: &mut Testcase<S::Input>,
stage_idx: i32,
) -> Result<MutationResult, Error>;
/// Gets the [`Mutator`] at the given index and runs the `post_exec` function on it.
fn get_and_post_exec(
&mut self,
index: usize,
state: &mut S,
stage_idx: i32,
corpus_idx: Option<usize>,
) -> Result<(), Error>;
}
impl<S> MutatorsTuple<S> for ()
where
S: UsesInput,
{
fn mutate_all(
&mut self,
_state: &mut S,
_input: &mut Testcase<S::Input>,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
Ok(MutationResult::Skipped)
}
fn post_exec_all(
&mut self,
_state: &mut S,
_stage_idx: i32,
_corpus_idx: Option<usize>,
) -> Result<(), Error> {
Ok(())
}
fn get_and_mutate(
&mut self,
_index: usize,
_state: &mut S,
_input: &mut Testcase<S::Input>,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
Ok(MutationResult::Skipped)
}
fn get_and_post_exec(
&mut self,
_index: usize,
_state: &mut S,
_stage_idx: i32,
_corpus_idx: Option<usize>,
) -> Result<(), Error> {
Ok(())
}
}
impl<Head, Tail, S> MutatorsTuple<S> for (Head, Tail)
where
Head: Mutator<S> + Named,
Tail: MutatorsTuple<S>,
S: UsesInput,
{
fn mutate_all(
&mut self,
state: &mut S,
input: &mut Testcase<S::Input>,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let r = self.0.mutate(state, input, stage_idx)?;
if self.1.mutate_all(state, input, stage_idx)? == MutationResult::Mutated {
Ok(MutationResult::Mutated)
} else {
Ok(r)
}
}
fn post_exec_all(
&mut self,
state: &mut S,
stage_idx: i32,
corpus_idx: Option<usize>,
) -> Result<(), Error> {
self.0.post_exec(state, stage_idx, corpus_idx)?;
self.1.post_exec_all(state, stage_idx, corpus_idx)
}
fn get_and_mutate(
&mut self,
index: usize,
state: &mut S,
input: &mut Testcase<S::Input>,
stage_idx: i32,
) -> Result<MutationResult, Error> {
if index == 0 {
self.0.mutate(state, input, stage_idx)
} else {
self.1.get_and_mutate(index - 1, state, input, stage_idx)
}
}
fn get_and_post_exec(
&mut self,
index: usize,
state: &mut S,
stage_idx: i32,
corpus_idx: Option<usize>,
) -> Result<(), Error> {
if index == 0 {
self.0.post_exec(state, stage_idx, corpus_idx)
} else {
self.1
.get_and_post_exec(index - 1, state, stage_idx, corpus_idx)
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,421 +0,0 @@
//! The `ScheduledMutator` schedules multiple mutations internally.
extern crate alloc;
use alloc::{string::String, vec::Vec};
use core::{
fmt::{self, Debug},
marker::PhantomData,
};
use serde::{Deserialize, Serialize};
pub use crate::systemstate::mutation::{
mutations::*,
Mutator,MutatorsTuple,
};
use libafl::{
bolts::{
rands::Rand,
tuples::{tuple_list, tuple_list_type, NamedTuple},
AsMutSlice, AsSlice,
},
corpus::Corpus,
inputs::UsesInput,
mutators::{MutationResult},
state::{HasCorpus, HasMetadata, HasRand, State},
Error, prelude::{TokenInsert, TokenReplace, Testcase},
};
/// The metadata placed in a [`crate::corpus::Testcase`] by a [`LoggerScheduledMutator`].
#[derive(Debug, Serialize, Deserialize)]
pub struct LogMutationMetadata {
/// A list of logs
pub list: Vec<String>,
}
libafl::impl_serdeany!(LogMutationMetadata);
impl AsSlice for LogMutationMetadata {
type Entry = String;
#[must_use]
fn as_slice(&self) -> &[String] {
self.list.as_slice()
}
}
impl AsMutSlice for LogMutationMetadata {
type Entry = String;
#[must_use]
fn as_mut_slice(&mut self) -> &mut [String] {
self.list.as_mut_slice()
}
}
impl LogMutationMetadata {
/// Creates new [`struct@LogMutationMetadata`].
#[must_use]
pub fn new(list: Vec<String>) -> Self {
Self { list }
}
}
/// A [`Mutator`] that composes multiple mutations into one.
pub trait ComposedByMutations<MT, S>
where
MT: MutatorsTuple<S>,
S: UsesInput,
{
/// Get the mutations
fn mutations(&self) -> &MT;
/// Get the mutations (mutable)
fn mutations_mut(&mut self) -> &mut MT;
}
/// A [`Mutator`] scheduling multiple [`Mutator`]s for an input.
pub trait ScheduledMutator<MT, S>: ComposedByMutations<MT, S> + Mutator<S>
where
MT: MutatorsTuple<S>,
S: UsesInput,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, input: &Testcase<S::Input>) -> u64;
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, input: &Testcase<S::Input>) -> usize;
/// New default implementation for mutate.
/// Implementations must forward mutate() to this method
fn scheduled_mutate(
&mut self,
state: &mut S,
input: &mut Testcase<S::Input>,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
let num = self.iterations(state, input);
for _ in 0..num {
let idx = self.schedule(state, input);
let outcome = self
.mutations_mut()
.get_and_mutate(idx, state, input, stage_idx)?;
if outcome == MutationResult::Mutated {
r = MutationResult::Mutated;
}
}
Ok(r)
}
}
/// A [`Mutator`] that schedules one of the embedded mutations on each call.
pub struct StdScheduledMutator<MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
{
mutations: MT,
max_stack_pow: u64,
phantom: PhantomData<S>,
}
impl<MT, S> Debug for StdScheduledMutator<MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"StdScheduledMutator with {} mutations for Input type {}",
self.mutations.len(),
core::any::type_name::<S::Input>()
)
}
}
impl<MT, S> Mutator<S> for StdScheduledMutator<MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
{
#[inline]
fn mutate(
&mut self,
state: &mut S,
input: &mut Testcase<S::Input>,
stage_idx: i32,
) -> Result<MutationResult, Error> {
self.scheduled_mutate(state, input, stage_idx)
}
}
impl<MT, S> ComposedByMutations<MT, S> for StdScheduledMutator<MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
{
/// Get the mutations
#[inline]
fn mutations(&self) -> &MT {
&self.mutations
}
// Get the mutations (mutable)
#[inline]
fn mutations_mut(&mut self) -> &mut MT {
&mut self.mutations
}
}
impl<MT, S> ScheduledMutator<MT, S> for StdScheduledMutator<MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &Testcase<S::Input>) -> u64 {
1 << (1 + state.rand_mut().below(self.max_stack_pow))
}
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &Testcase<S::Input>) -> usize {
debug_assert!(!self.mutations().is_empty());
state.rand_mut().below(self.mutations().len() as u64) as usize
}
}
impl<MT, S> StdScheduledMutator<MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
{
/// Create a new [`StdScheduledMutator`] instance specifying mutations
pub fn new(mutations: MT) -> Self {
StdScheduledMutator {
mutations,
max_stack_pow: 7,
phantom: PhantomData,
}
}
/// Create a new [`StdScheduledMutator`] instance specifying mutations and the maximun number of iterations
pub fn with_max_stack_pow(mutations: MT, max_stack_pow: u64) -> Self {
StdScheduledMutator {
mutations,
max_stack_pow,
phantom: PhantomData,
}
}
}
/// Tuple type of the mutations that compose the Havoc mutator
pub type HavocMutationsType = tuple_list_type!(
BitFlipMutator,
ByteFlipMutator,
ByteIncMutator,
ByteDecMutator,
ByteNegMutator,
ByteRandMutator,
ByteAddMutator,
WordAddMutator,
DwordAddMutator,
QwordAddMutator,
ByteInterestingMutator,
WordInterestingMutator,
DwordInterestingMutator,
BytesDeleteMutator,
BytesDeleteMutator,
BytesDeleteMutator,
BytesDeleteMutator,
BytesExpandMutator,
BytesInsertMutator,
BytesRandInsertMutator,
BytesSetMutator,
BytesRandSetMutator,
BytesCopyMutator,
BytesInsertCopyMutator,
BytesSwapMutator,
CrossoverInsertMutator,
CrossoverReplaceMutator,
);
/// Get the mutations that compose the Havoc mutator
#[must_use]
pub fn havoc_mutations() -> HavocMutationsType {
tuple_list!(
BitFlipMutator::new(),
ByteFlipMutator::new(),
ByteIncMutator::new(),
ByteDecMutator::new(),
ByteNegMutator::new(),
ByteRandMutator::new(),
ByteAddMutator::new(),
WordAddMutator::new(),
DwordAddMutator::new(),
QwordAddMutator::new(),
ByteInterestingMutator::new(),
WordInterestingMutator::new(),
DwordInterestingMutator::new(),
BytesDeleteMutator::new(),
BytesDeleteMutator::new(),
BytesDeleteMutator::new(),
BytesDeleteMutator::new(),
BytesExpandMutator::new(),
BytesInsertMutator::new(),
BytesRandInsertMutator::new(),
BytesSetMutator::new(),
BytesRandSetMutator::new(),
BytesCopyMutator::new(),
BytesInsertCopyMutator::new(),
BytesSwapMutator::new(),
CrossoverInsertMutator::new(),
CrossoverReplaceMutator::new(),
)
}
/// Get the mutations that uses the Tokens metadata
#[must_use]
pub fn tokens_mutations() -> tuple_list_type!(TokenInsert, TokenReplace) {
tuple_list!(TokenInsert::new(), TokenReplace::new(),)
}
/// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`].
pub struct LoggerScheduledMutator<MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: UsesInput + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
scheduled: SM,
mutation_log: Vec<usize>,
phantom: PhantomData<(MT, S)>,
}
impl<MT, S, SM> Debug for LoggerScheduledMutator<MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: UsesInput + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"LoggerScheduledMutator with {} mutations for Input type {}",
self.scheduled.mutations().len(),
core::any::type_name::<<S as UsesInput>::Input>()
)
}
}
impl<MT, S, SM> Mutator<S> for LoggerScheduledMutator<MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut Testcase<<S as UsesInput>::Input>,
stage_idx: i32,
) -> Result<MutationResult, Error> {
self.scheduled_mutate(state, input, stage_idx)
}
fn post_exec(
&mut self,
state: &mut S,
_stage_idx: i32,
corpus_idx: Option<usize>,
) -> Result<(), Error> {
if let Some(idx) = corpus_idx {
let mut testcase = (*state.corpus_mut().get(idx)?).borrow_mut();
let mut log = Vec::<String>::new();
while let Some(idx) = self.mutation_log.pop() {
let name = String::from(self.scheduled.mutations().name(idx).unwrap()); // TODO maybe return an Error on None
log.push(name);
}
let meta = LogMutationMetadata::new(log);
testcase.add_metadata(meta);
};
// Always reset the log for each run
self.mutation_log.clear();
Ok(())
}
}
impl<MT, S, SM> ComposedByMutations<MT, S> for LoggerScheduledMutator<MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
#[inline]
fn mutations(&self) -> &MT {
self.scheduled.mutations()
}
#[inline]
fn mutations_mut(&mut self) -> &mut MT {
self.scheduled.mutations_mut()
}
}
impl<MT, S, SM> ScheduledMutator<MT, S> for LoggerScheduledMutator<MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &Testcase<<S as UsesInput>::Input>) -> u64 {
1 << (1 + state.rand_mut().below(6))
}
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &Testcase<<S as UsesInput>::Input>) -> usize {
debug_assert!(!self.scheduled.mutations().is_empty());
state
.rand_mut()
.below(self.scheduled.mutations().len() as u64) as usize
}
fn scheduled_mutate(
&mut self,
state: &mut S,
input: &mut Testcase<<S as UsesInput>::Input>,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
let num = self.iterations(state, input);
self.mutation_log.clear();
for _ in 0..num {
let idx = self.schedule(state, input);
self.mutation_log.push(idx);
let outcome = self
.mutations_mut()
.get_and_mutate(idx, state, input, stage_idx)?;
if outcome == MutationResult::Mutated {
r = MutationResult::Mutated;
}
}
Ok(r)
}
}
impl<MT, S, SM> LoggerScheduledMutator<MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
{
/// Create a new [`StdScheduledMutator`] instance without mutations and corpus
pub fn new(scheduled: SM) -> Self {
Self {
scheduled,
mutation_log: vec![],
phantom: PhantomData,
}
}
}

View File

@ -1,253 +0,0 @@
use crate::fuzzer::DO_NUM_INTERRUPT;
use crate::systemstate::mutation::Mutator;
use crate::systemstate::graph::SysGraphMetadata;
use crate::systemstate::graph::SysGraphNode;
// use crate::systemstate::IRQ_INPUT_OFFSET;
// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
use crate::systemstate::graph::SysGraphFeedbackState;
use libafl::inputs::HasBytesVec;
use libafl::bolts::rands::RandomSeed;
use libafl::bolts::rands::StdRand;
use libafl::prelude::Testcase;
use libafl::mutators::MutationResult;
use libafl::prelude::Corpus;
use libafl::prelude::UsesInput;
use core::marker::PhantomData;
use std::cmp::max;
use std::cmp::min;
use libafl::state::HasCorpus;
use libafl::state::HasSolutions;
use libafl::state::HasRand;
use libafl::bolts::tuples::MatchName;
use libafl::bolts::tuples::Named;
use libafl::Error;
use libafl::{inputs::Input, state::HasMetadata};
use super::FreeRTOSSystemStateMetadata;
use super::RefinedFreeRTOSSystemState;
use libafl::bolts::rands::Rand;
pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4);
//=============================== Interrupt
/// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata
pub struct InterruptShifterMutator<S>
where
S: UsesInput,
{
phantom: PhantomData<S>,
}
impl<S> InterruptShifterMutator<S>
where
S: UsesInput,
{
pub fn new() -> Self {
InterruptShifterMutator{phantom: PhantomData}
}
}
impl<S> Mutator<S> for InterruptShifterMutator<S>
where
S: UsesInput + HasRand + HasMetadata + HasCorpus,
S::Input: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
_input: &mut Testcase<S::Input>,
_stage_idx: i32
) -> Result<MutationResult, Error>
{
// need our own random generator, because borrowing rules
let mut myrand = StdRand::new();
let mut target_bytes : Vec<u8> = vec![];
{
let input = _input.input_mut().as_ref().unwrap();
let tmp = &mut state.rand_mut();
myrand.set_seed(tmp.next());
target_bytes = input.bytes().to_vec();
}
// produce a slice of absolute interrupt times
let mut interrupt_offsets : [u32; 32] = [0u32; 32];
let mut num_interrupts : usize = 0;
{
let mut start_tick : u32 = 0;
for i in 0..DO_NUM_INTERRUPT {
let mut t : [u8; 4] = [0,0,0,0];
if target_bytes.len() > (i+1)*4 {
for j in 0 as usize..4 as usize {
t[j]=target_bytes[i*4+j];
}
if i == 0 {
start_tick = u32::from_le_bytes(t);
} else {
start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t)));
}
interrupt_offsets[i] = start_tick;
num_interrupts = i+1;
}
}
}
// println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec());
// let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT);
let mut suffix = target_bytes.split_off(4 * num_interrupts);
let mut prefix : Vec<[u8; 4]> = vec![];
// let mut suffix : Vec<u8> = vec![];
// #[cfg(feature = "feed_systemtrace")]
{
let tmp = _input.metadata().get::<FreeRTOSSystemStateMetadata>();
if tmp.is_none() {
return Ok(MutationResult::Skipped);
}
let trace = tmp.expect("FreeRTOSSystemStateMetadata not found");
// calculate hits and identify snippets
let mut last_m = false;
let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler
for i in 0..trace.inner.len() {
let curr = &trace.inner[i];
let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64)));
if m {
marks.push((curr, i, 1));
// println!("1: {}",curr.current_task.task_name);
} else if last_m {
marks.push((curr, i, 2));
// println!("2: {}",curr.current_task.task_name);
} else {
marks.push((curr, i, 0));
}
last_m = m;
}
for i in 0..num_interrupts {
// bounds based on minimum inter-arrival time
let mut lb = 0;
let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32");
if i > 0 {
lb = interrupt_offsets[i-1]+MINIMUM_INTER_ARRIVAL_TIME;
}
if i < num_interrupts-1 {
ub = interrupt_offsets[i+1]-MINIMUM_INTER_ARRIVAL_TIME;
}
// get old hit and handler
let old_hit = marks.iter().filter(
|x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick
).next();
let old_handler = match old_hit {
Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 {
Some(marks[s.1+1])
} else {None},
None => None
};
// find reachable alternatives
let alternatives : Vec<_> = marks.iter().filter(|x|
(
x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick
|| x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick )
).collect();
// in cases there are no alternatives
if alternatives.len() == 0 {
if old_hit.is_none() {
// choose something random
let untouched : Vec<_> = marks.iter().filter(
|x| x.2 == 0
).collect();
let tmp = interrupt_offsets[i];
let choice = myrand.choose(untouched);
interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick)
.try_into().expect("tick > u32");
// println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]);
continue;
} else {
// do nothing
// println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]);
continue;
}
}
let replacement = myrand.choose(alternatives);
if (old_hit.map_or(false, |x| x == replacement)) {
// use the old value
// println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]);
continue;
} else {
let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) {
// move futher back, respect old_handler
old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick)
} else { 0 };
let tmp = interrupt_offsets[i];
interrupt_offsets[i] = (myrand.between(replacement.0.start_tick,
replacement.0.end_tick) + extra).try_into().expect("ticks > u32");
// println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]);
}
}
// println!("Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec());
let mut numbers : Vec<u32> = interrupt_offsets[0..num_interrupts].to_vec();
numbers.sort();
let mut start : u32 = 0;
for i in 0..numbers.len() {
let tmp = numbers[i];
numbers[i] = numbers[i]-start;
start = tmp;
}
for i in 0..numbers.len() {
prefix.push(u32::to_le_bytes(numbers[i]));
}
}
// #[cfg(feature = "sched_state")]
// {
// let feedbackstate = state
// .feedback_states()
// .match_name::<SysGraphFeedbackState>("SysMap")
// .unwrap();
// let g = &feedbackstate.graph;
// let tmp = state.metadata().get::<SysGraphMetadata>();
// if tmp.is_none() { // if there are no metadata it was probably not interesting anyways
// return Ok(MutationResult::Skipped);
// }
// let trace = tmp.expect("SysGraphMetadata not found");
// let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())];
// target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) {
// Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64,
// None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64,
// };
// }
// calculate ranges, alternative hits
// move snippets
let mut n = [prefix.concat(), suffix].concat();
let input = _input.input_mut().as_mut().unwrap();
input.bytes_mut().clear();
input.bytes_mut().append(&mut n);
return Ok(MutationResult::Mutated);
// if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 {
// for i in 0..IRQ_INPUT_BYTES_NUMBER as usize {
// target_bytes[i] = u64::to_le_bytes(target_tick)[i];
// }
// return Ok(MutationResult::Mutated);
// } else {
// return Ok(MutationResult::Skipped);
// }
}
fn post_exec(
&mut self,
_state: &mut S,
_stage_idx: i32,
_corpus_idx: Option<usize>
) -> Result<(), Error> {
Ok(())
}
}
impl<S> Named for InterruptShifterMutator<S>
where
S: UsesInput,
{
fn name(&self) -> &str {
"InterruptShifterMutator"
}
}