cmplog observer

This commit is contained in:
Andrea Fioraldi 2021-05-20 01:25:01 +02:00 committed by Omree
parent ccfc95aa3a
commit 933b65dd86
7 changed files with 160 additions and 91 deletions

View File

@ -17,8 +17,7 @@ debug = true
[dependencies]
libafl = { path = "../../libafl/" }
libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_edges", "libfuzzer"] }
libafl_targets = { path = "../../libafl_targets/", features = ["sancov_pcguard_edges", "sancov_cmplog", "libfuzzer"] }
[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }

View File

@ -12,11 +12,11 @@ use libafl::{
events::setup_restarting_mgr_std,
executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or,
feedbacks::{CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
feedbacks::{CmpFeedback, CrashFeedback, MapFeedbackState, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::I2SRandReplace,
mutators::token_mutations::{I2SRandReplace, Tokens},
observers::{StdMapObserver, TimeObserver},
stages::{StdMutationalStage, TracingStage},
state::{HasCorpus, StdState},
@ -72,6 +72,9 @@ fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Re
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
let cmplog = unsafe { &mut CMPLOG_MAP };
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog);
let cmplog = unsafe { &mut CMPLOG_MAP };
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);

View File

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

@ -115,7 +115,7 @@ impl<'a, CM> StdCmpObserver<'a, CM>
where
CM: CmpMap,
{
/// Creates a new [`CmpObserver`] with the given name.
/// Creates a new [`StdCmpObserver`] with the given name.
#[must_use]
pub fn new(name: &'static str, map: &'a mut CM) -> Self {
Self {

View File

@ -1,54 +1,58 @@
use core::{marker::PhantomData, mem::drop};
use core::marker::PhantomData;
use crate::{
bolts::rands::Rand,
corpus::Corpus,
executors::{Executor, HasExecHooksTuple, HasObservers, HasObserversHooks},
fuzzer::Evaluator,
inputs::Input,
mark_feature_time,
observers::ObserversTuple,
mutators::Mutator,
stages::Stage,
start_timer,
state::{HasClientPerfStats, HasCorpus, HasExecutions},
state::{HasClientPerfStats, HasCorpus, HasRand},
Error,
};
#[cfg(feature = "introspection")]
use crate::stats::PerfFeature;
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct TracingStage<C, EM, I, OT, S, TE, Z>
where
I: Input,
C: Corpus<I>,
TE: Executor<EM, I, S, Z> + HasObservers<OT> + HasObserversHooks<EM, I, OT, S, Z>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
S: HasClientPerfStats + HasExecutions + HasCorpus<C, I>,
{
tracer_executor: TE,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(C, EM, I, OT, S, TE, Z)>,
}
// TODO multi mutators stage
impl<E, C, EM, I, OT, S, TE, Z> Stage<E, EM, S, Z> for TracingStage<C, EM, I, OT, S, TE, Z>
/// A Mutational stage is the stage in a fuzzing run that mutates inputs.
/// Mutational stages will usually have a range of mutations that are
/// being applied to the input one by one, between executions.
pub trait TracingStage<C, E, EM, I, M, S, Z>: Stage<E, EM, S, Z>
where
I: Input,
C: Corpus<I>,
TE: Executor<EM, I, S, Z> + HasObservers<OT> + HasObserversHooks<EM, I, OT, S, Z>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
S: HasClientPerfStats + HasExecutions + HasCorpus<C, I>,
M: Mutator<I, S>,
I: Input,
S: HasClientPerfStats + HasCorpus<C, I>,
Z: Evaluator<E, EM, I, S>,
{
#[inline]
fn perform(
/// The mutator registered for this stage
fn mutator(&self) -> &M;
/// The mutator registered for this stage (mutable)
fn mutator_mut(&mut self) -> &mut M;
/// Gets the number of iterations this mutator should run for.
fn iterations(&self, state: &mut S) -> usize;
/// Runs this (mutational) stage for the given testcase
#[allow(clippy::cast_possible_wrap)] // more than i32 stages on 32 bit system - highly unlikely...
fn perform_mutational(
&mut self,
fuzzer: &mut Z,
_executor: &mut E,
executor: &mut E,
state: &mut S,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
let num = self.iterations(state);
for i in 0..num {
start_timer!(state);
let input = state
let mut input = state
.corpus()
.get(corpus_idx)?
.borrow_mut()
@ -57,40 +61,103 @@ where
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
start_timer!(state);
self.tracer_executor
.pre_exec_observers(fuzzer, state, manager, &input)?;
mark_feature_time!(state, PerfFeature::PreExecObservers);
self.mutator_mut().mutate(state, &mut input, i as i32)?;
mark_feature_time!(state, PerfFeature::Mutate);
// Time is measured directly the `evaluate_input` function
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input)?;
start_timer!(state);
drop(
self.tracer_executor
.run_target(fuzzer, state, manager, &input)?,
);
mark_feature_time!(state, PerfFeature::TargetExecution);
*state.executions_mut() += 1;
start_timer!(state);
self.tracer_executor
.post_exec_observers(fuzzer, state, manager, &input)?;
mark_feature_time!(state, PerfFeature::PostExecObservers);
self.mutator_mut().post_exec(state, i as i32, corpus_idx)?;
mark_feature_time!(state, PerfFeature::MutatePostExec);
}
Ok(())
}
}
impl<C, EM, I, OT, S, TE, Z> TracingStage<C, EM, I, OT, S, TE, Z>
/// Default value, how many iterations each stage gets, as an upper bound
/// It may randomly continue earlier.
pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct StdMutationalStage<C, E, EM, I, M, R, S, Z>
where
I: Input,
C: Corpus<I>,
TE: Executor<EM, I, S, Z> + HasObservers<OT> + HasObserversHooks<EM, I, OT, S, Z>,
OT: ObserversTuple + HasExecHooksTuple<EM, I, S, Z>,
S: HasClientPerfStats + HasExecutions + HasCorpus<C, I>,
M: Mutator<I, S>,
I: Input,
R: Rand,
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
Z: Evaluator<E, EM, I, S>,
{
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(C, E, EM, I, R, S, Z)>,
}
impl<C, E, EM, I, M, R, S, Z> MutationalStage<C, E, EM, I, M, S, Z>
for StdMutationalStage<C, E, EM, I, M, R, S, Z>
where
C: Corpus<I>,
M: Mutator<I, S>,
I: Input,
R: Rand,
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
Z: Evaluator<E, EM, I, S>,
{
/// The mutator, added to this stage
#[inline]
fn mutator(&self) -> &M {
&self.mutator
}
/// The list of mutators, added to this stage (as mutable ref)
#[inline]
fn mutator_mut(&mut self) -> &mut M {
&mut self.mutator
}
/// Gets the number of iterations as a random number
fn iterations(&self, state: &mut S) -> usize {
1 + state.rand_mut().below(DEFAULT_MUTATIONAL_MAX_ITERATIONS) as usize
}
}
impl<C, E, EM, I, M, R, S, Z> Stage<E, EM, S, Z> for StdMutationalStage<C, E, EM, I, M, R, S, Z>
where
C: Corpus<I>,
M: Mutator<I, S>,
I: Input,
R: Rand,
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
Z: Evaluator<E, EM, I, S>,
{
#[inline]
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
self.perform_mutational(fuzzer, executor, state, manager, corpus_idx)
}
}
impl<C, E, EM, I, M, R, S, Z> StdMutationalStage<C, E, EM, I, M, R, S, Z>
where
C: Corpus<I>,
M: Mutator<I, S>,
I: Input,
R: Rand,
S: HasClientPerfStats + HasCorpus<C, I> + HasRand<R>,
Z: Evaluator<E, EM, I, S>,
{
/// Creates a new default mutational stage
pub fn new(tracer_executor: TE) -> Self {
pub fn new(mutator: M) -> Self {
Self {
tracer_executor,
mutator,
phantom: PhantomData,
}
}

View File

@ -2,9 +2,12 @@
//! The values will then be used in subsequent mutations.
use libafl::{
observers::{CmpMap, CmpValues},
bolts::{ownedref::OwnedRefMut, tuples::Named},
executors::HasExecHooks,
observers::{CmpMap, CmpObserver, CmpValues, Observer},
Error,
};
use serde::{Deserialize, Serialize};
// TODO compile time flag
@ -140,12 +143,11 @@ pub static mut libafl_cmplog_enabled: u8 = 0;
pub use libafl_cmplog_enabled as CMPLOG_ENABLED;
/// A [`CmpObserver`] observer for `CmpLog`
/// A [`CmpObserver`] observer for CmpLog
#[derive(Serialize, Deserialize, Debug)]
pub struct CmpLogObserver<'a> {
map: OwnedRefMut<'a, CmpLogMap>,
size: Option<OwnedRefMut<'a, usize>>,
add_meta: bool,
name: String,
}
@ -169,10 +171,7 @@ 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>
where
S: HasMetadata,
{
impl<'a, EM, I, S, Z> HasExecHooks<EM, I, S, Z> for CmpLogObserver<'a> {
fn pre_exec(
&mut self,
_fuzzer: &mut Z,
@ -190,16 +189,13 @@ where
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(())
}
}
@ -213,11 +209,11 @@ 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, add_meta: bool) -> Self {
pub fn new(name: &'static str, map: &'a mut CmpLogMap) -> Self {
Self {
name: name.to_string(),
size: None,
add_meta,
map: OwnedRefMut::Ref(map),
}
}

View File

@ -33,13 +33,13 @@ void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) {
uintptr_t k = RETADDR;
k = (k >> 4) ^ (k << 8);
#ifdef SANCOV_VALUE_PROFILE
k &= MAP_SIZE - 1;
#ifdef SANCOV_VALUE_PROFILE
k &= MAP_SIZE - 1;
__libafl_targets_value_profile1(k, arg1, arg2);
#endif
#ifdef SANCOV_CMPLOG
k &= CMPLOG_MAP_W - 1;
__libafl_targets_cmplog(k, 1, (uint64_t)arg1, (uint64_t)arg2);
#endif
@ -50,13 +50,13 @@ void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) {
uintptr_t k = RETADDR;
k = (k >> 4) ^ (k << 8);
#ifdef SANCOV_VALUE_PROFILE
k &= MAP_SIZE - 1;
#ifdef SANCOV_VALUE_PROFILE
k &= MAP_SIZE - 1;
__libafl_targets_value_profile2(k, arg1, arg2);
#endif
#ifdef SANCOV_CMPLOG
k &= CMPLOG_MAP_W - 1;
__libafl_targets_cmplog(k, 2, (uint64_t)arg1, (uint64_t)arg2);
#endif
@ -67,13 +67,13 @@ void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) {
uintptr_t k = RETADDR;
k = (k >> 4) ^ (k << 8);
#ifdef SANCOV_VALUE_PROFILE
k &= MAP_SIZE - 1;
#ifdef SANCOV_VALUE_PROFILE
k &= MAP_SIZE - 1;
__libafl_targets_value_profile4(k, arg1, arg2);
#endif
#ifdef SANCOV_CMPLOG
k &= CMPLOG_MAP_W - 1;
__libafl_targets_cmplog(k, 4, (uint64_t)arg1, (uint64_t)arg2);
#endif
@ -84,6 +84,7 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) {
uintptr_t k = RETADDR;
k = (k >> 4) ^ (k << 8);
#ifdef SANCOV_VALUE_PROFILE
k &= MAP_SIZE - 1;
__libafl_targets_value_profile8(k, arg1, arg2);
@ -93,13 +94,6 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) {
__libafl_targets_cmplog(k, 8, (uint64_t)arg1, (uint64_t)arg2);
#endif
#ifdef SANCOV_VALUE_PROFILE
__libafl_targets_value_profile8(k, arg1, arg2);
#endif
#ifdef SANCOV_CMPLOG
__libafl_targets_cmplog(k, 8, (uint64_t)arg1, (uint64_t)arg2);
#endif
}
void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
@ -112,9 +106,9 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
uintptr_t k = rt + i;
k = (k >> 4) ^ (k << 8);
k &= MAP_SIZE - 1;
// val , cases[i + 2]
#ifdef SANCOV_VALUE_PROFILE
k &= MAP_SIZE - 1;
switch (cases[1]) {
case 8:
__libafl_targets_value_profile1(k, (uint8_t)val, (uint8_t)cases[i + 2]);
@ -131,6 +125,7 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
}
#endif
#ifdef SANCOV_CMPLOG
k &= CMPLOG_MAP_W - 1;
__libafl_targets_cmplog(k, cases[1] / 8, val, cases[i + 2]);
#endif