working random cmplog mutations

This commit is contained in:
Andrea Fioraldi 2021-05-20 11:49:28 +02:00 committed by Omree
parent f61140bdcf
commit 16d4c36f12
7 changed files with 136 additions and 153 deletions

View File

@ -12,15 +12,15 @@ use libafl::{
events::setup_restarting_mgr_std, events::setup_restarting_mgr_std,
executors::{inprocess::InProcessExecutor, ExitKind}, executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or, feedback_or,
feedbacks::{CmpFeedback, CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback}, feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer}, fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes}, inputs::{BytesInput, HasTargetBytes},
mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::{I2SRandReplace, Tokens}, mutators::token_mutations::I2SRandReplace,
observers::{StdMapObserver, TimeObserver}, observers::{StdMapObserver, TimeObserver},
stages::{StdMutationalStage, TracingStage}, stages::{StdMutationalStage, TracingStage},
state::{HasCorpus, StdState}, state::{HasCorpus, StdState},
stats::MultiStats, stats::SimpleStats,
Error, Error,
}; };
@ -73,7 +73,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
let time_observer = TimeObserver::new("time"); let time_observer = TimeObserver::new("time");
let cmplog = unsafe { &mut CMPLOG_MAP }; let cmplog = unsafe { &mut CMPLOG_MAP };
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog); let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
let cmplog = unsafe { &mut CMPLOG_MAP }; let cmplog = unsafe { &mut CMPLOG_MAP };
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true); let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
@ -155,9 +155,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
} }
// Secondary harness due to mut ownership // Secondary harness due to mut ownership
let mut harness = |input: &BytesInput| { let mut harness = |buf: &[u8]| {
let target = input.target_bytes();
let buf = target.as_slice();
libfuzzer_test_one_input(buf); libfuzzer_test_one_input(buf);
ExitKind::Ok ExitKind::Ok
}; };
@ -172,14 +170,16 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
)?); )?);
// Setup a randomic Input2State stage // Setup a randomic Input2State stage
let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
//let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
// Setup a basic mutator // Setup a basic mutator
let mutator = StdScheduledMutator::new(havoc_mutations()); let mutator = StdScheduledMutator::new(havoc_mutations());
let mutational = StdMutationalStage::new(mutator); let mutational = StdMutationalStage::new(mutator);
// The order of the stages matter! // The order of the stages matter!
let mut stages = tuple_list!(tracing, i2s, mutational);
let mut stages = tuple_list!(tracing, mutational);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?; fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut restarting_mgr)?;

View File

