add generation based genetic testing
This commit is contained in:
parent
c548c6bc09
commit
c628afaa81
@ -17,6 +17,7 @@ feed_systemgraph = [ "systemstate" ]
|
|||||||
feed_systemtrace = [ "systemstate" ]
|
feed_systemtrace = [ "systemstate" ]
|
||||||
feed_longest = [ ]
|
feed_longest = [ ]
|
||||||
feed_afl = [ ]
|
feed_afl = [ ]
|
||||||
|
feed_genetic = [ ]
|
||||||
fuzz_int = [ ]
|
fuzz_int = [ ]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
|
@ -73,6 +73,18 @@ rule build_feedlongest_int:
|
|||||||
shell:
|
shell:
|
||||||
"cargo build --target-dir {output} {def_flags},feed_longest,fuzz_int"
|
"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:
|
rule run_bench:
|
||||||
input:
|
input:
|
||||||
"build/{target}.elf",
|
"build/{target}.elf",
|
||||||
@ -114,11 +126,11 @@ rule run_bench:
|
|||||||
rule run_showmap:
|
rule run_showmap:
|
||||||
input:
|
input:
|
||||||
"build/{target}.elf",
|
"build/{target}.elf",
|
||||||
"bins/target_showmap",
|
"bins/target_showmap_int",
|
||||||
"timedump/{fuzzer}/{target}.{num}.case"
|
"mnt/timedump/{fuzzer}/{target}.{num}.case"
|
||||||
output:
|
output:
|
||||||
"timedump/{fuzzer}/{target}.{num}.trace.ron",
|
"mnt/timedump/{fuzzer}/{target}.{num}.trace.ron",
|
||||||
"timedump/{fuzzer}/{target}.{num}.case.time",
|
"mnt/timedump/{fuzzer}/{target}.{num}.case.time",
|
||||||
run:
|
run:
|
||||||
with open('target_symbols.csv') as csvfile:
|
with open('target_symbols.csv') as csvfile:
|
||||||
reader = csv.DictReader(csvfile)
|
reader = csv.DictReader(csvfile)
|
||||||
@ -176,7 +188,7 @@ rule all_bins:
|
|||||||
|
|
||||||
rule all_periodic:
|
rule all_periodic:
|
||||||
input:
|
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:
|
rule all_compare_afl_longest:
|
||||||
input:
|
input:
|
||||||
@ -184,4 +196,4 @@ rule all_compare_afl_longest:
|
|||||||
|
|
||||||
rule all_micro:
|
rule all_micro:
|
||||||
input:
|
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::{
|
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}, 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;
|
pub static mut RNG_SEED: u64 = 1;
|
||||||
|
|
||||||
@ -245,6 +245,11 @@ pub fn fuzz() {
|
|||||||
// Time feedback, this one does not need a feedback state
|
// Time feedback, this one does not need a feedback state
|
||||||
ClockTimeFeedback::new_with_observer(&clock_time_observer)
|
ClockTimeFeedback::new_with_observer(&clock_time_observer)
|
||||||
);
|
);
|
||||||
|
#[cfg(feature = "feed_genetic")]
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
feedback,
|
||||||
|
AlwaysTrueFeedback::new()
|
||||||
|
);
|
||||||
#[cfg(feature = "feed_afl")]
|
#[cfg(feature = "feed_afl")]
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
feedback,
|
feedback,
|
||||||
@ -297,7 +302,7 @@ pub fn fuzz() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// A minimization+queue policy to get testcasess from the corpus
|
// 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();
|
let scheduler = QueueScheduler::new();
|
||||||
#[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))]
|
#[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))]
|
||||||
let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new());
|
let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new());
|
||||||
@ -305,6 +310,8 @@ pub fn fuzz() {
|
|||||||
let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()));
|
let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()));
|
||||||
#[cfg(feature = "feed_systemgraph")]
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new());
|
let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new());
|
||||||
|
#[cfg(feature = "feed_genetic")]
|
||||||
|
let scheduler = GenerationScheduler::new();
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
//! with testcases only from a subset of the total corpus.
|
//! with testcases only from a subset of the total corpus.
|
||||||
|
|
||||||
use core::{marker::PhantomData};
|
use core::{marker::PhantomData};
|
||||||
use std::cmp::max;
|
use std::{cmp::{max, min}, mem::swap, borrow::BorrowMut};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
@ -11,11 +11,13 @@ use libafl::{
|
|||||||
corpus::{Corpus, Testcase},
|
corpus::{Corpus, Testcase},
|
||||||
inputs::UsesInput,
|
inputs::UsesInput,
|
||||||
schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB },
|
schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB },
|
||||||
state::{HasCorpus, HasMetadata, HasRand, UsesState},
|
state::{HasCorpus, HasMetadata, HasRand, UsesState, State},
|
||||||
Error, SerdeAny,
|
Error, SerdeAny, prelude::HasLen,
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::worst::MaxTimeFavFactor;
|
||||||
|
|
||||||
use super::FreeRTOSSystemStateMetadata;
|
use super::FreeRTOSSystemStateMetadata;
|
||||||
|
|
||||||
/// A state metadata holding a map of favoreds testcases for each map entry
|
/// 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: HasCorpus + HasMetadata,
|
||||||
S::Input: HasLen,
|
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()
|
// TODO maybe enforce entry.exec_time().is_some()
|
||||||
let et = entry.exec_time().expect("testcase.exec_time is needed for scheduler");
|
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");
|
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}
|
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