working random cmplog mutations
This commit is contained in:
parent
f61140bdcf
commit
16d4c36f12
@ -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)?;
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
@ -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};
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user