@ -361,12 +361,9 @@ where
} }
/// Merge two `TupeList` /// Merge two `TupeList`
pub trait Merge<T>: TupleList pub trait Merge<T> {
where
T: TupleList,
{
/// The Resulting [`TupleList`], of an [`Merge::merge()`] call /// The Resulting [`TupleList`], of an [`Merge::merge()`] call
type MergeResult: TupleList; type MergeResult;
/// Merge and return the merged tuple /// Merge and return the merged tuple
#[must_use] #[must_use]
@ -374,10 +371,7 @@ where
} }
/// Implement merge for an empty tuple list. /// Implement merge for an empty tuple list.
impl<T> Merge<T> for () impl<T> Merge<T> for () {
where
T: TupleList,
{
type MergeResult = T; type MergeResult = T;
fn merge(self, value: T) -> Self::MergeResult { fn merge(self, value: T) -> Self::MergeResult {
@ -388,10 +382,7 @@ where
/// Implement merge for non-empty tuple list. /// Implement merge for non-empty tuple list.
impl<Head, Tail, T> Merge<T> for (Head, Tail) impl<Head, Tail, T> Merge<T> for (Head, Tail)
where where
T: TupleList,
Self: TupleList,
Tail: Merge<T>, Tail: Merge<T>,
(Head, Tail::MergeResult): TupleList,
{ {
type MergeResult = (Head, Tail::MergeResult); type MergeResult = (Head, Tail::MergeResult);

View File

@ -1,122 +0,0 @@
use alloc::string::{String, ToString};
use core::marker::PhantomData;
use serde::{Deserialize, Serialize};
use crate::{
bolts::{tuples::Named, AsSlice},
executors::ExitKind,
feedbacks::Feedback,
inputs::Input,
observers::{CmpMap, CmpObserver, CmpValues, ObserversTuple},
state::HasMetadata,
Error,
};
/// A state metadata holding a list of values logged from comparisons
#[derive(Serialize, Deserialize)]
pub struct CmpValuesMetadata {
/// A `list` of values.
pub list: Vec<CmpValues>,
}
crate::impl_serdeany!(CmpValuesMetadata);
impl AsSlice<CmpValues> for CmpValuesMetadata {
/// Convert to a slice
#[must_use]
fn as_slice(&self) -> &[CmpValues] {
self.list.as_slice()
}
}
impl CmpValuesMetadata {
/// Creates a new [`struct@CmpValuesMetadata`]
#[must_use]
pub fn new() -> Self {
Self { list: vec![] }
}
}
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct CmpFeedback<CM, CO>
where
CO: CmpObserver<CM>,
CM: CmpMap,
{
name: String,
phantom: PhantomData<(CM, CO)>,
}
impl<CM, CO, I, S> Feedback<I, S> for CmpFeedback<CM, CO>
where
I: Input,
CO: CmpObserver<CM>,
CM: CmpMap,
S: HasMetadata,
{
fn is_interesting<OT>(
&mut self,
state: &mut S,
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
if state.metadata().get::<CmpValuesMetadata>().is_none() {
state.add_metadata(CmpValuesMetadata::new());
}
let meta = state.metadata_mut().get_mut::<CmpValuesMetadata>().unwrap();
meta.list.clear();
// TODO Replace with match_name_type when stable
let observer = observers.match_name::<CO>(self.name()).unwrap();
let count = observer.usable_count();
for i in 0..count {
let execs = observer.map().usable_executions_for(i);
if execs > 0 {
// Recongize loops and discard
// TODO
for j in 0..execs {
meta.list.push(observer.map().values_of(i, j));
}
}
}
Ok(false)
}
}
impl<CM, CO> Named for CmpFeedback<CM, CO>
where
CO: CmpObserver<CM>,
CM: CmpMap,
{
#[inline]
fn name(&self) -> &str {
self.name.as_str()
}
}
impl<CM, CO> CmpFeedback<CM, CO>
where
CO: CmpObserver<CM>,
CM: CmpMap,
{
/// Creates a new [`CmpFeedback`]
#[must_use]
pub fn with_name(name: &'static str) -> Self {
Self {
name: name.to_string(),
phantom: PhantomData,
}
}
/// Creates a new [`CmpFeedback`]
#[must_use]
pub fn new(observer: &CO) -> Self {
Self {
name: observer.name().to_string(),
phantom: PhantomData,
}
}
}

View File

@ -4,9 +4,6 @@
pub mod map; pub mod map;
pub use map::*; pub use map::*;
pub mod cmp;
pub use cmp::*;
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};

View File

@ -15,10 +15,9 @@ use std::{
use crate::{ use crate::{
bolts::rands::Rand, bolts::rands::Rand,
feedbacks::cmp::CmpValuesMetadata,
inputs::{HasBytesVec, Input}, inputs::{HasBytesVec, Input},
mutators::{buffer_self_copy, mutations::buffer_copy, MutationResult, Mutator, Named}, mutators::{buffer_self_copy, mutations::buffer_copy, MutationResult, Mutator, Named},
observers::cmp::CmpValues, observers::cmp::{CmpValues, CmpValuesMetadata},
state::{HasMaxSize, HasMetadata, HasRand}, state::{HasMaxSize, HasMetadata, HasRand},
Error, Error,
}; };
@ -449,6 +448,8 @@ where
} }
} }
//println!("{:?}", result);
Ok(result) Ok(result)
} }
} }

View File

