diff --git a/fuzzers/FRET/Cargo.toml b/fuzzers/FRET/Cargo.toml index 2ee354a86d..2ea9362b51 100644 --- a/fuzzers/FRET/Cargo.toml +++ b/fuzzers/FRET/Cargo.toml @@ -17,6 +17,7 @@ feed_systemgraph = [ "systemstate" ] feed_systemtrace = [ "systemstate" ] feed_longest = [ ] feed_afl = [ ] +feed_genetic = [ ] fuzz_int = [ ] [profile.release] diff --git a/fuzzers/FRET/benchmark/Snakefile b/fuzzers/FRET/benchmark/Snakefile index 9f9d2ee628..504d01f352 100644 --- a/fuzzers/FRET/benchmark/Snakefile +++ b/fuzzers/FRET/benchmark/Snakefile @@ -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)) diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index ff28de50c8..2138a51407 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -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); diff --git a/fuzzers/FRET/src/systemstate/schedulers.rs b/fuzzers/FRET/src/systemstate/schedulers.rs index 258e610391..56318161b1 100644 --- a/fuzzers/FRET/src/systemstate/schedulers.rs +++ b/fuzzers/FRET/src/systemstate/schedulers.rs @@ -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 @@ -131,4 +133,135 @@ where skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB, } } -} \ No newline at end of file +} + +//========================================================================================== + +/// 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 { + phantom: PhantomData, + gen_size: usize, +} + +impl UsesState for GenerationScheduler +where + S: UsesInput, +{ + type State = S; +} + +impl Scheduler for GenerationScheduler +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 { + 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::().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::() { + 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<::Input> + ) -> Result<(), Error> { + // println!("On Replace {_idx}"); + Ok(()) + } + + fn on_remove( + &self, + state: &mut Self::State, + idx: usize, + _testcase: &Option::Input>> + ) -> Result<(), Error> { + // println!("On Remove {idx}"); + if let Some(gm) = state.metadata_mut().get_mut::() { + gm.next_gen = gm.next_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::>(); + gm.current_gen = gm.current_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::>(); + } else { + state.add_metadata(GeneticMetadata::new(vec![], vec![])); + } + Ok(()) + } +} + +impl GenerationScheduler +{ + pub fn new() -> Self { + Self { + phantom: PhantomData, + gen_size: 100, + } + } +} diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs index 36e5e3f396..df79289a90 100644 --- a/fuzzers/FRET/src/worst.rs +++ b/fuzzers/FRET/src/worst.rs @@ -54,7 +54,7 @@ where S: HasCorpus + HasMetadata, S::Input: HasLen, { - fn compute(entry: &mut Testcase, state: &S) -> Result { + fn compute(entry: &mut Testcase<::Input>, state: &S) -> Result { // 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"); @@ -332,4 +332,50 @@ where pub fn new() -> Self { 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 Feedback for AlwaysTrueFeedback +where + S: UsesInput + HasClientPerfMonitor, +{ + #[allow(clippy::wrong_self_convention)] + fn is_interesting( + &mut self, + _state: &mut S, + _manager: &mut EM, + _input: &S::Input, + _observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + 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 { + } + } } \ No newline at end of file