add interrupt mutator
This commit is contained in:
parent
f3180a35cc
commit
e6816cc2de
@ -1,7 +1,7 @@
|
|||||||
//! A fuzzer using qemu in systemmode for binary-only coverage of kernels
|
//! A fuzzer using qemu in systemmode for binary-only coverage of kernels
|
||||||
//!
|
//!
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::min, mem::transmute_copy, collections::btree_map::Range};
|
use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range};
|
||||||
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
bolts::{
|
bolts::{
|
||||||
@ -22,10 +22,8 @@ use libafl::{
|
|||||||
fuzzer::{Fuzzer, StdFuzzer},
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
inputs::{BytesInput, HasTargetBytes},
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
monitors::MultiMonitor,
|
monitors::MultiMonitor,
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
|
||||||
observers::{VariableMapObserver},
|
observers::{VariableMapObserver},
|
||||||
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||||
stages::StdMutationalStage,
|
|
||||||
state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata},
|
state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata},
|
||||||
Error,
|
Error,
|
||||||
prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata}, Evaluator,
|
prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata}, Evaluator,
|
||||||
@ -39,8 +37,11 @@ use rand::{SeedableRng, StdRng, Rng};
|
|||||||
use crate::{
|
use crate::{
|
||||||
clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist},
|
clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist},
|
||||||
qemustate::QemuStateRestoreHelper,
|
qemustate::QemuStateRestoreHelper,
|
||||||
systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback},
|
systemstate::{mutators::{MINIMUM_INTER_ARRIVAL_TIME, InterruptShifterMutator}, helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback},
|
||||||
|
systemstate::mutation::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
};
|
};
|
||||||
|
use crate::mutational::StdMutationalStage;
|
||||||
|
|
||||||
pub static mut RNG_SEED: u64 = 1;
|
pub static mut RNG_SEED: u64 = 1;
|
||||||
|
|
||||||
pub const MAX_NUM_INTERRUPT: usize = 32;
|
pub const MAX_NUM_INTERRUPT: usize = 32;
|
||||||
@ -189,13 +190,19 @@ pub fn fuzz() {
|
|||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(feature = "fuzz_int")]
|
#[cfg(feature = "fuzz_int")]
|
||||||
{
|
{
|
||||||
|
let mut start_tick : u32 = 0;
|
||||||
for i in 0..DO_NUM_INTERRUPT {
|
for i in 0..DO_NUM_INTERRUPT {
|
||||||
let mut t : [u8; 4] = [0,0,0,0];
|
let mut t : [u8; 4] = [0,0,0,0];
|
||||||
if len > (i+1)*4 {
|
if len > (i+1)*4 {
|
||||||
for j in 0 as usize..4 as usize {
|
for j in 0 as usize..4 as usize {
|
||||||
t[j]=buf[i*4+j];
|
t[j]=buf[i*4+j];
|
||||||
}
|
}
|
||||||
libafl_interrupt_offsets[i] = u32::from_le_bytes(t);
|
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)));
|
||||||
|
}
|
||||||
|
libafl_interrupt_offsets[i] = start_tick;
|
||||||
libafl_num_interrupts = i+1;
|
libafl_num_interrupts = i+1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -348,6 +355,8 @@ pub fn fuzz() {
|
|||||||
let mut executor = TimeoutExecutor::new(executor, timeout);
|
let mut executor = TimeoutExecutor::new(executor, timeout);
|
||||||
|
|
||||||
let mutations = havoc_mutations();
|
let mutations = havoc_mutations();
|
||||||
|
#[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))]
|
||||||
|
let mutations = (InterruptShifterMutator::new(),havoc_mutations());
|
||||||
// Setup an havoc mutator with a mutational stage
|
// Setup an havoc mutator with a mutational stage
|
||||||
let mutator = StdScheduledMutator::new(mutations);
|
let mutator = StdScheduledMutator::new(mutations);
|
||||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
@ -8,4 +8,6 @@ mod qemustate;
|
|||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub mod systemstate;
|
pub mod systemstate;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
|
mod mutational;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
mod worst;
|
mod worst;
|
@ -10,6 +10,8 @@ mod qemustate;
|
|||||||
mod systemstate;
|
mod systemstate;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod worst;
|
mod worst;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod mutational;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
164
fuzzers/FRET/src/mutational.rs
Normal file
164
fuzzers/FRET/src/mutational.rs
Normal file
@ -0,0 +1,164 @@
|
|||||||
|
//| 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.
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
bolts::rands::Rand,
|
||||||
|
corpus::Corpus,
|
||||||
|
fuzzer::Evaluator,
|
||||||
|
mark_feature_time,
|
||||||
|
stages::{Stage},
|
||||||
|
start_timer,
|
||||||
|
state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use crate::systemstate::mutation::Mutator;
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,13 +15,14 @@ pub mod observers;
|
|||||||
pub mod feedbacks;
|
pub mod feedbacks;
|
||||||
pub mod graph;
|
pub mod graph;
|
||||||
pub mod schedulers;
|
pub mod schedulers;
|
||||||
// pub mod mutators;
|
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
|
||||||
#[cfg(not(feature = "fuzz_interrupt"))]
|
// #[cfg(not(feature = "fuzz_interrupt"))]
|
||||||
pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes
|
// pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes
|
||||||
pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start
|
// pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
const NUM_PRIOS: usize = 5;
|
const NUM_PRIOS: usize = 5;
|
||||||
|
185
fuzzers/FRET/src/systemstate/mutation/mod.rs
Normal file
185
fuzzers/FRET/src/systemstate/mutation/mod.rs
Normal file
@ -0,0 +1,185 @@
|
|||||||
|
//! 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
1189
fuzzers/FRET/src/systemstate/mutation/mutations.rs
Normal file
1189
fuzzers/FRET/src/systemstate/mutation/mutations.rs
Normal file
File diff suppressed because it is too large
Load Diff
421
fuzzers/FRET/src/systemstate/mutation/scheduled.rs
Normal file
421
fuzzers/FRET/src/systemstate/mutation/scheduled.rs
Normal file
@ -0,0 +1,421 @@
|
|||||||
|
//! 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -1,15 +1,19 @@
|
|||||||
|
use crate::fuzzer::DO_NUM_INTERRUPT;
|
||||||
|
use crate::systemstate::mutation::Mutator;
|
||||||
use crate::systemstate::graph::SysGraphMetadata;
|
use crate::systemstate::graph::SysGraphMetadata;
|
||||||
use crate::systemstate::graph::SysGraphNode;
|
use crate::systemstate::graph::SysGraphNode;
|
||||||
use crate::systemstate::IRQ_INPUT_OFFSET;
|
// use crate::systemstate::IRQ_INPUT_OFFSET;
|
||||||
use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
|
// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
|
||||||
use crate::systemstate::graph::SysGraphFeedbackState;
|
use crate::systemstate::graph::SysGraphFeedbackState;
|
||||||
use libafl::inputs::HasBytesVec;
|
use libafl::inputs::HasBytesVec;
|
||||||
use libafl::bolts::rands::RandomSeed;
|
use libafl::bolts::rands::RandomSeed;
|
||||||
use libafl::bolts::rands::StdRand;
|
use libafl::bolts::rands::StdRand;
|
||||||
use libafl::mutators::Mutator;
|
use libafl::prelude::Testcase;
|
||||||
use libafl::mutators::MutationResult;
|
use libafl::mutators::MutationResult;
|
||||||
|
use libafl::prelude::Corpus;
|
||||||
use libafl::prelude::UsesInput;
|
use libafl::prelude::UsesInput;
|
||||||
use core::marker::PhantomData;
|
use core::marker::PhantomData;
|
||||||
|
use std::cmp::max;
|
||||||
use libafl::state::HasCorpus;
|
use libafl::state::HasCorpus;
|
||||||
use libafl::state::HasSolutions;
|
use libafl::state::HasSolutions;
|
||||||
use libafl::state::HasRand;
|
use libafl::state::HasRand;
|
||||||
@ -20,9 +24,11 @@ use libafl::Error;
|
|||||||
use libafl::{inputs::Input, state::HasMetadata};
|
use libafl::{inputs::Input, state::HasMetadata};
|
||||||
|
|
||||||
use super::FreeRTOSSystemStateMetadata;
|
use super::FreeRTOSSystemStateMetadata;
|
||||||
|
use super::RefinedFreeRTOSSystemState;
|
||||||
|
|
||||||
use libafl::bolts::rands::Rand;
|
use libafl::bolts::rands::Rand;
|
||||||
|
|
||||||
|
pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4);
|
||||||
|
|
||||||
//=============================== Interrupt
|
//=============================== Interrupt
|
||||||
/// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata
|
/// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata
|
||||||
@ -42,61 +48,136 @@ where
|
|||||||
}
|
}
|
||||||
impl<S> Mutator<S> for InterruptShifterMutator<S>
|
impl<S> Mutator<S> for InterruptShifterMutator<S>
|
||||||
where
|
where
|
||||||
S: UsesInput,
|
S: UsesInput + HasRand + HasMetadata + HasCorpus,
|
||||||
|
S::Input: HasBytesVec,
|
||||||
{
|
{
|
||||||
fn mutate(
|
fn mutate(
|
||||||
&mut self,
|
&mut self,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
input: &mut S::Input,
|
_input: &mut Testcase<S::Input>,
|
||||||
_stage_idx: i32
|
_stage_idx: i32
|
||||||
) -> Result<MutationResult, Error>
|
) -> Result<MutationResult, Error>
|
||||||
{
|
{
|
||||||
// need our own random generator, because borrowing rules
|
// need our own random generator, because borrowing rules
|
||||||
let mut myrand = StdRand::new();
|
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();
|
let tmp = &mut state.rand_mut();
|
||||||
myrand.set_seed(tmp.next());
|
myrand.set_seed(tmp.next());
|
||||||
drop(tmp);
|
target_bytes = input.bytes().to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
let target_bytes = input.bytes_mut();
|
|
||||||
let mut target_tick = 0;
|
let mut target_tick = 0;
|
||||||
|
|
||||||
#[cfg(feature = "sched_state")]
|
// produce a slice of absolute interrupt times
|
||||||
|
let mut interrupt_offsets : [u32; 32] = [0u32; 32];
|
||||||
|
let mut num_interrupts : usize = 0;
|
||||||
{
|
{
|
||||||
let tmp = state.metadata().get::<FreeRTOSSystemStateMetadata>();
|
let mut start_tick : u32 = 0;
|
||||||
if tmp.is_none() { // if there are no metadata it was probably not interesting anyways
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
return Ok(MutationResult::Skipped);
|
||||||
}
|
}
|
||||||
let trace = tmp.expect("FreeRTOSSystemStateMetadata not found");
|
let trace = tmp.expect("FreeRTOSSystemStateMetadata not found");
|
||||||
let target_block = myrand.choose(trace.inner.iter());
|
|
||||||
target_tick = myrand.between(target_block.start_tick,target_block.end_tick)-IRQ_INPUT_OFFSET as u64;
|
|
||||||
}
|
|
||||||
#[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 hits and identify snippets
|
||||||
if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 {
|
let mut last_m = false;
|
||||||
for i in 0..IRQ_INPUT_BYTES_NUMBER as usize {
|
let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler
|
||||||
target_bytes[i] = u64::to_le_bytes(target_tick)[i];
|
for i in 0..trace.inner.len() {
|
||||||
}
|
let curr = &trace.inner[i];
|
||||||
return Ok(MutationResult::Mutated);
|
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 {
|
} else {
|
||||||
return Ok(MutationResult::Skipped);
|
marks.push((curr, i, 0));
|
||||||
}
|
}
|
||||||
|
last_m = m;
|
||||||
|
}
|
||||||
|
let untouched : Vec<u32> = marks.iter().filter_map(
|
||||||
|
|x| if x.2 == 0 {
|
||||||
|
Some(x.0.start_tick.try_into().expect("ticks > u32"))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
).collect();
|
||||||
|
let mut numbers : Vec<u32> = vec![];
|
||||||
|
for i in 0..num_interrupts {
|
||||||
|
numbers.push(myrand.choose(untouched.clone().into_iter()).try_into().expect("ticks > u32"));
|
||||||
|
}
|
||||||
|
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]));
|
||||||
|
}
|
||||||
|
suffix = target_bytes[4*num_interrupts..].to_vec();
|
||||||
|
}
|
||||||
|
// #[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(
|
fn post_exec(
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
|
// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
|
||||||
use libafl::prelude::{ExitKind, AsSlice};
|
use libafl::prelude::{ExitKind, AsSlice};
|
||||||
use libafl::{inputs::HasTargetBytes, prelude::UsesInput};
|
use libafl::{inputs::HasTargetBytes, prelude::UsesInput};
|
||||||
use libafl::bolts::HasLen;
|
use libafl::bolts::HasLen;
|
||||||
@ -124,7 +124,7 @@ for mut i in input.drain(..) {
|
|||||||
start_tick: start_tick,
|
start_tick: start_tick,
|
||||||
end_tick: i.qemu_tick,
|
end_tick: i.qemu_tick,
|
||||||
ready_list_after: collector,
|
ready_list_after: collector,
|
||||||
input_counter: i.input_counter+IRQ_INPUT_BYTES_NUMBER,
|
input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER,
|
||||||
last_pc: i.last_pc,
|
last_pc: i.last_pc,
|
||||||
});
|
});
|
||||||
start_tick=i.qemu_tick;
|
start_tick=i.qemu_tick;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user