@ -8,13 +8,14 @@ use alloc::{
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{ use crate::{
bolts::{ownedref::OwnedRefMut, tuples::Named}, bolts::{ownedref::OwnedRefMut, tuples::Named, AsSlice},
executors::HasExecHooks, executors::HasExecHooks,
observers::Observer, observers::Observer,
state::HasMetadata,
Error, Error,
}; };
#[derive(Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
pub enum CmpValues { pub enum CmpValues {
U8((u8, u8)), U8((u8, u8)),
U16((u16, u16)), U16((u16, u16)),
@ -23,6 +24,53 @@ pub enum CmpValues {
Bytes((Vec<u8>, Vec<u8>)), Bytes((Vec<u8>, Vec<u8>)),
} }
impl CmpValues {
pub fn is_numeric(&self) -> bool {
match self {
CmpValues::U8(_) => true,
CmpValues::U16(_) => true,
CmpValues::U32(_) => true,
CmpValues::U64(_) => true,
_ => false,
}
}
pub fn to_u64_tuple(&self) -> Option<(u64, u64)> {
match self {
CmpValues::U8(t) => Some((t.0 as u64, t.1 as u64)),
CmpValues::U16(t) => Some((t.0 as u64, t.1 as u64)),
CmpValues::U32(t) => Some((t.0 as u64, t.1 as u64)),
CmpValues::U64(t) => Some((t.0 as u64, t.1 as u64)),
_ => None,
}
}
}
/// A state metadata holding a list of values logged from comparisons
#[derive(Serialize, Deserialize)]
pub struct CmpValuesMetadata {
/// A `list` of values.
pub list: Vec<CmpValues>,
}
crate::impl_serdeany!(CmpValuesMetadata);
impl AsSlice<CmpValues> for CmpValuesMetadata {
/// Convert to a slice
#[must_use]
fn as_slice(&self) -> &[CmpValues] {
self.list.as_slice()
}
}
impl CmpValuesMetadata {
/// Creates a new [`struct@CmpValuesMetadata`]
#[must_use]
pub fn new() -> Self {
Self { list: vec![] }
}
}
/// A [`CmpMap`] traces comparisons during the current execution /// A [`CmpMap`] traces comparisons during the current execution
pub trait CmpMap: Serialize + DeserializeOwned { pub trait CmpMap: Serialize + DeserializeOwned {
/// Get the number of cmps /// Get the number of cmps
@ -49,6 +97,64 @@ where
fn map(&self) -> &CM; fn map(&self) -> &CM;
fn map_mut(&mut self) -> &mut CM; fn map_mut(&mut self) -> &mut CM;
fn add_cmpvalues_meta<S>(&mut self, state: &mut S)
where
S: HasMetadata,
{
if state.metadata().get::<CmpValuesMetadata>().is_none() {
state.add_metadata(CmpValuesMetadata::new());
}
let meta = state.metadata_mut().get_mut::<CmpValuesMetadata>().unwrap();
meta.list.clear();
let count = self.usable_count();
for i in 0..count {
let execs = self.map().usable_executions_for(i);
if execs > 0 {
// Recongize loops and discard if needed
if execs > 4 {
let mut increasing_v0 = 0;
let mut increasing_v1 = 0;
let mut decreasing_v0 = 0;
let mut decreasing_v1 = 0;
let mut last: Option<CmpValues> = None;
for j in 0..execs {
let val = self.map().values_of(i, j);
if let Some(l) = last.and_then(|x| x.to_u64_tuple()) {
if let Some(v) = val.to_u64_tuple() {
if l.0.wrapping_add(1) == v.0 {
increasing_v0 += 1;
}
if l.1.wrapping_add(1) == v.1 {
increasing_v1 += 1;
}
if l.0.wrapping_sub(1) == v.0 {
decreasing_v0 += 1;
}
if l.1.wrapping_sub(1) == v.1 {
decreasing_v1 += 1;
}
}
}
last = Some(val);
}
// We check for execs-2 because the logged execs may wrap and have something like
// 8 9 10 3 4 5 6 7
if increasing_v0 >= execs - 2
|| increasing_v1 >= execs - 2
|| decreasing_v0 >= execs - 2
|| decreasing_v1 >= execs - 2
{
continue;
}
}
for j in 0..execs {
meta.list.push(self.map().values_of(i, j));
}
}
}
}
} }
/// A standard [`CmpObserver`] observer /// A standard [`CmpObserver`] observer

View File

@ -5,6 +5,7 @@ use libafl::{
bolts::{ownedref::OwnedRefMut, tuples::Named}, bolts::{ownedref::OwnedRefMut, tuples::Named},
executors::HasExecHooks, executors::HasExecHooks,
observers::{CmpMap, CmpObserver, CmpValues, Observer}, observers::{CmpMap, CmpObserver, CmpValues, Observer},
state::HasMetadata,
Error, Error,
}; };
@ -148,6 +149,7 @@ pub use libafl_cmplog_enabled as CMPLOG_ENABLED;
pub struct CmpLogObserver<'a> { pub struct CmpLogObserver<'a> {
map: OwnedRefMut<'a, CmpLogMap>, map: OwnedRefMut<'a, CmpLogMap>,
size: Option<OwnedRefMut<'a, usize>>, size: Option<OwnedRefMut<'a, usize>>,
add_meta: bool,
name: String, name: String,
} }
@ -171,7 +173,10 @@ impl<'a> CmpObserver<CmpLogMap> for CmpLogObserver<'a> {
impl<'a> Observer for CmpLogObserver<'a> {} impl<'a> Observer for CmpLogObserver<'a> {}
impl<'a, EM, I, S, Z> HasExecHooks<EM, I, S, Z> for CmpLogObserver<'a> { impl<'a, EM, I, S, Z> HasExecHooks<EM, I, S, Z> for CmpLogObserver<'a>
where
S: HasMetadata,
{
fn pre_exec( fn pre_exec(
&mut self, &mut self,
_fuzzer: &mut Z, _fuzzer: &mut Z,
@ -189,13 +194,17 @@ impl<'a, EM, I, S, Z> HasExecHooks<EM, I, S, Z> for CmpLogObserver<'a> {
fn post_exec( fn post_exec(
&mut self, &mut self,
_fuzzer: &mut Z, _fuzzer: &mut Z,
_state: &mut S, state: &mut S,
_mgr: &mut EM, _mgr: &mut EM,
_input: &I, _input: &I,
) -> Result<(), Error> { ) -> Result<(), Error> {
unsafe { unsafe {
CMPLOG_ENABLED = 0; CMPLOG_ENABLED = 0;
} }
if self.add_meta {
self.add_cmpvalues_meta(state);
}
Ok(()) Ok(())
} }
} }
@ -209,11 +218,12 @@ impl<'a> Named for CmpLogObserver<'a> {
impl<'a> CmpLogObserver<'a> { impl<'a> CmpLogObserver<'a> {
/// Creates a new [`CmpLogObserver`] with the given name. /// Creates a new [`CmpLogObserver`] with the given name.
#[must_use] #[must_use]
pub fn new(name: &'static str, map: &'a mut CmpLogMap) -> Self { pub fn new(name: &'static str, map: &'a mut CmpLogMap, add_meta: bool) -> Self {
Self { Self {
name: name.to_string(), name: name.to_string(),
size: None, size: None,
add_meta,
map: OwnedRefMut::Ref(map), map: OwnedRefMut::Ref(map),
} }
} }