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,
executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or,
feedbacks::{CmpFeedback, CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::{I2SRandReplace, Tokens},
mutators::token_mutations::I2SRandReplace,
observers::{StdMapObserver, TimeObserver},
stages::{StdMutationalStage, TracingStage},
state::{HasCorpus, StdState},
stats::MultiStats,
stats::SimpleStats,
Error,
};
@ -73,7 +73,7 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
let time_observer = TimeObserver::new("time");
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_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
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
let mut harness = |buf: &[u8]| {
libfuzzer_test_one_input(buf);
ExitKind::Ok
};
@ -172,14 +170,16 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
)?);
// 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
let mutator = StdScheduledMutator::new(havoc_mutations());
let mutational = StdMutationalStage::new(mutator);
// 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)?;

View File

@ -361,12 +361,9 @@ where
}
/// Merge two `TupeList`
pub trait Merge<T>: TupleList
where
T: TupleList,
{
pub trait Merge<T> {
/// The Resulting [`TupleList`], of an [`Merge::merge()`] call
type MergeResult: TupleList;
type MergeResult;
/// Merge and return the merged tuple
#[must_use]
@ -374,10 +371,7 @@ where
}
/// Implement merge for an empty tuple list.
impl<T> Merge<T> for ()
where
T: TupleList,
{
impl<T> Merge<T> for () {
type MergeResult = T;
fn merge(self, value: T) -> Self::MergeResult {
@ -388,10 +382,7 @@ where
/// Implement merge for non-empty tuple list.
impl<Head, Tail, T> Merge<T> for (Head, Tail)
where
T: TupleList,
Self: TupleList,
Tail: Merge<T>,
(Head, Tail::MergeResult): TupleList,
{
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 use map::*;
pub mod cmp;
pub use cmp::*;
use alloc::string::{String, ToString};
use serde::{Deserialize, Serialize};

View File

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

View File

@ -8,13 +8,14 @@ use alloc::{
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{
bolts::{ownedref::OwnedRefMut, tuples::Named},
bolts::{ownedref::OwnedRefMut, tuples::Named, AsSlice},
executors::HasExecHooks,
observers::Observer,
state::HasMetadata,
Error,
};
#[derive(Serialize, Deserialize)]
#[derive(Debug, Serialize, Deserialize)]
pub enum CmpValues {
U8((u8, u8)),
U16((u16, u16)),
@ -23,6 +24,53 @@ pub enum CmpValues {
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
pub trait CmpMap: Serialize + DeserializeOwned {
/// Get the number of cmps
@ -49,6 +97,64 @@ where
fn map(&self) -> &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

View File

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