From 06d382bff8c6dd1a2a2efede6c949e3b30fbe1cb Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Sun, 6 Feb 2022 19:36:28 +0100 Subject: [PATCH] Various improvements Replace TimeObserver with ClockObserver Add a Maximization Scheduler for Clocks Factor out icount Metadata from Feedback Allow Breakpoint removal in systemmode --- fuzzers/wcet_qemu_sys/src/fuzzer.rs | 19 +++- fuzzers/wcet_qemu_sys/src/worst.rs | 38 ++++++- libafl_qemu/src/clock.rs | 162 ++++++++++++++++++++++++---- libafl_qemu/src/emu.rs | 9 +- 4 files changed, 199 insertions(+), 29 deletions(-) diff --git a/fuzzers/wcet_qemu_sys/src/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/fuzzer.rs index 48ac651d88..1042acc6eb 100644 --- a/fuzzers/wcet_qemu_sys/src/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/fuzzer.rs @@ -1,6 +1,9 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. -use libafl_qemu::QemuClockObserver; +use libafl::mutators::ByteFlipMutator; +use libafl::mutators::BitFlipMutator; +use crate::worst::LenTimeMaximizerCorpusScheduler; +use libafl::corpus::MinimizerCorpusScheduler; use hashbrown::HashMap; use libafl::events::SimpleEventManager; use clap::{App, Arg}; @@ -57,6 +60,10 @@ use libafl_qemu::{ filter_qemu_args, snapshot_sys::QemuSysSnapshotHelper, QemuExecutor, + clock, + QemuClockObserver, + clock::ClockFeedback, + clock::QemuClockIncreaseFeedback }; use crate::worst::{HitFeedback,HitcountsMapObserver,HitImprovingFeedback}; @@ -283,7 +290,7 @@ fn fuzz( HitcountsMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter)); // Create an observation channel to keep track of the execution time - let time_observer = TimeObserver::new("time"); + // let time_observer = TimeObserver::new("time"); let clock_observer = QemuClockObserver::default(); // Create an observation channel using cmplog map @@ -304,7 +311,9 @@ fn fuzz( }; let feedback = feedback_or!( MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false), - HitImprovingFeedback::new(target_map.clone()) + HitImprovingFeedback::new(target_map.clone()), + QemuClockIncreaseFeedback::default(), + ClockFeedback::new_with_observer(&clock_observer) ); // let feedback = feedback_or!( // // New maximization map feedback linked to the edges observer and the feedback state @@ -329,7 +338,7 @@ fn fuzz( OnDiskCorpus::new(objective_dir).unwrap(), // States of the feedbacks. // They are the data related to the feedbacks that you want to persist in the State. - tuple_list!(feedback_state), + tuple_list!(feedback_state,clock::MaxIcountMetadata::default()), ) }; @@ -380,7 +389,7 @@ fn fuzz( //QemuAsanHelper::new(), QemuSysSnapshotHelper::new() ), - tuple_list!(edges_observer, time_observer, clock_observer), + tuple_list!(edges_observer, clock_observer), &mut fuzzer, &mut state, &mut mgr, diff --git a/fuzzers/wcet_qemu_sys/src/worst.rs b/fuzzers/wcet_qemu_sys/src/worst.rs index 16ec309e3e..2c0a3d5dc2 100644 --- a/fuzzers/wcet_qemu_sys/src/worst.rs +++ b/fuzzers/wcet_qemu_sys/src/worst.rs @@ -1,3 +1,8 @@ +use libafl::feedbacks::MapIndexesMetadata; +use libafl::corpus::Testcase; +use libafl::corpus::FavFactor; +use core::marker::PhantomData; +use libafl::corpus::MinimizerCorpusScheduler; use std::path::PathBuf; use std::fs; use libafl::observers::VariableMapObserver; @@ -185,7 +190,7 @@ where } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); let hit_target = mean_sum_of_squares <= self.target_msd; - eprintln!("hit target: {} {} {:?}",hit_target,mean_sum_of_squares, _input); + // eprintln!("hit target: {} {} {:?}",hit_target,mean_sum_of_squares, _input); if hit_target { Ok(true) } else { @@ -314,9 +319,10 @@ where } } let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64); - let hit_target = mean_sum_of_squares <= self.best_msd; - eprintln!("improving: {}",hit_target); + let hit_target = mean_sum_of_squares < self.best_msd; + // eprintln!("improving: {}",hit_target); if hit_target { + // println!("Hit Improving: {}",mean_sum_of_squares); self.best_msd = mean_sum_of_squares; Ok(true) } else { @@ -408,7 +414,7 @@ impl Default for DumpMapFeedback { } //=========================== Debugging Feedback -/// A [`Feedback`] meant to dump the edgemap for debugging. +/// A NoOp [`Feedback`] with fixed response. #[derive(Debug)] pub struct DummyFeedback { response: bool @@ -455,4 +461,28 @@ impl Default for DummyFeedback { fn default() -> Self { Self::new(true) } +} + +pub type LenTimeMaximizerCorpusScheduler = + MinimizerCorpusScheduler, I, MapIndexesMetadata, S>; + +/// Multiply the testcase size with the execution time. +/// This favors small and quick testcases. +#[derive(Debug, Clone)] +pub struct MaxLenTimeFavFactor +where + I: Input + HasLen, +{ + phantom: PhantomData, +} + +impl FavFactor for MaxLenTimeFavFactor +where + I: Input + HasLen, +{ + fn compute(entry: &mut Testcase) -> 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()) as u64; + Ok(execs_per_hour) + } } \ No newline at end of file diff --git a/libafl_qemu/src/clock.rs b/libafl_qemu/src/clock.rs index 04c669afb6..9e05c4b959 100644 --- a/libafl_qemu/src/clock.rs +++ b/libafl_qemu/src/clock.rs @@ -31,21 +31,66 @@ use libafl::feedbacks::Feedback; use libafl::SerdeAny; use libafl::state::HasMetadata; use libafl::corpus::testcase::Testcase; +use core::{fmt::Debug, time::Duration}; +use libafl::feedbacks::FeedbackState; +use libafl::state::HasFeedbackStates; +use libafl::bolts::tuples::MatchName; //========== Metadata #[derive(Debug, Serialize, Deserialize, SerdeAny)] pub struct QemuIcountMetadata { - runtime: i64, + runtime: u64, } // libafl::impl_serdeany!(QemuIcountMetadata); +/// Metadata for [`QemuClockIncreaseFeedback`] +#[derive(Debug, Serialize, Deserialize, SerdeAny)] +pub struct MaxIcountMetadata { + pub max_icount_seen: u64, + pub name: String, +} + +impl FeedbackState for MaxIcountMetadata +{ + fn reset(&mut self) -> Result<(), Error> { + self.max_icount_seen = 0; + Ok(()) + } +} + +impl Named for MaxIcountMetadata +{ + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl MaxIcountMetadata +{ + /// Create new `MaxIcountMetadata` + #[must_use] + pub fn new(name: &'static str) -> Self { + Self { + max_icount_seen: 0, + name: name.to_string(), + } + } +} + +impl Default for MaxIcountMetadata { + fn default() -> Self { + Self::new("MaxClock") + } +} + //========== Observer /// A simple observer, just overlooking the runtime of the target. #[derive(Serialize, Deserialize, Debug, Clone)] pub struct QemuClockObserver { name: String, - last_runtime: i64, + last_runtime: u64, } impl QemuClockObserver { @@ -60,7 +105,7 @@ impl QemuClockObserver { /// Gets the runtime for the last execution of this target. #[must_use] - pub fn last_runtime(&self) -> i64 { + pub fn last_runtime(&self) -> u64 { self.last_runtime } } @@ -83,7 +128,7 @@ where // } else { // println!("Found Metadata"); // } - println!("Observer Clock: {}",self.last_runtime()); + // println!("Observer Clock: {}",self.last_runtime()); Ok(()) } } @@ -105,15 +150,16 @@ impl Default for QemuClockObserver { } //========== Feedback - -/// A [`Feedback`] rewarding increasing the execution cycles on Qemu. -#[derive(Debug)] -pub struct QemuClockIncreaseFeedback { - maximum: i64, - last_runtime: i64, +/// Nop feedback that annotates execution time in the new testcase, if any +/// for this Feedback, the testcase is never interesting (use with an OR) +/// It decides, if the given [`ClockObserver`] value of a run is interesting. +#[derive(Serialize, Deserialize, Clone, Debug)] +pub struct ClockFeedback { + exec_time: Option, + name: String, } -impl Feedback for QemuClockIncreaseFeedback +impl Feedback for ClockFeedback where I: Input, S: HasClientPerfMonitor, @@ -123,6 +169,81 @@ where _state: &mut S, _manager: &mut EM, _input: &I, + observers: &OT, + _exit_kind: &ExitKind, + ) -> Result + where + EM: EventFirer, + OT: ObserversTuple, + { + // TODO Replace with match_name_type when stable + let observer = observers.match_name::(self.name()).unwrap(); + self.exec_time = Some(observer.last_runtime()); + Ok(false) + } + + /// Append to the testcase the generated metadata in case of a new corpus item + #[inline] + fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { + *testcase.exec_time_mut() = match self.exec_time { + Some(s) => Some(Duration::new(0,360*s as u32)), + None => None, + }; + self.exec_time = None; + Ok(()) + } + + /// Discard the stored metadata in case that the testcase is not added to the corpus + #[inline] + fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> { + self.exec_time = None; + Ok(()) + } +} + +impl Named for ClockFeedback { + #[inline] + fn name(&self) -> &str { + self.name.as_str() + } +} + +impl ClockFeedback { + /// Creates a new [`ClockFeedback`], deciding if the value of a [`TimeObserver`] with the given `name` of a run is interesting. + #[must_use] + pub fn new(name: &'static str) -> Self { + Self { + exec_time: None, + name: name.to_string(), + } + } + + /// Creates a new [`ClockFeedback`], deciding if the given [`TimeObserver`] value of a run is interesting. + #[must_use] + pub fn new_with_observer(observer: &QemuClockObserver) -> Self { + Self { + exec_time: None, + name: observer.name().to_string(), + } + } +} + +/// A [`Feedback`] rewarding increasing the execution cycles on Qemu. +#[derive(Debug)] +pub struct QemuClockIncreaseFeedback { + name: String, +} + +impl Feedback for QemuClockIncreaseFeedback +where + I: Input, + S: HasFeedbackStates + HasClientPerfMonitor, +{ + fn is_interesting( + &mut self, + state: &mut S, + _manager: &mut EM, + _input: &I, _observers: &OT, _exit_kind: &ExitKind, ) -> Result @@ -132,8 +253,13 @@ where { let observer = _observers.match_name::("clock") .expect("QemuClockObserver not found"); - if observer.last_runtime() > self.maximum { - self.maximum = observer.last_runtime(); + let clock_state = state + .feedback_states_mut() + .match_name_mut::(&self.name) + .unwrap(); + if observer.last_runtime() > clock_state.max_icount_seen { + // println!("Clock improving {}",observer.last_runtime()); + clock_state.max_icount_seen = observer.last_runtime(); return Ok(true); } Ok(false) @@ -142,7 +268,7 @@ where /// Append to the testcase the generated metadata in case of a new corpus item #[inline] fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase) -> Result<(), Error> { - testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); + // testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime}); Ok(()) } @@ -157,20 +283,20 @@ where impl Named for QemuClockIncreaseFeedback { #[inline] fn name(&self) -> &str { - "QemuClockFeedback" + &self.name } } impl QemuClockIncreaseFeedback { /// Creates a new [`HitFeedback`] #[must_use] - pub fn new() -> Self { - Self {maximum: 0, last_runtime: 0} + pub fn new(name: &'static str) -> Self { + Self {name: String::from(name)} } } impl Default for QemuClockIncreaseFeedback { fn default() -> Self { - Self::new() + Self::new("MaxClock") } } \ No newline at end of file diff --git a/libafl_qemu/src/emu.rs b/libafl_qemu/src/emu.rs index 532766f483..6f5d46a20c 100644 --- a/libafl_qemu/src/emu.rs +++ b/libafl_qemu/src/emu.rs @@ -183,6 +183,8 @@ extern "C" { fn libafl_qemu_set_breakpoint(addr: u64) -> i32; #[cfg(feature = "systemmode")] fn libafl_qemu_set_native_breakpoint(addr: u64) -> i32; + #[cfg(feature = "systemmode")] + fn libafl_qemu_remove_native_breakpoint(addr: u64) -> i32; fn libafl_qemu_remove_breakpoint(addr: u64) -> i32; fn libafl_qemu_set_hook(addr: u64, callback: extern "C" fn(u64), val: u64) -> i32; fn libafl_qemu_remove_hook(addr: u64) -> i32; @@ -200,7 +202,7 @@ extern "C" { #[cfg(feature = "systemmode")] fn libafl_snapshot_load(name: *const c_char) -> i32; #[cfg(feature = "systemmode")] - pub fn libafl_get_clock() -> i64; + pub fn libafl_get_clock() -> u64; fn strlen(s: *const u8) -> usize; @@ -438,6 +440,9 @@ impl Emulator { pub fn remove_breakpoint(&self, addr: u64) { unsafe { + #[cfg(feature = "systemmode")] + libafl_qemu_remove_native_breakpoint(addr); + #[cfg(not(feature = "systemmode"))] libafl_qemu_remove_breakpoint(addr); } } @@ -682,7 +687,7 @@ impl Emulator { } #[cfg(feature = "systemmode")] - pub fn get_ticks(&self) -> i64{ + pub fn get_ticks(&self) -> u64{ return unsafe { libafl_get_clock() }; } }