From 3435a79e260f2bb4aaf17dd52ec670c963387aad Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Mon, 19 Dec 2022 17:44:58 +0100 Subject: [PATCH] add TimeMaximizerCorpusScheduler --- fuzzers/FRET/src/fuzzer.rs | 4 +- fuzzers/FRET/src/main.rs | 3 + fuzzers/FRET/src/worst.rs | 264 +++++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+), 2 deletions(-) create mode 100644 fuzzers/FRET/src/worst.rs diff --git a/fuzzers/FRET/src/fuzzer.rs b/fuzzers/FRET/src/fuzzer.rs index ce1f1447a9..2e21fbc042 100644 --- a/fuzzers/FRET/src/fuzzer.rs +++ b/fuzzers/FRET/src/fuzzer.rs @@ -37,7 +37,7 @@ use libafl_qemu::{ use crate::{ clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback}, qemustate::QemuStateRestoreHelper, - systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::DumpSystraceFeedback}, + systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::DumpSystraceFeedback}, worst::TimeMaximizerCorpusScheduler, }; pub static mut MAX_INPUT_SIZE: usize = 32; @@ -205,7 +205,7 @@ pub fn fuzz() { }); // A minimization+queue policy to get testcasess from the corpus - let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new()); + let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); // A fuzzer with feedbacks and a corpus scheduler let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); diff --git a/fuzzers/FRET/src/main.rs b/fuzzers/FRET/src/main.rs index d6ca96089a..57798ef3d3 100644 --- a/fuzzers/FRET/src/main.rs +++ b/fuzzers/FRET/src/main.rs @@ -1,3 +1,4 @@ +#![feature(is_sorted)] //! A libfuzzer-like fuzzer using qemu for binary-only coverage #[cfg(target_os = "linux")] mod fuzzer; @@ -7,6 +8,8 @@ mod clock; mod qemustate; #[cfg(target_os = "linux")] mod systemstate; +#[cfg(target_os = "linux")] +mod worst; #[cfg(target_os = "linux")] pub fn main() { diff --git a/fuzzers/FRET/src/worst.rs b/fuzzers/FRET/src/worst.rs new file mode 100644 index 0000000000..17b467df8c --- /dev/null +++ b/fuzzers/FRET/src/worst.rs @@ -0,0 +1,264 @@ +use core::fmt::Debug; +use core::cmp::Ordering::{Greater,Less,Equal}; +use libafl::inputs::BytesInput; +use libafl::inputs::HasTargetBytes; +use libafl::feedbacks::MapIndexesMetadata; +use libafl::corpus::Testcase; +use libafl::prelude::{UsesInput, AsSlice}; +use core::marker::PhantomData; +use libafl::schedulers::{MinimizerScheduler, TestcaseScore}; +use std::path::PathBuf; +use std::fs; +use hashbrown::{HashMap}; +use libafl::observers::ObserversTuple; +use libafl::executors::ExitKind; +use libafl::events::EventFirer; +use libafl::state::{HasClientPerfMonitor, HasCorpus, UsesState}; +use libafl::inputs::Input; +use libafl::feedbacks::Feedback; +use libafl::state::HasMetadata; +use libafl_qemu::edges::QemuEdgesMapMetadata; +use libafl::observers::MapObserver; +use serde::{Deserialize, Serialize}; +use std::cmp; + +use libafl::{ + bolts::{ + tuples::Named, + HasLen, + }, + observers::Observer, + Error, +}; + +use crate::clock::QemuClockObserver; +use crate::systemstate::FreeRTOSSystemStateMetadata; +//=========================== Scheduler + +pub type TimeMaximizerCorpusScheduler = + MinimizerScheduler::State>, MapIndexesMetadata>; + +/// Multiply the testcase size with the execution time. +/// This favors small and quick testcases. +#[derive(Debug, Clone)] +pub struct MaxTimeFavFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + phantom: PhantomData, +} + +impl TestcaseScore for MaxTimeFavFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + fn compute(entry: &mut Testcase, state: &S) -> Result { + // TODO maybe enforce entry.exec_time().is_some() + let execs_per_hour = 3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64(); + Ok(execs_per_hour) + } +} + +pub type LenTimeMaximizerCorpusScheduler = + MinimizerScheduler::Input>, MapIndexesMetadata>; + +pub type TimeStateMaximizerCorpusScheduler = + MinimizerScheduler::Input>, FreeRTOSSystemStateMetadata>; + +/// Multiply the testcase size with the execution time. +/// This favors small and quick testcases. +#[derive(Debug, Clone)] +pub struct MaxExecsLenFavFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + phantom: PhantomData, +} + +impl TestcaseScore for MaxExecsLenFavFactor +where + S: HasCorpus + HasMetadata, + S::Input: HasLen, +{ + fn compute(entry: &mut Testcase, state: &S) -> Result { + let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()); + let execs_times_length_per_hour = execs_per_hour*entry.cached_len()? as f64; + Ok(execs_times_length_per_hour) + } +} + +//=================================================================== + +/// A Feedback reporting if the Input consists of strictly decreasing bytes. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct SortedFeedback { +} + +impl Feedback for SortedFeedback +where + S: UsesInput + HasClientPerfMonitor, + S::Input: HasTargetBytes, +{ + #[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, + { + let t = _input.target_bytes(); + let tmp = t.as_slice(); + if tmp.len()<32 {return Ok(false);} + let tmp = Vec::::from(&tmp[0..32]); + // tmp.reverse(); + if tmp.is_sorted_by(|a,b| match a.partial_cmp(b).unwrap_or(Less) { + Less => Some(Greater), + Equal => Some(Greater), + Greater => Some(Less), + }) {return Ok(true)}; + return Ok(false); + } +} + +impl Named for SortedFeedback { + #[inline] + fn name(&self) -> &str { + "Sorted" + } +} + +impl SortedFeedback { + /// Creates a new [`HitFeedback`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} + +impl Default for SortedFeedback { + fn default() -> Self { + Self::new() + } +} + +//=================================================================== +/// A Feedback which expects a certain minimum execution time +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ExecTimeReachedFeedback +{ + target_time: u64, +} + +impl Feedback for ExecTimeReachedFeedback +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, + { + let observer = observers.match_name::("clock") + .expect("QemuClockObserver not found"); + Ok(observer.last_runtime() >= self.target_time) + } +} + +impl Named for ExecTimeReachedFeedback +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeReachedFeedback" + } +} + +impl ExecTimeReachedFeedback +where +{ + /// Creates a new [`ExecTimeReachedFeedback`] + #[must_use] + pub fn new(target_time : u64) -> Self { + Self {target_time: target_time} + } +} + +pub static mut EXEC_TIME_COLLECTION : Vec = Vec::new(); + +/// A Noop Feedback which records a list of all execution times +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ExecTimeCollectorFeedback +{ +} + +impl Feedback for ExecTimeCollectorFeedback +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, + { + let observer = observers.match_name::("clock") + .expect("QemuClockObserver not found"); + unsafe { EXEC_TIME_COLLECTION.push(observer.last_runtime().try_into().unwrap()); } + Ok(false) + } +} + +impl Named for ExecTimeCollectorFeedback +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeCollectorFeedback" + } +} + +impl ExecTimeCollectorFeedback +where +{ + /// Creates a new [`ExecTimeCollectorFeedback`] + #[must_use] + pub fn new() -> Self { + Self {} + } +} + +/// Shared Metadata for a SysStateFeedback +#[derive(Serialize, Deserialize, Clone, Debug, Default)] +pub struct ExecTimeCollectorFeedbackState +{ + collection: Vec, +} +impl Named for ExecTimeCollectorFeedbackState +{ + #[inline] + fn name(&self) -> &str { + "ExecTimeCollectorFeedbackState" + } +} \ No newline at end of file