Various improvements

Replace TimeObserver with ClockObserver
Add a Maximization Scheduler for Clocks
Factor out icount Metadata from Feedback
Allow Breakpoint removal in systemmode
This commit is contained in:
Alwin Berger 2022-02-06 19:36:28 +01:00
parent 2ead941419
commit 06d382bff8
4 changed files with 199 additions and 29 deletions

View File

@ -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,

View File

@ -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<CS, I, S> =
MinimizerCorpusScheduler<CS, MaxLenTimeFavFactor<I>, I, MapIndexesMetadata, S>;
/// Multiply the testcase size with the execution time.
/// This favors small and quick testcases.
#[derive(Debug, Clone)]
pub struct MaxLenTimeFavFactor<I>
where
I: Input + HasLen,
{
phantom: PhantomData<I>,
}
impl<I> FavFactor<I> for MaxLenTimeFavFactor<I>
where
I: Input + HasLen,
{
fn compute(entry: &mut Testcase<I>) -> Result<u64, Error> {
// 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)
}
}

View File

@ -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<u64>,
name: String,
}
impl<I, S> Feedback<I, S> for QemuClockIncreaseFeedback
impl<I, S> Feedback<I, S> 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<bool, Error>
where
EM: EventFirer<I>,
OT: ObserversTuple<I, S>,
{
// TODO Replace with match_name_type when stable
let observer = observers.match_name::<QemuClockObserver>(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<I>) -> 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<I, S> Feedback<I, S> for QemuClockIncreaseFeedback
where
I: Input,
S: HasFeedbackStates + HasClientPerfMonitor,
{
fn is_interesting<EM, OT>(
&mut self,
state: &mut S,
_manager: &mut EM,
_input: &I,
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
@ -132,8 +253,13 @@ where
{
let observer = _observers.match_name::<QemuClockObserver>("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::<MaxIcountMetadata>(&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<I>) -> 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")
}
}

View File

@ -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() };
}
}