add generation based genetic testing
This commit is contained in:
parent
def4071c2b
commit
4125f2be22
@ -17,6 +17,7 @@ feed_systemgraph = [ "systemstate" ]
|
||||
feed_systemtrace = [ "systemstate" ]
|
||||
feed_longest = [ ]
|
||||
feed_afl = [ ]
|
||||
feed_genetic = [ ]
|
||||
fuzz_int = [ ]
|
||||
|
||||
[profile.release]
|
||||
|
@ -73,6 +73,18 @@ rule build_feedlongest_int:
|
||||
shell:
|
||||
"cargo build --target-dir {output} {def_flags},feed_longest,fuzz_int"
|
||||
|
||||
rule build_feedgeneration:
|
||||
output:
|
||||
directory("bins/target_feedlongest_int")
|
||||
shell:
|
||||
"cargo build --target-dir {output} {def_flags},feed_genetic"
|
||||
|
||||
rule build_feedgeneration_int:
|
||||
output:
|
||||
directory("bins/target_feedlongest_int")
|
||||
shell:
|
||||
"cargo build --target-dir {output} {def_flags},feed_genetic"
|
||||
|
||||
rule run_bench:
|
||||
input:
|
||||
"build/{target}.elf",
|
||||
@ -114,11 +126,11 @@ rule run_bench:
|
||||
rule run_showmap:
|
||||
input:
|
||||
"build/{target}.elf",
|
||||
"bins/target_showmap",
|
||||
"timedump/{fuzzer}/{target}.{num}.case"
|
||||
"bins/target_showmap_int",
|
||||
"mnt/timedump/{fuzzer}/{target}.{num}.case"
|
||||
output:
|
||||
"timedump/{fuzzer}/{target}.{num}.trace.ron",
|
||||
"timedump/{fuzzer}/{target}.{num}.case.time",
|
||||
"mnt/timedump/{fuzzer}/{target}.{num}.trace.ron",
|
||||
"mnt/timedump/{fuzzer}/{target}.{num}.case.time",
|
||||
run:
|
||||
with open('target_symbols.csv') as csvfile:
|
||||
reader = csv.DictReader(csvfile)
|
||||
@ -176,7 +188,7 @@ rule all_bins:
|
||||
|
||||
rule all_periodic:
|
||||
input:
|
||||
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state'], target=['waters', 'tmr'],num=range(0,10))
|
||||
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random'], target=['tmr'],num=range(0,10))
|
||||
|
||||
rule all_compare_afl_longest:
|
||||
input:
|
||||
@ -184,4 +196,4 @@ rule all_compare_afl_longest:
|
||||
|
||||
rule all_micro:
|
||||
input:
|
||||
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','state_int','random_int','feedlongest_int'], target=['waters_int','micro_longint'],num=range(0,10))
|
||||
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int'], target=['micro_longint'],num=range(0,10))
|
||||
|
@ -39,7 +39,7 @@ use rand::{SeedableRng, StdRng, Rng};
|
||||
use crate::{
|
||||
clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist},
|
||||
qemustate::QemuStateRestoreHelper,
|
||||
systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::LongestTraceScheduler}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler},
|
||||
systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback},
|
||||
};
|
||||
pub static mut RNG_SEED: u64 = 1;
|
||||
|
||||
@ -245,6 +245,11 @@ pub fn fuzz() {
|
||||
// Time feedback, this one does not need a feedback state
|
||||
ClockTimeFeedback::new_with_observer(&clock_time_observer)
|
||||
);
|
||||
#[cfg(feature = "feed_genetic")]
|
||||
let mut feedback = feedback_or!(
|
||||
feedback,
|
||||
AlwaysTrueFeedback::new()
|
||||
);
|
||||
#[cfg(feature = "feed_afl")]
|
||||
let mut feedback = feedback_or!(
|
||||
feedback,
|
||||
@ -297,7 +302,7 @@ pub fn fuzz() {
|
||||
});
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
#[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace")))]
|
||||
#[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace", feature = "feed_genetic")))]
|
||||
let scheduler = QueueScheduler::new();
|
||||
#[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))]
|
||||
let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new());
|
||||
@ -305,6 +310,8 @@ pub fn fuzz() {
|
||||
let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()));
|
||||
#[cfg(feature = "feed_systemgraph")]
|
||||
let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new());
|
||||
#[cfg(feature = "feed_genetic")]
|
||||
let scheduler = GenerationScheduler::new();
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
@ -2,7 +2,7 @@
|
||||
//! with testcases only from a subset of the total corpus.
|
||||
|
||||
use core::{marker::PhantomData};
|
||||
use std::cmp::max;
|
||||
use std::{cmp::{max, min}, mem::swap, borrow::BorrowMut};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
@ -11,11 +11,13 @@ use libafl::{
|
||||
corpus::{Corpus, Testcase},
|
||||
inputs::UsesInput,
|
||||
schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB },
|
||||
state::{HasCorpus, HasMetadata, HasRand, UsesState},
|
||||
Error, SerdeAny,
|
||||
state::{HasCorpus, HasMetadata, HasRand, UsesState, State},
|
||||
Error, SerdeAny, prelude::HasLen,
|
||||
|
||||
};
|
||||
|
||||
use crate::worst::MaxTimeFavFactor;
|
||||
|
||||
use super::FreeRTOSSystemStateMetadata;
|
||||
|
||||
/// A state metadata holding a map of favoreds testcases for each map entry
|
||||
@ -132,3 +134,134 @@ where
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================================
|
||||
|
||||
/// A state metadata holding a map of favoreds testcases for each map entry
|
||||
#[derive(Debug, Serialize, Deserialize, SerdeAny, Default)]
|
||||
pub struct GeneticMetadata {
|
||||
pub current_gen: Vec<(usize, f64)>,
|
||||
pub current_cursor: usize,
|
||||
pub next_gen: Vec<(usize, f64)>,
|
||||
pub gen: usize
|
||||
}
|
||||
|
||||
impl GeneticMetadata {
|
||||
fn new(current_gen: Vec<(usize, f64)>, next_gen: Vec<(usize, f64)>) -> Self {
|
||||
Self {current_gen, current_cursor: 0, next_gen, gen: 0}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct GenerationScheduler<S> {
|
||||
phantom: PhantomData<S>,
|
||||
gen_size: usize,
|
||||
}
|
||||
|
||||
impl<S> UsesState for GenerationScheduler<S>
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
type State = S;
|
||||
}
|
||||
|
||||
impl<S> Scheduler for GenerationScheduler<S>
|
||||
where
|
||||
S: HasCorpus + HasMetadata,
|
||||
S::Input: HasLen,
|
||||
{
|
||||
/// get first element in current gen,
|
||||
/// if current_gen is empty, swap lists, sort by FavFactor, take top k and return first
|
||||
fn next(&self, state: &mut Self::State) -> Result<usize, Error> {
|
||||
let mut to_remove : Vec<(usize, f64)> = vec![];
|
||||
let mut to_return : usize = 0;
|
||||
let c = state.corpus().count();
|
||||
let gm = state.metadata_mut().get_mut::<GeneticMetadata>().expect("Corpus Scheduler empty");
|
||||
// println!("index: {} curr: {:?} next: {:?} gen: {} corp: {}", gm.current_cursor, gm.current_gen.len(), gm.next_gen.len(), gm.gen,
|
||||
c);
|
||||
match gm.current_gen.get(gm.current_cursor) {
|
||||
Some(c) => {
|
||||
gm.current_cursor+=1;
|
||||
// println!("normal next: {}", (*c).0);
|
||||
return Ok((*c).0)
|
||||
},
|
||||
None => {
|
||||
swap(&mut to_remove, &mut gm.current_gen);
|
||||
swap(&mut gm.next_gen, &mut gm.current_gen);
|
||||
gm.current_gen.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
||||
// gm.current_gen.reverse();
|
||||
if gm.current_gen.len() == 0 {panic!("Corpus is empty");}
|
||||
let d : Vec<(usize, f64)> = gm.current_gen.drain(min(gm.current_gen.len(), self.gen_size)..).collect();
|
||||
to_remove.extend(d);
|
||||
// move all indices to the left, since all other indices will be deleted
|
||||
gm.current_gen.sort_by(|a,b| a.0.cmp(&(*b).0)); // in order of the corpus index
|
||||
for i in 0..gm.current_gen.len() {
|
||||
gm.current_gen[i] = (i, gm.current_gen[i].1);
|
||||
}
|
||||
to_return = gm.current_gen.get(0).unwrap().0;
|
||||
gm.current_cursor=1;
|
||||
gm.gen+=1;
|
||||
}
|
||||
};
|
||||
// removing these elements will move all indices left by to_remove.len()
|
||||
to_remove.sort_by(|x,y| x.0.cmp(&(*y).0));
|
||||
to_remove.reverse();
|
||||
for i in to_remove {
|
||||
state.corpus_mut().remove(i.0).unwrap();
|
||||
}
|
||||
// println!("switch next: {to_return}");
|
||||
return Ok(to_return);
|
||||
}
|
||||
|
||||
/// Add the new input to the next generation
|
||||
fn on_add(
|
||||
&self,
|
||||
state: &mut Self::State,
|
||||
idx: usize
|
||||
) -> Result<(), Error> {
|
||||
// println!("On Add {idx}");
|
||||
let mut tc = state.corpus_mut().get(idx).unwrap().borrow_mut().clone();
|
||||
let ff = MaxTimeFavFactor::compute(&mut tc, state).unwrap();
|
||||
if let Some(gm) = state.metadata_mut().get_mut::<GeneticMetadata>() {
|
||||
gm.next_gen.push((idx,ff));
|
||||
} else {
|
||||
state.add_metadata(GeneticMetadata::new(vec![], vec![(idx,ff)]));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn on_replace(
|
||||
&self,
|
||||
_state: &mut Self::State,
|
||||
_idx: usize,
|
||||
_prev: &Testcase<<Self::State as UsesInput>::Input>
|
||||
) -> Result<(), Error> {
|
||||
// println!("On Replace {_idx}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_remove(
|
||||
&self,
|
||||
state: &mut Self::State,
|
||||
idx: usize,
|
||||
_testcase: &Option<Testcase<<Self::State as UsesInput>::Input>>
|
||||
) -> Result<(), Error> {
|
||||
// println!("On Remove {idx}");
|
||||
if let Some(gm) = state.metadata_mut().get_mut::<GeneticMetadata>() {
|
||||
gm.next_gen = gm.next_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::<Vec<(usize, f64)>>();
|
||||
gm.current_gen = gm.current_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::<Vec<(usize, f64)>>();
|
||||
} else {
|
||||
state.add_metadata(GeneticMetadata::new(vec![], vec![]));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> GenerationScheduler<S>
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
phantom: PhantomData,
|
||||
gen_size: 100,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ where
|
||||
S: HasCorpus + HasMetadata,
|
||||
S::Input: HasLen,
|
||||
{
|
||||
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error> {
|
||||
fn compute(entry: &mut Testcase<<S as UsesInput>::Input>, state: &S) -> Result<f64, Error> {
|
||||
// TODO maybe enforce entry.exec_time().is_some()
|
||||
let et = entry.exec_time().expect("testcase.exec_time is needed for scheduler");
|
||||
let tns : i64 = et.as_nanos().try_into().expect("failed to convert time");
|
||||
@ -333,3 +333,49 @@ where
|
||||
Self {longest_time: 0, last_is_longest: false}
|
||||
}
|
||||
}
|
||||
|
||||
/// A Noop Feedback which records a list of all execution times
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct AlwaysTrueFeedback
|
||||
{
|
||||
}
|
||||
|
||||
impl<S> Feedback<S> for AlwaysTrueFeedback
|
||||
where
|
||||
S: UsesInput + HasClientPerfMonitor,
|
||||
{
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &S::Input,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<S>,
|
||||
{
|
||||
Ok(true)
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for AlwaysTrueFeedback
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"AlwaysTrueFeedback"
|
||||
}
|
||||
}
|
||||
|
||||
impl AlwaysTrueFeedback
|
||||
where
|
||||
{
|
||||
/// Creates a new [`ExecTimeCollectorFeedback`]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user