Refactor cmplog observers (#1603)
* refactor * Rename Everything * fmt * chg * test * aa * doc fix * fix? * doc
This commit is contained in:
parent
0bba8535b8
commit
bc91436ef4
@ -22,5 +22,6 @@ which = "4.4"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../libafl/" }
|
libafl = { path = "../../libafl/" }
|
||||||
libafl_bolts = { path = "../../libafl_bolts/" }
|
libafl_bolts = { path = "../../libafl_bolts/" }
|
||||||
|
libafl_targets = { path = "../../libafl_targets/" }
|
||||||
clap = { version = "4.0", features = ["default"] }
|
clap = { version = "4.0", features = ["default"] }
|
||||||
nix = "0.26"
|
nix = "0.26"
|
||||||
|
@ -21,9 +21,7 @@ use libafl::{
|
|||||||
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
|
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
|
||||||
StdMOptMutator, StdScheduledMutator, Tokens,
|
StdMOptMutator, StdScheduledMutator, Tokens,
|
||||||
},
|
},
|
||||||
observers::{
|
observers::{HitcountsMapObserver, StdCmpValuesObserver, StdMapObserver, TimeObserver},
|
||||||
AFLppCmpMap, HitcountsMapObserver, StdCmpValuesObserver, StdMapObserver, TimeObserver,
|
|
||||||
},
|
|
||||||
schedulers::{
|
schedulers::{
|
||||||
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
||||||
},
|
},
|
||||||
@ -41,6 +39,7 @@ use libafl_bolts::{
|
|||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
AsMutSlice,
|
AsMutSlice,
|
||||||
};
|
};
|
||||||
|
use libafl_targets::cmps::AFLppCmpLogMap;
|
||||||
use nix::sys::signal::Signal;
|
use nix::sys::signal::Signal;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
@ -342,11 +341,11 @@ fn fuzz(
|
|||||||
if let Some(exec) = &cmplog_exec {
|
if let Some(exec) = &cmplog_exec {
|
||||||
// The cmplog map shared between observer and executor
|
// The cmplog map shared between observer and executor
|
||||||
let mut cmplog_shmem = shmem_provider
|
let mut cmplog_shmem = shmem_provider
|
||||||
.new_shmem(core::mem::size_of::<AFLppCmpMap>())
|
.new_shmem(core::mem::size_of::<AFLppCmpLogMap>())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// let the forkserver know the shmid
|
// let the forkserver know the shmid
|
||||||
cmplog_shmem.write_to_env("__AFL_CMPLOG_SHM_ID").unwrap();
|
cmplog_shmem.write_to_env("__AFL_CMPLOG_SHM_ID").unwrap();
|
||||||
let cmpmap = unsafe { cmplog_shmem.as_object_mut::<AFLppCmpMap>() };
|
let cmpmap = unsafe { cmplog_shmem.as_object_mut::<AFLppCmpLogMap>() };
|
||||||
|
|
||||||
let cmplog_observer = StdCmpValuesObserver::new("cmplog", cmpmap, true);
|
let cmplog_observer = StdCmpValuesObserver::new("cmplog", cmpmap, true);
|
||||||
|
|
||||||
|
@ -22,5 +22,6 @@ which = "4.4"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../libafl/" }
|
libafl = { path = "../../libafl/" }
|
||||||
libafl_bolts = { path = "../../libafl_bolts/" }
|
libafl_bolts = { path = "../../libafl_bolts/" }
|
||||||
|
libafl_targets = { path = "../../libafl_targets/" }
|
||||||
clap = { version = "4.0", features = ["default"] }
|
clap = { version = "4.0", features = ["default"] }
|
||||||
nix = "0.26"
|
nix = "0.26"
|
@ -21,16 +21,13 @@ use libafl::{
|
|||||||
scheduled::havoc_mutations, token_mutations::AFLppRedQueen, tokens_mutations,
|
scheduled::havoc_mutations, token_mutations::AFLppRedQueen, tokens_mutations,
|
||||||
StdMOptMutator, Tokens,
|
StdMOptMutator, Tokens,
|
||||||
},
|
},
|
||||||
observers::{
|
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
|
||||||
AFLppCmpMap, AFLppCmpObserver, HitcountsMapObserver, StdMapObserver, TimeObserver,
|
|
||||||
},
|
|
||||||
schedulers::{
|
schedulers::{
|
||||||
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
||||||
},
|
},
|
||||||
stages::{
|
stages::{
|
||||||
calibrate::CalibrationStage, mutational::MultiMutationalStage,
|
calibrate::CalibrationStage, mutational::MultiMutationalStage,
|
||||||
power::StdPowerMutationalStage, tracing::AFLppCmplogTracingStage, ColorizationStage,
|
power::StdPowerMutationalStage, ColorizationStage, IfStage,
|
||||||
IfStage,
|
|
||||||
},
|
},
|
||||||
state::{HasCorpus, HasMetadata, StdState},
|
state::{HasCorpus, HasMetadata, StdState},
|
||||||
Error,
|
Error,
|
||||||
@ -42,6 +39,10 @@ use libafl_bolts::{
|
|||||||
tuples::{tuple_list, Merge},
|
tuples::{tuple_list, Merge},
|
||||||
AsMutSlice,
|
AsMutSlice,
|
||||||
};
|
};
|
||||||
|
use libafl_targets::{
|
||||||
|
cmps::{observers::AFLppCmpLogObserver, stages::AFLppCmplogTracingStage},
|
||||||
|
AFLppCmpLogMap,
|
||||||
|
};
|
||||||
use nix::sys::signal::Signal;
|
use nix::sys::signal::Signal;
|
||||||
|
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
@ -344,13 +345,13 @@ fn fuzz(
|
|||||||
if let Some(exec) = &cmplog_exec {
|
if let Some(exec) = &cmplog_exec {
|
||||||
// The cmplog map shared between observer and executor
|
// The cmplog map shared between observer and executor
|
||||||
let mut cmplog_shmem = shmem_provider
|
let mut cmplog_shmem = shmem_provider
|
||||||
.new_shmem(core::mem::size_of::<AFLppCmpMap>())
|
.new_shmem(core::mem::size_of::<AFLppCmpLogMap>())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
// let the forkserver know the shmid
|
// let the forkserver know the shmid
|
||||||
cmplog_shmem.write_to_env("__AFL_CMPLOG_SHM_ID").unwrap();
|
cmplog_shmem.write_to_env("__AFL_CMPLOG_SHM_ID").unwrap();
|
||||||
let cmpmap = unsafe { cmplog_shmem.as_object_mut::<AFLppCmpMap>() };
|
let cmpmap = unsafe { cmplog_shmem.as_object_mut::<AFLppCmpLogMap>() };
|
||||||
|
|
||||||
let cmplog_observer = AFLppCmpObserver::new("cmplog", cmpmap, true);
|
let cmplog_observer = AFLppCmpLogObserver::new("cmplog", cmpmap, true);
|
||||||
|
|
||||||
let cmplog_forkserver = ForkserverExecutor::builder()
|
let cmplog_forkserver = ForkserverExecutor::builder()
|
||||||
.program(exec)
|
.program(exec)
|
||||||
|
7
fuzzers/fuzzbench_forkserver_cmplog/test/compile.sh
Executable file
7
fuzzers/fuzzbench_forkserver_cmplog/test/compile.sh
Executable file
@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
~/AFLplusplus/afl-cc -O0 test-cmplog.c -o test-cmplog.afl
|
||||||
|
AFL_LLVM_CMPLOG=1 ~/AFLplusplus/afl-cc -O0 test-cmplog.c -o test-cmplog.cmplog
|
||||||
|
|
||||||
|
cp ./test-cmplog.afl ..
|
||||||
|
cp ./test-cmplog.cmplog ..
|
37
fuzzers/fuzzbench_forkserver_cmplog/test/test-cmplog.c
Normal file
37
fuzzers/fuzzbench_forkserver_cmplog/test/test-cmplog.c
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
int LLVMFuzzerTestOneInput(const uint8_t *buf, size_t i) {
|
||||||
|
if (i < 15) return -1;
|
||||||
|
if (buf[0] != 'A') return 0;
|
||||||
|
if (buf[1] != 'B') return 0;
|
||||||
|
if (buf[2] != 'C') return 0;
|
||||||
|
if (buf[3] != 'D') return 0;
|
||||||
|
int *icmp = (int *)(buf + 4);
|
||||||
|
if (*icmp != 0x69694141) return 0;
|
||||||
|
if (memcmp(buf + 8, "1234EF", 6) == 0) abort();
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
|
||||||
|
unsigned char buf[1024];
|
||||||
|
ssize_t i;
|
||||||
|
while (__AFL_LOOP(1000)) {
|
||||||
|
|
||||||
|
i = read(0, (char *)buf, sizeof(buf) - 1);
|
||||||
|
if (i > 0) buf[i] = 0;
|
||||||
|
LLVMFuzzerTestOneInput(buf, i);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,17 +1,15 @@
|
|||||||
//! The `CmpObserver` provides access to the logged values of CMP instructions
|
//! The `CmpObserver` provides access to the logged values of CMP instructions
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
alloc::alloc_zeroed,
|
|
||||||
boxed::Box,
|
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use core::{alloc::Layout, fmt::Debug, marker::PhantomData};
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
use c2rust_bitfields::BitfieldStruct;
|
use c2rust_bitfields::BitfieldStruct;
|
||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libafl_bolts::{ownedref::OwnedRefMut, serdeany::SerdeAny, AsMutSlice, AsSlice, Named};
|
use libafl_bolts::{ownedref::OwnedRefMut, serdeany::SerdeAny, AsMutSlice, AsSlice, Named};
|
||||||
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
|
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
executors::ExitKind, inputs::UsesInput, observers::Observer, state::HasMetadata, Error,
|
executors::ExitKind, inputs::UsesInput, observers::Observer, state::HasMetadata, Error,
|
||||||
@ -24,7 +22,7 @@ where
|
|||||||
CM: CmpMap + Debug,
|
CM: CmpMap + Debug,
|
||||||
{
|
{
|
||||||
/// Extra data used by the metadata when adding information from a `CmpObserver`, for example
|
/// Extra data used by the metadata when adding information from a `CmpObserver`, for example
|
||||||
/// the `original` field in `AFLppCmpObserver`
|
/// the `original` field in `AFLppCmpLogObserver`
|
||||||
type Data: 'a + Debug + Default + Serialize + DeserializeOwned;
|
type Data: 'a + Debug + Default + Serialize + DeserializeOwned;
|
||||||
|
|
||||||
/// Instantiate a new metadata instance. This is used by `CmpObserver` to create a new
|
/// Instantiate a new metadata instance. This is used by `CmpObserver` to create a new
|
||||||
@ -34,7 +32,7 @@ where
|
|||||||
|
|
||||||
/// Add comparisons to a metadata from a `CmpObserver`. `cmp_map` is mutable in case
|
/// Add comparisons to a metadata from a `CmpObserver`. `cmp_map` is mutable in case
|
||||||
/// it is needed for a custom map, but this is not utilized for `CmpObserver` or
|
/// it is needed for a custom map, but this is not utilized for `CmpObserver` or
|
||||||
/// `AFLppCmpObserver`.
|
/// `AFLppCmpLogObserver`.
|
||||||
fn add_from(&mut self, usable_count: usize, cmp_map: &mut CM, cmp_observer_data: Self::Data);
|
fn add_from(&mut self, usable_count: usize, cmp_map: &mut CM, cmp_observer_data: Self::Data);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -458,154 +456,6 @@ struct cmp_map {
|
|||||||
};
|
};
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/// A [`CmpObserver`] observer for AFL++ redqueen
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
pub struct AFLppCmpObserver<'a, S>
|
|
||||||
where
|
|
||||||
S: UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
cmp_map: OwnedRefMut<'a, AFLppCmpMap>,
|
|
||||||
size: Option<OwnedRefMut<'a, usize>>,
|
|
||||||
name: String,
|
|
||||||
add_meta: bool,
|
|
||||||
original: <AFLppCmpValuesMetadata as CmpObserverMetadata<'a, AFLppCmpMap>>::Data,
|
|
||||||
phantom: PhantomData<S>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S> CmpObserver<'a, AFLppCmpMap, S, AFLppCmpValuesMetadata> for AFLppCmpObserver<'a, S>
|
|
||||||
where
|
|
||||||
S: UsesInput + Debug + HasMetadata,
|
|
||||||
{
|
|
||||||
/// Get the number of usable cmps (all by default)
|
|
||||||
fn usable_count(&self) -> usize {
|
|
||||||
match &self.size {
|
|
||||||
None => self.cmp_map.as_ref().len(),
|
|
||||||
Some(o) => *o.as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmp_map(&self) -> &AFLppCmpMap {
|
|
||||||
self.cmp_map.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmp_map_mut(&mut self) -> &mut AFLppCmpMap {
|
|
||||||
self.cmp_map.as_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmp_observer_data(
|
|
||||||
&self,
|
|
||||||
) -> <AFLppCmpValuesMetadata as CmpObserverMetadata<'a, AFLppCmpMap>>::Data {
|
|
||||||
self.original
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add [`struct@CmpValuesMetadata`] to the State including the logged values.
|
|
||||||
/// This routine does a basic loop filtering because loop index cmps are not interesting.
|
|
||||||
fn add_cmpvalues_meta(&mut self, state: &mut S)
|
|
||||||
where
|
|
||||||
S: HasMetadata,
|
|
||||||
{
|
|
||||||
#[allow(clippy::option_if_let_else)] // we can't mutate state in a closure
|
|
||||||
let meta = if let Some(meta) = state.metadata_map_mut().get_mut::<AFLppCmpValuesMetadata>()
|
|
||||||
{
|
|
||||||
meta
|
|
||||||
} else {
|
|
||||||
state.add_metadata(AFLppCmpValuesMetadata::new());
|
|
||||||
state
|
|
||||||
.metadata_map_mut()
|
|
||||||
.get_mut::<AFLppCmpValuesMetadata>()
|
|
||||||
.unwrap()
|
|
||||||
};
|
|
||||||
|
|
||||||
if self.original {
|
|
||||||
// If this observer is for original input, then we have run the un-mutated input
|
|
||||||
// Clear orig_cmpvals
|
|
||||||
meta.orig_cmpvals.clear();
|
|
||||||
// Clear headers
|
|
||||||
meta.headers.clear();
|
|
||||||
} else {
|
|
||||||
// If this observer is for the mutated input
|
|
||||||
meta.new_cmpvals.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
let usable_count = self.usable_count();
|
|
||||||
let cmp_observer_data = self.cmp_observer_data();
|
|
||||||
|
|
||||||
meta.add_from(usable_count, self.cmp_map_mut(), cmp_observer_data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S> Observer<S> for AFLppCmpObserver<'a, S>
|
|
||||||
where
|
|
||||||
S: UsesInput + Debug + HasMetadata,
|
|
||||||
{
|
|
||||||
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
|
||||||
self.cmp_map.as_mut().reset()?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec(
|
|
||||||
&mut self,
|
|
||||||
state: &mut S,
|
|
||||||
_input: &S::Input,
|
|
||||||
_exit_kind: &ExitKind,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
if self.add_meta {
|
|
||||||
self.add_cmpvalues_meta(state);
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S> Named for AFLppCmpObserver<'a, S>
|
|
||||||
where
|
|
||||||
S: UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S> AFLppCmpObserver<'a, S>
|
|
||||||
where
|
|
||||||
S: UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
/// Creates a new [`AFLppCmpObserver`] with the given name and map.
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(name: &'static str, map: &'a mut AFLppCmpMap, add_meta: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
name: name.to_string(),
|
|
||||||
size: None,
|
|
||||||
cmp_map: OwnedRefMut::Ref(map),
|
|
||||||
add_meta,
|
|
||||||
original: false,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Setter for the flag if the executed input is a mutated one or the original one
|
|
||||||
pub fn set_original(&mut self, v: bool) {
|
|
||||||
self.original = v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new [`AFLppCmpObserver`] with the given name, map and reference to variable size.
|
|
||||||
#[must_use]
|
|
||||||
pub fn with_size(
|
|
||||||
name: &'static str,
|
|
||||||
map: &'a mut AFLppCmpMap,
|
|
||||||
add_meta: bool,
|
|
||||||
original: bool,
|
|
||||||
size: &'a mut usize,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
|
||||||
name: name.to_string(),
|
|
||||||
size: Some(OwnedRefMut::Ref(size)),
|
|
||||||
cmp_map: OwnedRefMut::Ref(map),
|
|
||||||
add_meta,
|
|
||||||
original,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A state metadata holding a list of values logged from comparisons. AFL++ RQ version.
|
/// A state metadata holding a list of values logged from comparisons. AFL++ RQ version.
|
||||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@ -613,15 +463,15 @@ where
|
|||||||
allow(clippy::unsafe_derive_deserialize)
|
allow(clippy::unsafe_derive_deserialize)
|
||||||
)] // for SerdeAny
|
)] // for SerdeAny
|
||||||
pub struct AFLppCmpValuesMetadata {
|
pub struct AFLppCmpValuesMetadata {
|
||||||
/// The first map of AFLppCmpVals retrieved by running the un-mutated input
|
/// The first map of AFLppCmpLogVals retrieved by running the un-mutated input
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub orig_cmpvals: HashMap<usize, Vec<CmpValues>>,
|
pub orig_cmpvals: HashMap<usize, Vec<CmpValues>>,
|
||||||
/// The second map of AFLppCmpVals retrieved by runnning the mutated input
|
/// The second map of AFLppCmpLogVals retrieved by runnning the mutated input
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub new_cmpvals: HashMap<usize, Vec<CmpValues>>,
|
pub new_cmpvals: HashMap<usize, Vec<CmpValues>>,
|
||||||
/// The list of logged idx and headers retrieved by runnning the mutated input
|
/// The list of logged idx and headers retrieved by runnning the mutated input
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub headers: Vec<(usize, AFLppCmpHeader)>,
|
pub headers: Vec<(usize, AFLppCmpLogHeader)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
libafl_bolts::impl_serdeany!(AFLppCmpValuesMetadata);
|
libafl_bolts::impl_serdeany!(AFLppCmpValuesMetadata);
|
||||||
@ -651,113 +501,11 @@ impl AFLppCmpValuesMetadata {
|
|||||||
|
|
||||||
/// Getter for `headers`
|
/// Getter for `headers`
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn headers(&self) -> &Vec<(usize, AFLppCmpHeader)> {
|
pub fn headers(&self) -> &Vec<(usize, AFLppCmpLogHeader)> {
|
||||||
&self.headers
|
&self.headers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> CmpObserverMetadata<'a, AFLppCmpMap> for AFLppCmpValuesMetadata {
|
|
||||||
type Data = bool;
|
|
||||||
|
|
||||||
fn new_metadata() -> Self {
|
|
||||||
Self::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn add_from(
|
|
||||||
&mut self,
|
|
||||||
usable_count: usize,
|
|
||||||
cmp_map: &mut AFLppCmpMap,
|
|
||||||
cmp_observer_data: Self::Data,
|
|
||||||
) {
|
|
||||||
let count = usable_count;
|
|
||||||
for i in 0..count {
|
|
||||||
let execs = cmp_map.usable_executions_for(i);
|
|
||||||
if execs > 0 {
|
|
||||||
if cmp_observer_data {
|
|
||||||
// Update header
|
|
||||||
self.headers.push((i, cmp_map.headers[i]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 {
|
|
||||||
if let Some(val) = cmp_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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let cmpmap_idx = i;
|
|
||||||
let mut cmp_values = Vec::new();
|
|
||||||
if cmp_observer_data {
|
|
||||||
// push into orig_cmpvals
|
|
||||||
// println!("Adding to orig_cmpvals");
|
|
||||||
for j in 0..execs {
|
|
||||||
if let Some(val) = cmp_map.values_of(i, j) {
|
|
||||||
cmp_values.push(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// println!("idx: {cmpmap_idx} cmp_values: {:#?}", cmp_values);
|
|
||||||
self.orig_cmpvals.insert(cmpmap_idx, cmp_values);
|
|
||||||
} else {
|
|
||||||
// push into new_cmpvals
|
|
||||||
// println!("Adding to new_cmpvals");
|
|
||||||
for j in 0..execs {
|
|
||||||
if let Some(val) = cmp_map.values_of(i, j) {
|
|
||||||
cmp_values.push(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// println!("idx: {cmpmap_idx} cmp_values: {:#?}", cmp_values);
|
|
||||||
self.new_cmpvals.insert(cmpmap_idx, cmp_values);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The AFL++ `CMP_MAP_W`
|
|
||||||
pub const AFL_CMP_MAP_W: usize = 65536;
|
|
||||||
/// The AFL++ `CMP_MAP_H`
|
|
||||||
pub const AFL_CMP_MAP_H: usize = 32;
|
|
||||||
/// The AFL++ `CMP_MAP_RTN_H`
|
|
||||||
pub const AFL_CMP_MAP_RTN_H: usize = AFL_CMP_MAP_H / 2;
|
|
||||||
|
|
||||||
/// The AFL++ `CMP_TYPE_INS`
|
|
||||||
pub const AFL_CMP_TYPE_INS: u32 = 1;
|
|
||||||
/// The AFL++ `CMP_TYPE_RTN`
|
|
||||||
pub const AFL_CMP_TYPE_RTN: u32 = 2;
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, BitfieldStruct)]
|
#[derive(Debug, Copy, Clone, BitfieldStruct)]
|
||||||
#[repr(C, packed)]
|
#[repr(C, packed)]
|
||||||
/// Comparison header, used to describe a set of comparison values efficiently.
|
/// Comparison header, used to describe a set of comparison values efficiently.
|
||||||
@ -772,7 +520,8 @@ pub const AFL_CMP_TYPE_RTN: u32 = 2;
|
|||||||
/// - attribute: OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform
|
/// - attribute: OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform
|
||||||
/// - overflow: Whether the comparison overflows
|
/// - overflow: Whether the comparison overflows
|
||||||
/// - reserved: Reserved for future use
|
/// - reserved: Reserved for future use
|
||||||
pub struct AFLppCmpHeader {
|
pub struct AFLppCmpLogHeader {
|
||||||
|
/// The header values
|
||||||
#[bitfield(name = "hits", ty = "u32", bits = "0..=23")]
|
#[bitfield(name = "hits", ty = "u32", bits = "0..=23")]
|
||||||
#[bitfield(name = "id", ty = "u32", bits = "24..=47")]
|
#[bitfield(name = "id", ty = "u32", bits = "24..=47")]
|
||||||
#[bitfield(name = "shape", ty = "u32", bits = "48..=52")]
|
#[bitfield(name = "shape", ty = "u32", bits = "48..=52")]
|
||||||
@ -780,343 +529,5 @@ pub struct AFLppCmpHeader {
|
|||||||
#[bitfield(name = "attribute", ty = "u32", bits = "55..=58")]
|
#[bitfield(name = "attribute", ty = "u32", bits = "55..=58")]
|
||||||
#[bitfield(name = "overflow", ty = "u32", bits = "59..=59")]
|
#[bitfield(name = "overflow", ty = "u32", bits = "59..=59")]
|
||||||
#[bitfield(name = "reserved", ty = "u32", bits = "60..=63")]
|
#[bitfield(name = "reserved", ty = "u32", bits = "60..=63")]
|
||||||
data: [u8; 8],
|
pub data: [u8; 8],
|
||||||
}
|
|
||||||
|
|
||||||
/// The AFL++ `cmp_operands` struct
|
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
/// Comparison operands, represented as either two (left and right of comparison) u64 values or
|
|
||||||
/// two (left and right of comparison) u128 values, split into two u64 values. If the left and
|
|
||||||
/// right values are smaller than u64, they can be sign or zero extended to 64 bits, as the actual
|
|
||||||
/// comparison size is determined by the `hits` field of the associated `AFLppCmpHeader`.
|
|
||||||
pub struct AFLppCmpOperands {
|
|
||||||
v0: u64,
|
|
||||||
v1: u64,
|
|
||||||
v0_128: u64,
|
|
||||||
v1_128: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AFLppCmpOperands {
|
|
||||||
#[must_use]
|
|
||||||
/// Create new `AFLppCmpOperands`
|
|
||||||
pub fn new(v0: u64, v1: u64) -> Self {
|
|
||||||
Self {
|
|
||||||
v0,
|
|
||||||
v1,
|
|
||||||
v0_128: 0,
|
|
||||||
v1_128: 0,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// Create new `AFLppCmpOperands` with 128-bit comparison values
|
|
||||||
pub fn new_128bit(v0: u128, v1: u128) -> Self {
|
|
||||||
let v0_128 = (v0 >> 64) as u64;
|
|
||||||
let v0 = v0 as u64;
|
|
||||||
let v1_128 = (v1 >> 64) as u64;
|
|
||||||
let v1 = v1 as u64;
|
|
||||||
|
|
||||||
Self {
|
|
||||||
v0,
|
|
||||||
v1,
|
|
||||||
v0_128,
|
|
||||||
v1_128,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// 64bit first cmp operand
|
|
||||||
pub fn v0(&self) -> u64 {
|
|
||||||
self.v0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// 64bit second cmp operand
|
|
||||||
pub fn v1(&self) -> u64 {
|
|
||||||
self.v1
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// 128bit first cmp operand
|
|
||||||
pub fn v0_128(&self) -> u64 {
|
|
||||||
self.v0_128
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// 128bit second cmp operand
|
|
||||||
pub fn v1_128(&self) -> u64 {
|
|
||||||
self.v1_128
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the v0 (left) side of the comparison
|
|
||||||
pub fn set_v0(&mut self, v0: u64) {
|
|
||||||
self.v0 = v0;
|
|
||||||
self.v0_128 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the v1 (right) side of the comparison
|
|
||||||
pub fn set_v1(&mut self, v1: u64) {
|
|
||||||
self.v1 = v1;
|
|
||||||
self.v1_128 = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the v0 (left) side of the comparison from a 128-bit comparison value
|
|
||||||
pub fn set_v0_128(&mut self, v0: u128) {
|
|
||||||
self.v0_128 = (v0 >> 64) as u64;
|
|
||||||
self.v0 = v0 as u64;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the v1 (right) side of the comparison from a 128-bit comparison value
|
|
||||||
pub fn set_v1_128(&mut self, v1: u128) {
|
|
||||||
self.v1_128 = (v1 >> 64) as u64;
|
|
||||||
self.v1 = v1 as u64;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The AFL++ `cmpfn_operands` struct
|
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
/// Comparison function operands, like for strcmp/memcmp, represented as two byte arrays.
|
|
||||||
pub struct AFLppCmpFnOperands {
|
|
||||||
v0: [u8; 31],
|
|
||||||
v0_len: u8,
|
|
||||||
v1: [u8; 31],
|
|
||||||
v1_len: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AFLppCmpFnOperands {
|
|
||||||
#[must_use]
|
|
||||||
/// Create a new AFL++ function operands comparison values from two byte slices
|
|
||||||
pub fn new(v0: &[u8], v1: &[u8]) -> Self {
|
|
||||||
let v0_len = v0.len() as u8;
|
|
||||||
let v1_len = v1.len() as u8;
|
|
||||||
|
|
||||||
let mut v0_arr = [0; 31];
|
|
||||||
let mut v1_arr = [0; 31];
|
|
||||||
|
|
||||||
v0_arr.copy_from_slice(v0);
|
|
||||||
v1_arr.copy_from_slice(v1);
|
|
||||||
|
|
||||||
Self {
|
|
||||||
v0: v0_arr,
|
|
||||||
v0_len,
|
|
||||||
v1: v1_arr,
|
|
||||||
v1_len,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// first rtn operand
|
|
||||||
pub fn v0(&self) -> &[u8; 31] {
|
|
||||||
&self.v0
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// second rtn operand
|
|
||||||
pub fn v0_len(&self) -> u8 {
|
|
||||||
self.v0_len
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// first rtn operand len
|
|
||||||
pub fn v1(&self) -> &[u8; 31] {
|
|
||||||
&self.v1
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// second rtn operand len
|
|
||||||
pub fn v1_len(&self) -> u8 {
|
|
||||||
self.v1_len
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the v0 (left) side of the comparison
|
|
||||||
pub fn set_v0(&mut self, v0: &[u8]) {
|
|
||||||
self.v0_len = v0.len() as u8;
|
|
||||||
self.v0.copy_from_slice(v0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the v1 (right) side of the comparison
|
|
||||||
pub fn set_v1(&mut self, v1: &[u8]) {
|
|
||||||
self.v1_len = v1.len() as u8;
|
|
||||||
self.v1.copy_from_slice(v1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
/// Comparison values
|
|
||||||
pub union AFLppCmpVals {
|
|
||||||
operands: [[AFLppCmpOperands; AFL_CMP_MAP_H]; AFL_CMP_MAP_W],
|
|
||||||
fn_operands: [[AFLppCmpFnOperands; AFL_CMP_MAP_RTN_H]; AFL_CMP_MAP_W],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for AFLppCmpVals {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
f.debug_struct("AFLppCmpVals").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AFLppCmpVals {
|
|
||||||
#[must_use]
|
|
||||||
/// Reference comparison values as comparison operands
|
|
||||||
pub fn operands(&self) -> &[[AFLppCmpOperands; AFL_CMP_MAP_H]; AFL_CMP_MAP_W] {
|
|
||||||
unsafe { &self.operands }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// Mutably reference comparison values as comparison operands
|
|
||||||
pub fn operands_mut(&mut self) -> &mut [[AFLppCmpOperands; AFL_CMP_MAP_H]; AFL_CMP_MAP_W] {
|
|
||||||
unsafe { &mut self.operands }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// Reference comparison values as comparison function operands
|
|
||||||
pub fn fn_operands(&self) -> &[[AFLppCmpFnOperands; AFL_CMP_MAP_RTN_H]; AFL_CMP_MAP_W] {
|
|
||||||
unsafe { &self.fn_operands }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// Mutably reference comparison values as comparison function operands
|
|
||||||
pub fn fn_operands_mut(
|
|
||||||
&mut self,
|
|
||||||
) -> &mut [[AFLppCmpFnOperands; AFL_CMP_MAP_RTN_H]; AFL_CMP_MAP_W] {
|
|
||||||
unsafe { &mut self.fn_operands }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
#[repr(C, packed)]
|
|
||||||
/// Comparison map compatible with AFL++ cmplog instrumentation
|
|
||||||
pub struct AFLppCmpMap {
|
|
||||||
headers: [AFLppCmpHeader; AFL_CMP_MAP_W],
|
|
||||||
vals: AFLppCmpVals,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AFLppCmpMap {
|
|
||||||
#[must_use]
|
|
||||||
/// Instantiate a new boxed zeroed `AFLppCmpMap`. This should be used to create a new
|
|
||||||
/// map, because it is so large it cannot be allocated on the stack with default
|
|
||||||
/// runtime configuration.
|
|
||||||
pub fn boxed() -> Box<Self> {
|
|
||||||
unsafe { Box::from_raw(alloc_zeroed(Layout::new::<AFLppCmpMap>()) as *mut AFLppCmpMap) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// Reference the headers for the map
|
|
||||||
pub fn headers(&self) -> &[AFLppCmpHeader] {
|
|
||||||
&self.headers
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// Mutably reference the headers for the map
|
|
||||||
pub fn headers_mut(&mut self) -> &mut [AFLppCmpHeader] {
|
|
||||||
&mut self.headers
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// Reference the values for the map
|
|
||||||
pub fn values(&self) -> &AFLppCmpVals {
|
|
||||||
&self.vals
|
|
||||||
}
|
|
||||||
|
|
||||||
#[must_use]
|
|
||||||
/// Mutably reference the headers for the map
|
|
||||||
pub fn values_mut(&mut self) -> &mut AFLppCmpVals {
|
|
||||||
&mut self.vals
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Serialize for AFLppCmpMap {
|
|
||||||
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
||||||
where
|
|
||||||
S: Serializer,
|
|
||||||
{
|
|
||||||
let slice = unsafe {
|
|
||||||
core::slice::from_raw_parts(
|
|
||||||
(self as *const Self) as *const u8,
|
|
||||||
core::mem::size_of::<Self>(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
serializer.serialize_bytes(slice)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'de> Deserialize<'de> for AFLppCmpMap {
|
|
||||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
||||||
where
|
|
||||||
D: Deserializer<'de>,
|
|
||||||
{
|
|
||||||
let bytes = Vec::<u8>::deserialize(deserializer)?;
|
|
||||||
let map: Self = unsafe { core::ptr::read(bytes.as_ptr() as *const _) };
|
|
||||||
Ok(map)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CmpMap for AFLppCmpMap {
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
AFL_CMP_MAP_W
|
|
||||||
}
|
|
||||||
|
|
||||||
fn executions_for(&self, idx: usize) -> usize {
|
|
||||||
self.headers[idx].hits() as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usable_executions_for(&self, idx: usize) -> usize {
|
|
||||||
if self.headers[idx]._type() == AFL_CMP_TYPE_INS {
|
|
||||||
if self.executions_for(idx) < AFL_CMP_MAP_H {
|
|
||||||
self.executions_for(idx)
|
|
||||||
} else {
|
|
||||||
AFL_CMP_MAP_H
|
|
||||||
}
|
|
||||||
} else if self.executions_for(idx) < AFL_CMP_MAP_RTN_H {
|
|
||||||
self.executions_for(idx)
|
|
||||||
} else {
|
|
||||||
AFL_CMP_MAP_RTN_H
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> {
|
|
||||||
if self.headers[idx]._type() == AFL_CMP_TYPE_INS {
|
|
||||||
unsafe {
|
|
||||||
match self.headers[idx].shape() {
|
|
||||||
0 => Some(CmpValues::U8((
|
|
||||||
self.vals.operands[idx][execution].v0 as u8,
|
|
||||||
self.vals.operands[idx][execution].v1 as u8,
|
|
||||||
))),
|
|
||||||
1 => Some(CmpValues::U16((
|
|
||||||
self.vals.operands[idx][execution].v0 as u16,
|
|
||||||
self.vals.operands[idx][execution].v1 as u16,
|
|
||||||
))),
|
|
||||||
3 => Some(CmpValues::U32((
|
|
||||||
self.vals.operands[idx][execution].v0 as u32,
|
|
||||||
self.vals.operands[idx][execution].v1 as u32,
|
|
||||||
))),
|
|
||||||
7 => Some(CmpValues::U64((
|
|
||||||
self.vals.operands[idx][execution].v0,
|
|
||||||
self.vals.operands[idx][execution].v1,
|
|
||||||
))),
|
|
||||||
// TODO handle 128 bits cmps
|
|
||||||
// other => panic!("Invalid CmpLog shape {}", other),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
let v0_len = self.vals.fn_operands[idx][execution].v0_len & (0x80 - 1);
|
|
||||||
let v1_len = self.vals.fn_operands[idx][execution].v1_len & (0x80 - 1);
|
|
||||||
Some(CmpValues::Bytes((
|
|
||||||
self.vals.fn_operands[idx][execution].v0[..(v0_len as usize)].to_vec(),
|
|
||||||
self.vals.fn_operands[idx][execution].v1[..(v1_len as usize)].to_vec(),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) -> Result<(), Error> {
|
|
||||||
// For performance, we reset just the headers
|
|
||||||
self.headers.fill(AFLppCmpHeader { data: [0; 8] });
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,17 @@
|
|||||||
//! The tracing stage can trace the target and enrich a testcase with metadata, for example for `CmpLog`.
|
//! The tracing stage can trace the target and enrich a testcase with metadata, for example for `CmpLog`.
|
||||||
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
use core::{fmt::Debug, marker::PhantomData};
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
use libafl_bolts::tuples::MatchName;
|
|
||||||
|
|
||||||
#[cfg(feature = "introspection")]
|
#[cfg(feature = "introspection")]
|
||||||
use crate::monitors::PerfFeature;
|
use crate::monitors::PerfFeature;
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, CorpusId},
|
corpus::{Corpus, CorpusId},
|
||||||
executors::{Executor, HasObservers, ShadowExecutor},
|
executors::{Executor, HasObservers, ShadowExecutor},
|
||||||
inputs::{BytesInput, UsesInput},
|
|
||||||
mark_feature_time,
|
mark_feature_time,
|
||||||
observers::{AFLppCmpObserver, ObserversTuple},
|
observers::ObserversTuple,
|
||||||
stages::{colorization::TaintMetadata, Stage},
|
stages::Stage,
|
||||||
start_timer,
|
start_timer,
|
||||||
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, State, UsesState},
|
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, State, UsesState},
|
||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -99,144 +95,6 @@ impl<EM, TE, Z> TracingStage<EM, TE, Z> {
|
|||||||
&mut self.tracer_executor
|
&mut self.tracer_executor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Trace with tainted input
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct AFLppCmplogTracingStage<EM, TE, Z> {
|
|
||||||
tracer_executor: TE,
|
|
||||||
cmplog_observer_name: Option<String>,
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
phantom: PhantomData<(EM, TE, Z)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<EM, TE, Z> UsesState for AFLppCmplogTracingStage<EM, TE, Z>
|
|
||||||
where
|
|
||||||
TE: UsesState,
|
|
||||||
{
|
|
||||||
type State = TE::State;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, EM, TE, Z> Stage<E, EM, Z> for AFLppCmplogTracingStage<EM, TE, Z>
|
|
||||||
where
|
|
||||||
E: UsesState<State = TE::State>,
|
|
||||||
TE: Executor<EM, Z> + HasObservers,
|
|
||||||
TE::State: HasClientPerfMonitor
|
|
||||||
+ HasExecutions
|
|
||||||
+ HasCorpus
|
|
||||||
+ HasMetadata
|
|
||||||
+ UsesInput<Input = BytesInput>,
|
|
||||||
EM: UsesState<State = TE::State>,
|
|
||||||
Z: UsesState<State = TE::State>,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
fn perform(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut Z,
|
|
||||||
_executor: &mut E,
|
|
||||||
state: &mut TE::State,
|
|
||||||
manager: &mut EM,
|
|
||||||
corpus_idx: CorpusId,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
// First run with the un-mutated input
|
|
||||||
|
|
||||||
let unmutated_input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
|
||||||
|
|
||||||
if let Some(name) = &self.cmplog_observer_name {
|
|
||||||
if let Some(ob) = self
|
|
||||||
.tracer_executor
|
|
||||||
.observers_mut()
|
|
||||||
.match_name_mut::<AFLppCmpObserver<TE::State>>(name)
|
|
||||||
{
|
|
||||||
// This is not the original input,
|
|
||||||
// Set it to false
|
|
||||||
ob.set_original(true);
|
|
||||||
}
|
|
||||||
// I can't think of any use of this stage if you don't use AFLppCmpObserver
|
|
||||||
// but do nothing ofcourse
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tracer_executor
|
|
||||||
.observers_mut()
|
|
||||||
.pre_exec_all(state, &unmutated_input)?;
|
|
||||||
|
|
||||||
let exit_kind =
|
|
||||||
self.tracer_executor
|
|
||||||
.run_target(fuzzer, state, manager, &unmutated_input)?;
|
|
||||||
|
|
||||||
*state.executions_mut() += 1;
|
|
||||||
|
|
||||||
self.tracer_executor
|
|
||||||
.observers_mut()
|
|
||||||
.post_exec_all(state, &unmutated_input, &exit_kind)?;
|
|
||||||
|
|
||||||
// Second run with the mutated input
|
|
||||||
let mutated_input = match state.metadata_map().get::<TaintMetadata>() {
|
|
||||||
Some(meta) => BytesInput::from(meta.input_vec().as_ref()),
|
|
||||||
None => return Err(Error::unknown("No metadata found")),
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(name) = &self.cmplog_observer_name {
|
|
||||||
if let Some(ob) = self
|
|
||||||
.tracer_executor
|
|
||||||
.observers_mut()
|
|
||||||
.match_name_mut::<AFLppCmpObserver<TE::State>>(name)
|
|
||||||
{
|
|
||||||
// This is not the original input,
|
|
||||||
// Set it to false
|
|
||||||
ob.set_original(false);
|
|
||||||
}
|
|
||||||
// I can't think of any use of this stage if you don't use AFLppCmpObserver
|
|
||||||
// but do nothing ofcourse
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tracer_executor
|
|
||||||
.observers_mut()
|
|
||||||
.pre_exec_all(state, &mutated_input)?;
|
|
||||||
|
|
||||||
let exit_kind = self
|
|
||||||
.tracer_executor
|
|
||||||
.run_target(fuzzer, state, manager, &mutated_input)?;
|
|
||||||
|
|
||||||
*state.executions_mut() += 1;
|
|
||||||
|
|
||||||
self.tracer_executor
|
|
||||||
.observers_mut()
|
|
||||||
.post_exec_all(state, &mutated_input, &exit_kind)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<EM, TE, Z> AFLppCmplogTracingStage<EM, TE, Z> {
|
|
||||||
/// Creates a new default stage
|
|
||||||
pub fn new(tracer_executor: TE) -> Self {
|
|
||||||
Self {
|
|
||||||
cmplog_observer_name: None,
|
|
||||||
tracer_executor,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// With cmplog observer
|
|
||||||
pub fn with_cmplog_observer_name(tracer_executor: TE, name: &'static str) -> Self {
|
|
||||||
Self {
|
|
||||||
cmplog_observer_name: Some(name.to_string()),
|
|
||||||
tracer_executor,
|
|
||||||
phantom: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the underlying tracer executor
|
|
||||||
pub fn executor(&self) -> &TE {
|
|
||||||
&self.tracer_executor
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Gets the underlying tracer executor (mut)
|
|
||||||
pub fn executor_mut(&mut self) -> &mut TE {
|
|
||||||
&mut self.tracer_executor
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A stage that runs the shadow executor using also the shadow observers
|
/// A stage that runs the shadow executor using also the shadow observers
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct ShadowTracingStage<E, EM, SOT, Z> {
|
pub struct ShadowTracingStage<E, EM, SOT, Z> {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use hashbrown::HashMap;
|
use hashbrown::HashMap;
|
||||||
use libafl::{inputs::UsesInput, state::HasMetadata};
|
use libafl::{inputs::UsesInput, state::HasMetadata};
|
||||||
pub use libafl_targets::{
|
pub use libafl_targets::{
|
||||||
cmplog::__libafl_targets_cmplog_instructions, CmpLogMap, CmpLogObserver, CMPLOG_MAP_H,
|
cmps::__libafl_targets_cmplog_instructions, CmpLogMap, CmpLogObserver, CMPLOG_MAP_H,
|
||||||
CMPLOG_MAP_PTR, CMPLOG_MAP_SIZE, CMPLOG_MAP_W,
|
CMPLOG_MAP_PTR, CMPLOG_MAP_SIZE, CMPLOG_MAP_W,
|
||||||
};
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -18,6 +18,12 @@ fn main() {
|
|||||||
let cmp_map_size: usize = option_env!("LIBAFL_CMP_MAP_SIZE")
|
let cmp_map_size: usize = option_env!("LIBAFL_CMP_MAP_SIZE")
|
||||||
.map_or(Ok(65536), str::parse)
|
.map_or(Ok(65536), str::parse)
|
||||||
.expect("Could not parse LIBAFL_CMP_MAP_SIZE");
|
.expect("Could not parse LIBAFL_CMP_MAP_SIZE");
|
||||||
|
let aflpp_cmplog_map_w: usize = option_env!("LIBAFL_AFLPP_CMPLOG_MAP_W")
|
||||||
|
.map_or(Ok(65536), str::parse)
|
||||||
|
.expect("Could not parse LIBAFL_AFLPP_CMPLOG_MAP_W");
|
||||||
|
let aflpp_cmplog_map_h: usize = option_env!("LIBAFL_AFLPP_CMPLOG_MAP_W")
|
||||||
|
.map_or(Ok(32), str::parse)
|
||||||
|
.expect("Could not parse LIBAFL_AFLPP_CMPLOG_MAP_W");
|
||||||
let cmplog_map_w: usize = option_env!("LIBAFL_CMPLOG_MAP_W")
|
let cmplog_map_w: usize = option_env!("LIBAFL_CMPLOG_MAP_W")
|
||||||
.map_or(Ok(65536), str::parse)
|
.map_or(Ok(65536), str::parse)
|
||||||
.expect("Could not parse LIBAFL_CMPLOG_MAP_W");
|
.expect("Could not parse LIBAFL_CMPLOG_MAP_W");
|
||||||
@ -36,6 +42,10 @@ fn main() {
|
|||||||
pub const EDGES_MAP_SIZE: usize = {edges_map_size};
|
pub const EDGES_MAP_SIZE: usize = {edges_map_size};
|
||||||
/// The size of the cmps map
|
/// The size of the cmps map
|
||||||
pub const CMP_MAP_SIZE: usize = {cmp_map_size};
|
pub const CMP_MAP_SIZE: usize = {cmp_map_size};
|
||||||
|
/// The width of the aflpp cmplog map
|
||||||
|
pub const AFLPP_CMPLOG_MAP_W: usize = {aflpp_cmplog_map_w};
|
||||||
|
/// The height of the aflpp cmplog map
|
||||||
|
pub const AFLPP_CMPLOG_MAP_H: usize = {aflpp_cmplog_map_h};
|
||||||
/// The width of the `CmpLog` map
|
/// The width of the `CmpLog` map
|
||||||
pub const CMPLOG_MAP_W: usize = {cmplog_map_w};
|
pub const CMPLOG_MAP_W: usize = {cmplog_map_w};
|
||||||
/// The height of the `CmpLog` map
|
/// The height of the `CmpLog` map
|
||||||
@ -48,6 +58,8 @@ fn main() {
|
|||||||
|
|
||||||
println!("cargo:rerun-if-env-changed=LIBAFL_EDGES_MAP_SIZE");
|
println!("cargo:rerun-if-env-changed=LIBAFL_EDGES_MAP_SIZE");
|
||||||
println!("cargo:rerun-if-env-changed=LIBAFL_CMP_MAP_SIZE");
|
println!("cargo:rerun-if-env-changed=LIBAFL_CMP_MAP_SIZE");
|
||||||
|
println!("cargo:rerun-if-env-changed=LIBAFL_AFLPP_CMPLOG_MAP_W");
|
||||||
|
println!("cargo:rerun-if-env-changed=LIBAFL_AFLPP_CMPLOG_MAP_H");
|
||||||
println!("cargo:rerun-if-env-changed=LIBAFL_CMPLOG_MAP_W");
|
println!("cargo:rerun-if-env-changed=LIBAFL_CMPLOG_MAP_W");
|
||||||
println!("cargo:rerun-if-env-changed=LIBAFL_CMPLOG_MAP_H");
|
println!("cargo:rerun-if-env-changed=LIBAFL_CMPLOG_MAP_H");
|
||||||
println!("cargo:rerun-if-env-changed=LIBAFL_ACCOUNTING_MAP_SIZE");
|
println!("cargo:rerun-if-env-changed=LIBAFL_ACCOUNTING_MAP_SIZE");
|
||||||
@ -80,6 +92,14 @@ fn main() {
|
|||||||
|
|
||||||
sancov_cmp
|
sancov_cmp
|
||||||
.define("CMP_MAP_SIZE", Some(&*format!("{cmp_map_size}")))
|
.define("CMP_MAP_SIZE", Some(&*format!("{cmp_map_size}")))
|
||||||
|
.define(
|
||||||
|
"AFLPP_CMPLOG_MAP_W",
|
||||||
|
Some(&*format!("{aflpp_cmplog_map_w}")),
|
||||||
|
)
|
||||||
|
.define(
|
||||||
|
"AFLPP_CMPLOG_MAP_H",
|
||||||
|
Some(&*format!("{aflpp_cmplog_map_h}")),
|
||||||
|
)
|
||||||
.define("CMPLOG_MAP_W", Some(&*format!("{cmplog_map_w}")))
|
.define("CMPLOG_MAP_W", Some(&*format!("{cmplog_map_w}")))
|
||||||
.define("CMPLOG_MAP_H", Some(&*format!("{cmplog_map_h}")))
|
.define("CMPLOG_MAP_H", Some(&*format!("{cmplog_map_h}")))
|
||||||
.file(src_dir.join("sancov_cmp.c"))
|
.file(src_dir.join("sancov_cmp.c"))
|
||||||
@ -158,6 +178,15 @@ fn main() {
|
|||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
.flag("-Wno-pointer-sign") // UNIX ONLY FLAGS
|
.flag("-Wno-pointer-sign") // UNIX ONLY FLAGS
|
||||||
.flag("-Wno-sign-compare")
|
.flag("-Wno-sign-compare")
|
||||||
|
.define("CMP_MAP_SIZE", Some(&*format!("{cmp_map_size}")))
|
||||||
|
.define(
|
||||||
|
"AFLPP_CMPLOG_MAP_W",
|
||||||
|
Some(&*format!("{aflpp_cmplog_map_w}")),
|
||||||
|
)
|
||||||
|
.define(
|
||||||
|
"AFLPP_CMPLOG_MAP_H",
|
||||||
|
Some(&*format!("{aflpp_cmplog_map_h}")),
|
||||||
|
)
|
||||||
.define("CMPLOG_MAP_W", Some(&*format!("{cmplog_map_w}")))
|
.define("CMPLOG_MAP_W", Some(&*format!("{cmplog_map_w}")))
|
||||||
.define("CMPLOG_MAP_H", Some(&*format!("{cmplog_map_h}")))
|
.define("CMPLOG_MAP_H", Some(&*format!("{cmplog_map_h}")))
|
||||||
.file(src_dir.join("cmplog.c"))
|
.file(src_dir.join("cmplog.c"))
|
||||||
@ -167,6 +196,15 @@ fn main() {
|
|||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
{
|
{
|
||||||
cc::Build::new()
|
cc::Build::new()
|
||||||
|
.define("CMP_MAP_SIZE", Some(&*format!("{cmp_map_size}")))
|
||||||
|
.define(
|
||||||
|
"AFLPP_CMPLOG_MAP_W",
|
||||||
|
Some(&*format!("{aflpp_cmplog_map_w}")),
|
||||||
|
)
|
||||||
|
.define(
|
||||||
|
"AFLPP_CMPLOG_MAP_H",
|
||||||
|
Some(&*format!("{aflpp_cmplog_map_h}")),
|
||||||
|
)
|
||||||
.define("CMPLOG_MAP_W", Some(&*format!("{cmplog_map_w}")))
|
.define("CMPLOG_MAP_W", Some(&*format!("{cmplog_map_w}")))
|
||||||
.define("CMPLOG_MAP_H", Some(&*format!("{cmplog_map_h}")))
|
.define("CMPLOG_MAP_H", Some(&*format!("{cmplog_map_h}")))
|
||||||
.file(src_dir.join("cmplog.c"))
|
.file(src_dir.join("cmplog.c"))
|
||||||
|
@ -40,11 +40,27 @@ __attribute__((weak)) void *__asan_region_is_poisoned(const void *beg,
|
|||||||
CmpLogMap *libafl_cmplog_map_ptr = &libafl_cmplog_map;
|
CmpLogMap *libafl_cmplog_map_ptr = &libafl_cmplog_map;
|
||||||
|
|
||||||
void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape,
|
void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape,
|
||||||
uint64_t arg1, uint64_t arg2) {
|
uint64_t arg1, uint64_t arg2) {
|
||||||
STATIC_ASSERT(sizeof(libafl_cmplog_map_ptr->vals.operands) ==
|
if (!libafl_cmplog_enabled) { return; }
|
||||||
sizeof(libafl_cmplog_map_ptr->vals.routines));
|
libafl_cmplog_enabled = false;
|
||||||
|
|
||||||
__libafl_targets_cmplog(k, shape, arg1, arg2);
|
uint16_t hits;
|
||||||
|
if (libafl_cmplog_map_ptr->headers[k].kind != CMPLOG_KIND_INS) {
|
||||||
|
libafl_cmplog_map_ptr->headers[k].kind = CMPLOG_KIND_INS;
|
||||||
|
libafl_cmplog_map_ptr->headers[k].hits = 1;
|
||||||
|
libafl_cmplog_map_ptr->headers[k].shape = shape;
|
||||||
|
hits = 0;
|
||||||
|
} else {
|
||||||
|
hits = libafl_cmplog_map_ptr->headers[k].hits++;
|
||||||
|
if (libafl_cmplog_map_ptr->headers[k].shape < shape) {
|
||||||
|
libafl_cmplog_map_ptr->headers[k].shape = shape;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
hits &= CMPLOG_MAP_H - 1;
|
||||||
|
libafl_cmplog_map_ptr->vals.operands[k][hits].v0 = arg1;
|
||||||
|
libafl_cmplog_map_ptr->vals.operands[k][hits].v1 = arg2;
|
||||||
|
libafl_cmplog_enabled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// POSIX shenanigan to see if an area is mapped.
|
// POSIX shenanigan to see if an area is mapped.
|
||||||
|
@ -57,28 +57,4 @@ void __libafl_targets_cmplog_routines(uintptr_t k, const uint8_t *ptr1,
|
|||||||
void __libafl_targets_cmplog_routines_len(uintptr_t k, const uint8_t *ptr1,
|
void __libafl_targets_cmplog_routines_len(uintptr_t k, const uint8_t *ptr1,
|
||||||
const uint8_t *ptr2, size_t len);
|
const uint8_t *ptr2, size_t len);
|
||||||
|
|
||||||
static inline void __libafl_targets_cmplog(uintptr_t k, uint8_t shape,
|
|
||||||
uint64_t arg1, uint64_t arg2) {
|
|
||||||
if (!libafl_cmplog_enabled) { return; }
|
|
||||||
libafl_cmplog_enabled = false;
|
|
||||||
|
|
||||||
uint16_t hits;
|
|
||||||
if (libafl_cmplog_map_ptr->headers[k].kind != CMPLOG_KIND_INS) {
|
|
||||||
libafl_cmplog_map_ptr->headers[k].kind = CMPLOG_KIND_INS;
|
|
||||||
libafl_cmplog_map_ptr->headers[k].hits = 1;
|
|
||||||
libafl_cmplog_map_ptr->headers[k].shape = shape;
|
|
||||||
hits = 0;
|
|
||||||
} else {
|
|
||||||
hits = libafl_cmplog_map_ptr->headers[k].hits++;
|
|
||||||
if (libafl_cmplog_map_ptr->headers[k].shape < shape) {
|
|
||||||
libafl_cmplog_map_ptr->headers[k].shape = shape;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
hits &= CMPLOG_MAP_H - 1;
|
|
||||||
libafl_cmplog_map_ptr->vals.operands[k][hits].v0 = arg1;
|
|
||||||
libafl_cmplog_map_ptr->vals.operands[k][hits].v1 = arg2;
|
|
||||||
libafl_cmplog_enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -1,272 +0,0 @@
|
|||||||
//! `CmpLog` logs and reports back values touched during fuzzing.
|
|
||||||
//! The values will then be used in subsequent mutations.
|
|
||||||
//!
|
|
||||||
|
|
||||||
use alloc::string::{String, ToString};
|
|
||||||
use core::fmt::{self, Debug, Formatter};
|
|
||||||
|
|
||||||
use libafl::{
|
|
||||||
executors::ExitKind,
|
|
||||||
inputs::UsesInput,
|
|
||||||
observers::{cmp::CmpValuesMetadata, CmpMap, CmpObserver, CmpValues, Observer},
|
|
||||||
state::HasMetadata,
|
|
||||||
Error,
|
|
||||||
};
|
|
||||||
use libafl_bolts::{ownedref::OwnedMutPtr, Named};
|
|
||||||
|
|
||||||
use crate::{CMPLOG_MAP_H, CMPLOG_MAP_W};
|
|
||||||
|
|
||||||
/// The `CmpLog` map size
|
|
||||||
pub const CMPLOG_MAP_SIZE: usize = CMPLOG_MAP_W * CMPLOG_MAP_H;
|
|
||||||
|
|
||||||
/// The size of a logged routine argument in bytes
|
|
||||||
pub const CMPLOG_RTN_LEN: usize = 32;
|
|
||||||
|
|
||||||
/// The hight of a cmplog routine map
|
|
||||||
pub const CMPLOG_MAP_RTN_H: usize = (CMPLOG_MAP_H * core::mem::size_of::<CmpLogInstruction>())
|
|
||||||
/ core::mem::size_of::<CmpLogRoutine>();
|
|
||||||
|
|
||||||
/// `CmpLog` instruction kind
|
|
||||||
pub const CMPLOG_KIND_INS: u8 = 0;
|
|
||||||
/// `CmpLog` routine kind
|
|
||||||
pub const CMPLOG_KIND_RTN: u8 = 1;
|
|
||||||
|
|
||||||
// void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2)
|
|
||||||
extern "C" {
|
|
||||||
/// Logs an instruction for feedback during fuzzing
|
|
||||||
pub fn __libafl_targets_cmplog_instructions(k: usize, shape: u8, arg1: u64, arg2: u64);
|
|
||||||
|
|
||||||
/// Pointer to the `CmpLog` map
|
|
||||||
pub static mut libafl_cmplog_map_ptr: *mut CmpLogMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub use libafl_cmplog_map_ptr as CMPLOG_MAP_PTR;
|
|
||||||
|
|
||||||
/// The header for `CmpLog` hits.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
|
||||||
pub struct CmpLogHeader {
|
|
||||||
hits: u16,
|
|
||||||
shape: u8,
|
|
||||||
kind: u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The operands logged during `CmpLog`.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
|
||||||
pub struct CmpLogInstruction(u64, u64);
|
|
||||||
|
|
||||||
/// The routine arguments logged during `CmpLog`.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Default, Debug, Clone, Copy)]
|
|
||||||
pub struct CmpLogRoutine([u8; CMPLOG_RTN_LEN], [u8; CMPLOG_RTN_LEN]);
|
|
||||||
|
|
||||||
/// Union of cmplog operands and routines
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Clone, Copy)]
|
|
||||||
pub union CmpLogVals {
|
|
||||||
operands: [[CmpLogInstruction; CMPLOG_MAP_H]; CMPLOG_MAP_W],
|
|
||||||
routines: [[CmpLogRoutine; CMPLOG_MAP_RTN_H]; CMPLOG_MAP_W],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for CmpLogVals {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
|
||||||
f.debug_struct("CmpLogVals").finish_non_exhaustive()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A struct containing the `CmpLog` metadata for a `LibAFL` run.
|
|
||||||
#[repr(C)]
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
|
||||||
pub struct CmpLogMap {
|
|
||||||
headers: [CmpLogHeader; CMPLOG_MAP_W],
|
|
||||||
vals: CmpLogVals,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for CmpLogMap {
|
|
||||||
fn default() -> Self {
|
|
||||||
unsafe { core::mem::zeroed() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CmpMap for CmpLogMap {
|
|
||||||
fn len(&self) -> usize {
|
|
||||||
CMPLOG_MAP_W
|
|
||||||
}
|
|
||||||
|
|
||||||
fn executions_for(&self, idx: usize) -> usize {
|
|
||||||
self.headers[idx].hits as usize
|
|
||||||
}
|
|
||||||
|
|
||||||
fn usable_executions_for(&self, idx: usize) -> usize {
|
|
||||||
if self.headers[idx].kind == CMPLOG_KIND_INS {
|
|
||||||
if self.executions_for(idx) < CMPLOG_MAP_H {
|
|
||||||
self.executions_for(idx)
|
|
||||||
} else {
|
|
||||||
CMPLOG_MAP_H
|
|
||||||
}
|
|
||||||
} else if self.executions_for(idx) < CMPLOG_MAP_RTN_H {
|
|
||||||
self.executions_for(idx)
|
|
||||||
} else {
|
|
||||||
CMPLOG_MAP_RTN_H
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> {
|
|
||||||
if self.headers[idx].kind == CMPLOG_KIND_INS {
|
|
||||||
unsafe {
|
|
||||||
match self.headers[idx].shape {
|
|
||||||
1 => Some(CmpValues::U8((
|
|
||||||
self.vals.operands[idx][execution].0 as u8,
|
|
||||||
self.vals.operands[idx][execution].1 as u8,
|
|
||||||
))),
|
|
||||||
2 => Some(CmpValues::U16((
|
|
||||||
self.vals.operands[idx][execution].0 as u16,
|
|
||||||
self.vals.operands[idx][execution].1 as u16,
|
|
||||||
))),
|
|
||||||
4 => Some(CmpValues::U32((
|
|
||||||
self.vals.operands[idx][execution].0 as u32,
|
|
||||||
self.vals.operands[idx][execution].1 as u32,
|
|
||||||
))),
|
|
||||||
8 => Some(CmpValues::U64((
|
|
||||||
self.vals.operands[idx][execution].0,
|
|
||||||
self.vals.operands[idx][execution].1,
|
|
||||||
))),
|
|
||||||
// other => panic!("Invalid CmpLog shape {}", other),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unsafe {
|
|
||||||
Some(CmpValues::Bytes((
|
|
||||||
self.vals.routines[idx][execution].0.to_vec(),
|
|
||||||
self.vals.routines[idx][execution].1.to_vec(),
|
|
||||||
)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn reset(&mut self) -> Result<(), Error> {
|
|
||||||
// For performance, we reset just the headers
|
|
||||||
self.headers.fill(CmpLogHeader {
|
|
||||||
hits: 0,
|
|
||||||
shape: 0,
|
|
||||||
kind: 0,
|
|
||||||
});
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The global `CmpLog` map for the current `LibAFL` run.
|
|
||||||
#[no_mangle]
|
|
||||||
#[allow(clippy::large_stack_arrays)]
|
|
||||||
pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap {
|
|
||||||
headers: [CmpLogHeader {
|
|
||||||
hits: 0,
|
|
||||||
shape: 0,
|
|
||||||
kind: 0,
|
|
||||||
}; CMPLOG_MAP_W],
|
|
||||||
vals: CmpLogVals {
|
|
||||||
operands: [[CmpLogInstruction(0, 0); CMPLOG_MAP_H]; CMPLOG_MAP_W],
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use libafl_cmplog_map as CMPLOG_MAP;
|
|
||||||
|
|
||||||
/// Value indicating if cmplog is enabled.
|
|
||||||
#[no_mangle]
|
|
||||||
pub static mut libafl_cmplog_enabled: u8 = 0;
|
|
||||||
|
|
||||||
pub use libafl_cmplog_enabled as CMPLOG_ENABLED;
|
|
||||||
|
|
||||||
/// A [`CmpObserver`] observer for `CmpLog`
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct CmpLogObserver {
|
|
||||||
map: OwnedMutPtr<CmpLogMap>,
|
|
||||||
size: Option<OwnedMutPtr<usize>>,
|
|
||||||
add_meta: bool,
|
|
||||||
name: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S> CmpObserver<'a, CmpLogMap, S, CmpValuesMetadata> for CmpLogObserver
|
|
||||||
where
|
|
||||||
S: UsesInput + HasMetadata,
|
|
||||||
{
|
|
||||||
/// Get the number of usable cmps (all by default)
|
|
||||||
fn usable_count(&self) -> usize {
|
|
||||||
match &self.size {
|
|
||||||
None => self.map.as_ref().len(),
|
|
||||||
Some(o) => *o.as_ref(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmp_map(&self) -> &CmpLogMap {
|
|
||||||
self.map.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmp_map_mut(&mut self) -> &mut CmpLogMap {
|
|
||||||
self.map.as_mut()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a, S> Observer<S> for CmpLogObserver
|
|
||||||
where
|
|
||||||
S: UsesInput + HasMetadata,
|
|
||||||
Self: CmpObserver<'a, CmpLogMap, S, CmpValuesMetadata>,
|
|
||||||
{
|
|
||||||
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
|
||||||
self.map.as_mut().reset()?;
|
|
||||||
unsafe {
|
|
||||||
CMPLOG_ENABLED = 1;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn post_exec(
|
|
||||||
&mut self,
|
|
||||||
state: &mut S,
|
|
||||||
_input: &S::Input,
|
|
||||||
_exit_kind: &ExitKind,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
unsafe {
|
|
||||||
CMPLOG_ENABLED = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.add_meta {
|
|
||||||
self.add_cmpvalues_meta(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Named for CmpLogObserver {
|
|
||||||
fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CmpLogObserver {
|
|
||||||
/// Creates a new [`CmpLogObserver`] with the given map and name.
|
|
||||||
///
|
|
||||||
/// # Safety
|
|
||||||
/// Will keep a ptr to the map. The map may not move in memory!
|
|
||||||
#[must_use]
|
|
||||||
pub unsafe fn with_map_ptr(name: &'static str, map: *mut CmpLogMap, add_meta: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
name: name.to_string(),
|
|
||||||
size: None,
|
|
||||||
add_meta,
|
|
||||||
map: OwnedMutPtr::Ptr(map),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a new [`CmpLogObserver`] with the given name from the default [`CMPLOG_MAP`]
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(name: &'static str, add_meta: bool) -> Self {
|
|
||||||
unsafe { Self::with_map_ptr(name, libafl_cmplog_map_ptr, add_meta) }
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO with_size
|
|
||||||
}
|
|
546
libafl_targets/src/cmps/mod.rs
Normal file
546
libafl_targets/src/cmps/mod.rs
Normal file
@ -0,0 +1,546 @@
|
|||||||
|
/// cmp related observers
|
||||||
|
pub mod observers;
|
||||||
|
pub use observers::*;
|
||||||
|
|
||||||
|
/// cmp related stages
|
||||||
|
pub mod stages;
|
||||||
|
use alloc::{alloc::alloc_zeroed, boxed::Box, vec::Vec};
|
||||||
|
use core::{
|
||||||
|
alloc::Layout,
|
||||||
|
fmt::{self, Debug, Formatter},
|
||||||
|
};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
observers::{cmp::AFLppCmpLogHeader, CmpMap, CmpValues},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||||
|
pub use stages::*;
|
||||||
|
|
||||||
|
use crate::{AFLPP_CMPLOG_MAP_H, AFLPP_CMPLOG_MAP_W, CMPLOG_MAP_H, CMPLOG_MAP_W};
|
||||||
|
|
||||||
|
// CONSTANTS
|
||||||
|
|
||||||
|
/// The `CmpLog` map size
|
||||||
|
pub const CMPLOG_MAP_SIZE: usize = CMPLOG_MAP_W * CMPLOG_MAP_H;
|
||||||
|
|
||||||
|
/// The size of a logged routine argument in bytes
|
||||||
|
pub const CMPLOG_RTN_LEN: usize = 32;
|
||||||
|
|
||||||
|
/// The hight of a cmplog routine map
|
||||||
|
pub const CMPLOG_MAP_RTN_H: usize = (CMPLOG_MAP_H * core::mem::size_of::<CmpLogInstruction>())
|
||||||
|
/ core::mem::size_of::<CmpLogRoutine>();
|
||||||
|
|
||||||
|
/// `CmpLog` instruction kind
|
||||||
|
pub const CMPLOG_KIND_INS: u8 = 0;
|
||||||
|
/// `CmpLog` routine kind
|
||||||
|
pub const CMPLOG_KIND_RTN: u8 = 1;
|
||||||
|
|
||||||
|
/// The height for RTN
|
||||||
|
pub const AFL_CMPLOG_MAP_RTN_H: usize = AFLPP_CMPLOG_MAP_H / 2;
|
||||||
|
|
||||||
|
/// The AFL++ `CMP_TYPE_INS`
|
||||||
|
pub const AFL_CMP_TYPE_INS: u32 = 1;
|
||||||
|
/// The AFL++ `CMP_TYPE_RTN`
|
||||||
|
pub const AFL_CMP_TYPE_RTN: u32 = 2;
|
||||||
|
|
||||||
|
// EXTERNS, GLOBALS
|
||||||
|
|
||||||
|
// void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape, uint64_t arg1, uint64_t arg2)
|
||||||
|
extern "C" {
|
||||||
|
/// Logs an instruction for feedback during fuzzing
|
||||||
|
pub fn __libafl_targets_cmplog_instructions(k: usize, shape: u8, arg1: u64, arg2: u64);
|
||||||
|
|
||||||
|
/// Pointer to the `CmpLog` map
|
||||||
|
pub static mut libafl_cmplog_map_ptr: *mut CmpLogMap;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use libafl_cmplog_map_ptr as CMPLOG_MAP_PTR;
|
||||||
|
|
||||||
|
/// Value indicating if cmplog is enabled.
|
||||||
|
#[no_mangle]
|
||||||
|
pub static mut libafl_cmplog_enabled: u8 = 0;
|
||||||
|
|
||||||
|
pub use libafl_cmplog_enabled as CMPLOG_ENABLED;
|
||||||
|
|
||||||
|
// HEADERS
|
||||||
|
|
||||||
|
/// The header for `CmpLog` hits.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
pub struct CmpLogHeader {
|
||||||
|
hits: u16,
|
||||||
|
shape: u8,
|
||||||
|
kind: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
// VALS
|
||||||
|
|
||||||
|
/// The AFL++ `cmp_operands` struct
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
/// Comparison operands, represented as either two (left and right of comparison) u64 values or
|
||||||
|
/// two (left and right of comparison) u128 values, split into two u64 values. If the left and
|
||||||
|
/// right values are smaller than u64, they can be sign or zero extended to 64 bits, as the actual
|
||||||
|
/// comparison size is determined by the `hits` field of the associated `AFLppCmpLogHeader`.
|
||||||
|
pub struct AFLppCmpLogOperands {
|
||||||
|
v0: u64,
|
||||||
|
v1: u64,
|
||||||
|
v0_128: u64,
|
||||||
|
v1_128: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AFLppCmpLogOperands {
|
||||||
|
#[must_use]
|
||||||
|
/// Create new `AFLppCmpLogOperands`
|
||||||
|
pub fn new(v0: u64, v1: u64) -> Self {
|
||||||
|
Self {
|
||||||
|
v0,
|
||||||
|
v1,
|
||||||
|
v0_128: 0,
|
||||||
|
v1_128: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Create new `AFLppCmpLogOperands` with 128-bit comparison values
|
||||||
|
pub fn new_128bit(v0: u128, v1: u128) -> Self {
|
||||||
|
let v0_128 = (v0 >> 64) as u64;
|
||||||
|
let v0 = v0 as u64;
|
||||||
|
let v1_128 = (v1 >> 64) as u64;
|
||||||
|
let v1 = v1 as u64;
|
||||||
|
|
||||||
|
Self {
|
||||||
|
v0,
|
||||||
|
v1,
|
||||||
|
v0_128,
|
||||||
|
v1_128,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// 64bit first cmp operand
|
||||||
|
pub fn v0(&self) -> u64 {
|
||||||
|
self.v0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// 64bit second cmp operand
|
||||||
|
pub fn v1(&self) -> u64 {
|
||||||
|
self.v1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// 128bit first cmp operand
|
||||||
|
pub fn v0_128(&self) -> u64 {
|
||||||
|
self.v0_128
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// 128bit second cmp operand
|
||||||
|
pub fn v1_128(&self) -> u64 {
|
||||||
|
self.v1_128
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the v0 (left) side of the comparison
|
||||||
|
pub fn set_v0(&mut self, v0: u64) {
|
||||||
|
self.v0 = v0;
|
||||||
|
self.v0_128 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the v1 (right) side of the comparison
|
||||||
|
pub fn set_v1(&mut self, v1: u64) {
|
||||||
|
self.v1 = v1;
|
||||||
|
self.v1_128 = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the v0 (left) side of the comparison from a 128-bit comparison value
|
||||||
|
pub fn set_v0_128(&mut self, v0: u128) {
|
||||||
|
self.v0_128 = (v0 >> 64) as u64;
|
||||||
|
self.v0 = v0 as u64;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the v1 (right) side of the comparison from a 128-bit comparison value
|
||||||
|
pub fn set_v1_128(&mut self, v1: u128) {
|
||||||
|
self.v1_128 = (v1 >> 64) as u64;
|
||||||
|
self.v1 = v1 as u64;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The AFL++ `cmpfn_operands` struct
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
/// Comparison function operands, like for strcmp/memcmp, represented as two byte arrays.
|
||||||
|
pub struct AFLppCmpLogFnOperands {
|
||||||
|
v0: [u8; 31],
|
||||||
|
v0_len: u8,
|
||||||
|
v1: [u8; 31],
|
||||||
|
v1_len: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AFLppCmpLogFnOperands {
|
||||||
|
#[must_use]
|
||||||
|
/// Create a new AFL++ function operands comparison values from two byte slices
|
||||||
|
pub fn new(v0: &[u8], v1: &[u8]) -> Self {
|
||||||
|
let v0_len = v0.len() as u8;
|
||||||
|
let v1_len = v1.len() as u8;
|
||||||
|
|
||||||
|
let mut v0_arr = [0; 31];
|
||||||
|
let mut v1_arr = [0; 31];
|
||||||
|
|
||||||
|
v0_arr.copy_from_slice(v0);
|
||||||
|
v1_arr.copy_from_slice(v1);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
v0: v0_arr,
|
||||||
|
v0_len,
|
||||||
|
v1: v1_arr,
|
||||||
|
v1_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// first rtn operand
|
||||||
|
pub fn v0(&self) -> &[u8; 31] {
|
||||||
|
&self.v0
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// second rtn operand
|
||||||
|
pub fn v0_len(&self) -> u8 {
|
||||||
|
self.v0_len
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// first rtn operand len
|
||||||
|
pub fn v1(&self) -> &[u8; 31] {
|
||||||
|
&self.v1
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// second rtn operand len
|
||||||
|
pub fn v1_len(&self) -> u8 {
|
||||||
|
self.v1_len
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the v0 (left) side of the comparison
|
||||||
|
pub fn set_v0(&mut self, v0: &[u8]) {
|
||||||
|
self.v0_len = v0.len() as u8;
|
||||||
|
self.v0.copy_from_slice(v0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the v1 (right) side of the comparison
|
||||||
|
pub fn set_v1(&mut self, v1: &[u8]) {
|
||||||
|
self.v1_len = v1.len() as u8;
|
||||||
|
self.v1.copy_from_slice(v1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The operands logged during `CmpLog`.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
pub struct CmpLogInstruction(u64, u64);
|
||||||
|
|
||||||
|
/// The routine arguments logged during `CmpLog`.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Default, Debug, Clone, Copy)]
|
||||||
|
pub struct CmpLogRoutine([u8; CMPLOG_RTN_LEN], [u8; CMPLOG_RTN_LEN]);
|
||||||
|
|
||||||
|
/// Union of cmplog operands and routines
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
pub union CmpLogVals {
|
||||||
|
operands: [[CmpLogInstruction; CMPLOG_MAP_H]; CMPLOG_MAP_W],
|
||||||
|
routines: [[CmpLogRoutine; CMPLOG_MAP_RTN_H]; CMPLOG_MAP_W],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for CmpLogVals {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_struct("CmpLogVals").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
/// Comparison values
|
||||||
|
pub union AFLppCmpLogVals {
|
||||||
|
operands: [[AFLppCmpLogOperands; AFLPP_CMPLOG_MAP_H]; AFLPP_CMPLOG_MAP_W],
|
||||||
|
fn_operands: [[AFLppCmpLogFnOperands; AFL_CMPLOG_MAP_RTN_H]; AFLPP_CMPLOG_MAP_W],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Debug for AFLppCmpLogVals {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
f.debug_struct("AFLppCmpLogVals").finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AFLppCmpLogVals {
|
||||||
|
#[must_use]
|
||||||
|
/// Reference comparison values as comparison operands
|
||||||
|
pub fn operands(&self) -> &[[AFLppCmpLogOperands; AFLPP_CMPLOG_MAP_H]; AFLPP_CMPLOG_MAP_W] {
|
||||||
|
unsafe { &self.operands }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Mutably reference comparison values as comparison operands
|
||||||
|
pub fn operands_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> &mut [[AFLppCmpLogOperands; AFLPP_CMPLOG_MAP_H]; AFLPP_CMPLOG_MAP_W] {
|
||||||
|
unsafe { &mut self.operands }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Reference comparison values as comparison function operands
|
||||||
|
pub fn fn_operands(
|
||||||
|
&self,
|
||||||
|
) -> &[[AFLppCmpLogFnOperands; AFL_CMPLOG_MAP_RTN_H]; AFLPP_CMPLOG_MAP_W] {
|
||||||
|
unsafe { &self.fn_operands }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Mutably reference comparison values as comparison function operands
|
||||||
|
pub fn fn_operands_mut(
|
||||||
|
&mut self,
|
||||||
|
) -> &mut [[AFLppCmpLogFnOperands; AFL_CMPLOG_MAP_RTN_H]; AFLPP_CMPLOG_MAP_W] {
|
||||||
|
unsafe { &mut self.fn_operands }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MAPS
|
||||||
|
|
||||||
|
/// A struct containing the `CmpLog` metadata for a `LibAFL` run.
|
||||||
|
#[repr(C)]
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
pub struct CmpLogMap {
|
||||||
|
headers: [CmpLogHeader; CMPLOG_MAP_W],
|
||||||
|
vals: CmpLogVals,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CmpLogMap {
|
||||||
|
fn default() -> Self {
|
||||||
|
unsafe { core::mem::zeroed() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmpMap for CmpLogMap {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
CMPLOG_MAP_W
|
||||||
|
}
|
||||||
|
|
||||||
|
fn executions_for(&self, idx: usize) -> usize {
|
||||||
|
self.headers[idx].hits as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usable_executions_for(&self, idx: usize) -> usize {
|
||||||
|
if self.headers[idx].kind == CMPLOG_KIND_INS {
|
||||||
|
if self.executions_for(idx) < CMPLOG_MAP_H {
|
||||||
|
self.executions_for(idx)
|
||||||
|
} else {
|
||||||
|
CMPLOG_MAP_H
|
||||||
|
}
|
||||||
|
} else if self.executions_for(idx) < CMPLOG_MAP_RTN_H {
|
||||||
|
self.executions_for(idx)
|
||||||
|
} else {
|
||||||
|
CMPLOG_MAP_RTN_H
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> {
|
||||||
|
if self.headers[idx].kind == CMPLOG_KIND_INS {
|
||||||
|
unsafe {
|
||||||
|
match self.headers[idx].shape {
|
||||||
|
1 => Some(CmpValues::U8((
|
||||||
|
self.vals.operands[idx][execution].0 as u8,
|
||||||
|
self.vals.operands[idx][execution].1 as u8,
|
||||||
|
))),
|
||||||
|
2 => Some(CmpValues::U16((
|
||||||
|
self.vals.operands[idx][execution].0 as u16,
|
||||||
|
self.vals.operands[idx][execution].1 as u16,
|
||||||
|
))),
|
||||||
|
4 => Some(CmpValues::U32((
|
||||||
|
self.vals.operands[idx][execution].0 as u32,
|
||||||
|
self.vals.operands[idx][execution].1 as u32,
|
||||||
|
))),
|
||||||
|
8 => Some(CmpValues::U64((
|
||||||
|
self.vals.operands[idx][execution].0,
|
||||||
|
self.vals.operands[idx][execution].1,
|
||||||
|
))),
|
||||||
|
// other => panic!("Invalid CmpLog shape {}", other),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
Some(CmpValues::Bytes((
|
||||||
|
self.vals.routines[idx][execution].0.to_vec(),
|
||||||
|
self.vals.routines[idx][execution].1.to_vec(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), Error> {
|
||||||
|
// For performance, we reset just the headers
|
||||||
|
self.headers.fill(CmpLogHeader {
|
||||||
|
hits: 0,
|
||||||
|
shape: 0,
|
||||||
|
kind: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The global `CmpLog` map for the current `LibAFL` run.
|
||||||
|
#[no_mangle]
|
||||||
|
#[allow(clippy::large_stack_arrays)]
|
||||||
|
pub static mut libafl_cmplog_map: CmpLogMap = CmpLogMap {
|
||||||
|
headers: [CmpLogHeader {
|
||||||
|
hits: 0,
|
||||||
|
shape: 0,
|
||||||
|
kind: 0,
|
||||||
|
}; CMPLOG_MAP_W],
|
||||||
|
vals: CmpLogVals {
|
||||||
|
operands: [[CmpLogInstruction(0, 0); CMPLOG_MAP_H]; CMPLOG_MAP_W],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub use libafl_cmplog_map as CMPLOG_MAP;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy)]
|
||||||
|
#[repr(C, packed)]
|
||||||
|
/// Comparison map compatible with AFL++ cmplog instrumentation
|
||||||
|
pub struct AFLppCmpLogMap {
|
||||||
|
headers: [AFLppCmpLogHeader; AFLPP_CMPLOG_MAP_W],
|
||||||
|
vals: AFLppCmpLogVals,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AFLppCmpLogMap {
|
||||||
|
#[must_use]
|
||||||
|
/// Instantiate a new boxed zeroed `AFLppCmpLogMap`. This should be used to create a new
|
||||||
|
/// map, because it is so large it cannot be allocated on the stack with default
|
||||||
|
/// runtime configuration.
|
||||||
|
pub fn boxed() -> Box<Self> {
|
||||||
|
unsafe {
|
||||||
|
Box::from_raw(alloc_zeroed(Layout::new::<AFLppCmpLogMap>()) as *mut AFLppCmpLogMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Reference the headers for the map
|
||||||
|
pub fn headers(&self) -> &[AFLppCmpLogHeader] {
|
||||||
|
&self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Mutably reference the headers for the map
|
||||||
|
pub fn headers_mut(&mut self) -> &mut [AFLppCmpLogHeader] {
|
||||||
|
&mut self.headers
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Reference the values for the map
|
||||||
|
pub fn values(&self) -> &AFLppCmpLogVals {
|
||||||
|
&self.vals
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
/// Mutably reference the headers for the map
|
||||||
|
pub fn values_mut(&mut self) -> &mut AFLppCmpLogVals {
|
||||||
|
&mut self.vals
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Serialize for AFLppCmpLogMap {
|
||||||
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
||||||
|
where
|
||||||
|
S: Serializer,
|
||||||
|
{
|
||||||
|
let slice = unsafe {
|
||||||
|
core::slice::from_raw_parts(
|
||||||
|
(self as *const Self) as *const u8,
|
||||||
|
core::mem::size_of::<Self>(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
serializer.serialize_bytes(slice)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'de> Deserialize<'de> for AFLppCmpLogMap {
|
||||||
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||||
|
where
|
||||||
|
D: Deserializer<'de>,
|
||||||
|
{
|
||||||
|
let bytes = Vec::<u8>::deserialize(deserializer)?;
|
||||||
|
let map: Self = unsafe { core::ptr::read(bytes.as_ptr() as *const _) };
|
||||||
|
Ok(map)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmpMap for AFLppCmpLogMap {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
AFLPP_CMPLOG_MAP_W
|
||||||
|
}
|
||||||
|
|
||||||
|
fn executions_for(&self, idx: usize) -> usize {
|
||||||
|
self.headers[idx].hits() as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn usable_executions_for(&self, idx: usize) -> usize {
|
||||||
|
if self.headers[idx]._type() == AFL_CMP_TYPE_INS {
|
||||||
|
if self.executions_for(idx) < AFLPP_CMPLOG_MAP_H {
|
||||||
|
self.executions_for(idx)
|
||||||
|
} else {
|
||||||
|
AFLPP_CMPLOG_MAP_H
|
||||||
|
}
|
||||||
|
} else if self.executions_for(idx) < AFL_CMPLOG_MAP_RTN_H {
|
||||||
|
self.executions_for(idx)
|
||||||
|
} else {
|
||||||
|
AFL_CMPLOG_MAP_RTN_H
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues> {
|
||||||
|
if self.headers[idx]._type() == AFL_CMP_TYPE_INS {
|
||||||
|
unsafe {
|
||||||
|
match self.headers[idx].shape() {
|
||||||
|
0 => Some(CmpValues::U8((
|
||||||
|
self.vals.operands[idx][execution].v0 as u8,
|
||||||
|
self.vals.operands[idx][execution].v1 as u8,
|
||||||
|
))),
|
||||||
|
1 => Some(CmpValues::U16((
|
||||||
|
self.vals.operands[idx][execution].v0 as u16,
|
||||||
|
self.vals.operands[idx][execution].v1 as u16,
|
||||||
|
))),
|
||||||
|
3 => Some(CmpValues::U32((
|
||||||
|
self.vals.operands[idx][execution].v0 as u32,
|
||||||
|
self.vals.operands[idx][execution].v1 as u32,
|
||||||
|
))),
|
||||||
|
7 => Some(CmpValues::U64((
|
||||||
|
self.vals.operands[idx][execution].v0,
|
||||||
|
self.vals.operands[idx][execution].v1,
|
||||||
|
))),
|
||||||
|
// TODO handle 128 bits cmps
|
||||||
|
// other => panic!("Invalid CmpLog shape {}", other),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unsafe {
|
||||||
|
let v0_len = self.vals.fn_operands[idx][execution].v0_len & (0x80 - 1);
|
||||||
|
let v1_len = self.vals.fn_operands[idx][execution].v1_len & (0x80 - 1);
|
||||||
|
Some(CmpValues::Bytes((
|
||||||
|
self.vals.fn_operands[idx][execution].v0[..(v0_len as usize)].to_vec(),
|
||||||
|
self.vals.fn_operands[idx][execution].v1[..(v1_len as usize)].to_vec(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) -> Result<(), Error> {
|
||||||
|
// For performance, we reset just the headers
|
||||||
|
self.headers.fill(AFLppCmpLogHeader { data: [0; 8] });
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
305
libafl_targets/src/cmps/observers/aflpp.rs
Normal file
305
libafl_targets/src/cmps/observers/aflpp.rs
Normal file
@ -0,0 +1,305 @@
|
|||||||
|
use alloc::{
|
||||||
|
string::{String, ToString},
|
||||||
|
vec::Vec,
|
||||||
|
};
|
||||||
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
executors::ExitKind,
|
||||||
|
inputs::UsesInput,
|
||||||
|
observers::{
|
||||||
|
cmp::{AFLppCmpValuesMetadata, CmpMap, CmpObserver, CmpObserverMetadata, CmpValues},
|
||||||
|
Observer,
|
||||||
|
},
|
||||||
|
state::HasMetadata,
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use libafl_bolts::{ownedref::OwnedRefMut, Named};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use crate::cmps::AFLppCmpLogMap;
|
||||||
|
|
||||||
|
/* From AFL++ cmplog.h
|
||||||
|
|
||||||
|
#define CMP_MAP_W 65536
|
||||||
|
#define CMP_MAP_H 32
|
||||||
|
#define CMP_MAP_RTN_H (CMP_MAP_H / 4)
|
||||||
|
|
||||||
|
struct cmp_header {
|
||||||
|
|
||||||
|
unsigned hits : 24;
|
||||||
|
unsigned id : 24;
|
||||||
|
unsigned shape : 5;
|
||||||
|
unsigned type : 2;
|
||||||
|
unsigned attribute : 4;
|
||||||
|
unsigned overflow : 1;
|
||||||
|
unsigned reserved : 4;
|
||||||
|
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct cmp_operands {
|
||||||
|
|
||||||
|
u64 v0;
|
||||||
|
u64 v1;
|
||||||
|
u64 v0_128;
|
||||||
|
u64 v1_128;
|
||||||
|
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
struct cmpfn_operands {
|
||||||
|
|
||||||
|
u8 v0[31];
|
||||||
|
u8 v0_len;
|
||||||
|
u8 v1[31];
|
||||||
|
u8 v1_len;
|
||||||
|
|
||||||
|
} __attribute__((packed));
|
||||||
|
|
||||||
|
typedef struct cmp_operands cmp_map_list[CMP_MAP_H];
|
||||||
|
|
||||||
|
struct cmp_map {
|
||||||
|
|
||||||
|
struct cmp_header headers[CMP_MAP_W];
|
||||||
|
struct cmp_operands log[CMP_MAP_W][CMP_MAP_H];
|
||||||
|
|
||||||
|
};
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// A [`CmpObserver`] observer for AFL++ redqueen
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct AFLppCmpLogObserver<'a, S>
|
||||||
|
where
|
||||||
|
S: UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
cmp_map: OwnedRefMut<'a, AFLppCmpLogMap>,
|
||||||
|
size: Option<OwnedRefMut<'a, usize>>,
|
||||||
|
name: String,
|
||||||
|
add_meta: bool,
|
||||||
|
original: <AFLppCmpValuesMetadata as CmpObserverMetadata<'a, AFLppCmpLogMap>>::Data,
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S> CmpObserver<'a, AFLppCmpLogMap, S, AFLppCmpValuesMetadata>
|
||||||
|
for AFLppCmpLogObserver<'a, S>
|
||||||
|
where
|
||||||
|
S: UsesInput + Debug + HasMetadata,
|
||||||
|
{
|
||||||
|
/// Get the number of usable cmps (all by default)
|
||||||
|
fn usable_count(&self) -> usize {
|
||||||
|
match &self.size {
|
||||||
|
None => self.cmp_map.as_ref().len(),
|
||||||
|
Some(o) => *o.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmp_map(&self) -> &AFLppCmpLogMap {
|
||||||
|
self.cmp_map.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmp_map_mut(&mut self) -> &mut AFLppCmpLogMap {
|
||||||
|
self.cmp_map.as_mut()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmp_observer_data(
|
||||||
|
&self,
|
||||||
|
) -> <AFLppCmpValuesMetadata as CmpObserverMetadata<'a, AFLppCmpLogMap>>::Data {
|
||||||
|
self.original
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add `AFLppCmpValuesMetadata` to the State including the logged values.
|
||||||
|
/// This routine does a basic loop filtering because loop index cmps are not interesting.
|
||||||
|
fn add_cmpvalues_meta(&mut self, state: &mut S)
|
||||||
|
where
|
||||||
|
S: HasMetadata,
|
||||||
|
{
|
||||||
|
#[allow(clippy::option_if_let_else)] // we can't mutate state in a closure
|
||||||
|
let meta = if let Some(meta) = state.metadata_map_mut().get_mut::<AFLppCmpValuesMetadata>()
|
||||||
|
{
|
||||||
|
meta
|
||||||
|
} else {
|
||||||
|
state.add_metadata(AFLppCmpValuesMetadata::new());
|
||||||
|
state
|
||||||
|
.metadata_map_mut()
|
||||||
|
.get_mut::<AFLppCmpValuesMetadata>()
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
if self.original {
|
||||||
|
// If this observer is for original input, then we have run the un-mutated input
|
||||||
|
// Clear orig_cmpvals
|
||||||
|
meta.orig_cmpvals.clear();
|
||||||
|
// Clear headers
|
||||||
|
meta.headers.clear();
|
||||||
|
} else {
|
||||||
|
// If this observer is for the mutated input
|
||||||
|
meta.new_cmpvals.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
let usable_count = self.usable_count();
|
||||||
|
let cmp_observer_data = self.cmp_observer_data();
|
||||||
|
|
||||||
|
meta.add_from(usable_count, self.cmp_map_mut(), cmp_observer_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S> Observer<S> for AFLppCmpLogObserver<'a, S>
|
||||||
|
where
|
||||||
|
S: UsesInput + Debug + HasMetadata,
|
||||||
|
{
|
||||||
|
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
|
self.cmp_map.as_mut().reset()?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
_input: &S::Input,
|
||||||
|
_exit_kind: &ExitKind,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if self.add_meta {
|
||||||
|
self.add_cmpvalues_meta(state);
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S> Named for AFLppCmpLogObserver<'a, S>
|
||||||
|
where
|
||||||
|
S: UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S> AFLppCmpLogObserver<'a, S>
|
||||||
|
where
|
||||||
|
S: UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
/// Creates a new [`AFLppCmpLogObserver`] with the given name and map.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(name: &'static str, map: &'a mut AFLppCmpLogMap, add_meta: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
size: None,
|
||||||
|
cmp_map: OwnedRefMut::Ref(map),
|
||||||
|
add_meta,
|
||||||
|
original: false,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Setter for the flag if the executed input is a mutated one or the original one
|
||||||
|
pub fn set_original(&mut self, v: bool) {
|
||||||
|
self.original = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`AFLppCmpLogObserver`] with the given name, map and reference to variable size.
|
||||||
|
#[must_use]
|
||||||
|
pub fn with_size(
|
||||||
|
name: &'static str,
|
||||||
|
map: &'a mut AFLppCmpLogMap,
|
||||||
|
add_meta: bool,
|
||||||
|
original: bool,
|
||||||
|
size: &'a mut usize,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
size: Some(OwnedRefMut::Ref(size)),
|
||||||
|
cmp_map: OwnedRefMut::Ref(map),
|
||||||
|
add_meta,
|
||||||
|
original,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> CmpObserverMetadata<'a, AFLppCmpLogMap> for AFLppCmpValuesMetadata {
|
||||||
|
type Data = bool;
|
||||||
|
|
||||||
|
fn new_metadata() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_from(
|
||||||
|
&mut self,
|
||||||
|
usable_count: usize,
|
||||||
|
cmp_map: &mut AFLppCmpLogMap,
|
||||||
|
cmp_observer_data: Self::Data,
|
||||||
|
) {
|
||||||
|
let count = usable_count;
|
||||||
|
for i in 0..count {
|
||||||
|
let execs = cmp_map.usable_executions_for(i);
|
||||||
|
if execs > 0 {
|
||||||
|
if cmp_observer_data {
|
||||||
|
// Update header
|
||||||
|
self.headers.push((i, cmp_map.headers[i]));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 {
|
||||||
|
if let Some(val) = cmp_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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let cmpmap_idx = i;
|
||||||
|
let mut cmp_values = Vec::new();
|
||||||
|
if cmp_observer_data {
|
||||||
|
// push into orig_cmpvals
|
||||||
|
// println!("Adding to orig_cmpvals");
|
||||||
|
for j in 0..execs {
|
||||||
|
if let Some(val) = cmp_map.values_of(i, j) {
|
||||||
|
cmp_values.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!("idx: {cmpmap_idx} cmp_values: {:#?}", cmp_values);
|
||||||
|
self.orig_cmpvals.insert(cmpmap_idx, cmp_values);
|
||||||
|
} else {
|
||||||
|
// push into new_cmpvals
|
||||||
|
// println!("Adding to new_cmpvals");
|
||||||
|
for j in 0..execs {
|
||||||
|
if let Some(val) = cmp_map.values_of(i, j) {
|
||||||
|
cmp_values.push(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// println!("idx: {cmpmap_idx} cmp_values: {:#?}", cmp_values);
|
||||||
|
self.new_cmpvals.insert(cmpmap_idx, cmp_values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
108
libafl_targets/src/cmps/observers/cmplog.rs
Normal file
108
libafl_targets/src/cmps/observers/cmplog.rs
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
//! `CmpLog` logs and reports back values touched during fuzzing.
|
||||||
|
//! The values will then be used in subsequent mutations.
|
||||||
|
//!
|
||||||
|
|
||||||
|
use alloc::string::{String, ToString};
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
executors::ExitKind,
|
||||||
|
inputs::UsesInput,
|
||||||
|
observers::{cmp::CmpValuesMetadata, CmpMap, CmpObserver, Observer},
|
||||||
|
state::HasMetadata,
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use libafl_bolts::{ownedref::OwnedMutPtr, Named};
|
||||||
|
|
||||||
|
use crate::cmps::{libafl_cmplog_map_ptr, CmpLogMap, CMPLOG_ENABLED};
|
||||||
|
|
||||||
|
/// A [`CmpObserver`] observer for `CmpLog`
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CmpLogObserver {
|
||||||
|
map: OwnedMutPtr<CmpLogMap>,
|
||||||
|
size: Option<OwnedMutPtr<usize>>,
|
||||||
|
add_meta: bool,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S> CmpObserver<'a, CmpLogMap, S, CmpValuesMetadata> for CmpLogObserver
|
||||||
|
where
|
||||||
|
S: UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
/// Get the number of usable cmps (all by default)
|
||||||
|
fn usable_count(&self) -> usize {
|
||||||
|
match &self.size {
|
||||||
|
None => self.map.as_ref().len(),
|
||||||
|
Some(o) => *o.as_ref(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmp_map(&self) -> &CmpLogMap {
|
||||||
|
self.map.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn cmp_map_mut(&mut self) -> &mut CmpLogMap {
|
||||||
|
self.map.as_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S> Observer<S> for CmpLogObserver
|
||||||
|
where
|
||||||
|
S: UsesInput + HasMetadata,
|
||||||
|
Self: CmpObserver<'a, CmpLogMap, S, CmpValuesMetadata>,
|
||||||
|
{
|
||||||
|
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
|
self.map.as_mut().reset()?;
|
||||||
|
unsafe {
|
||||||
|
CMPLOG_ENABLED = 1;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
_input: &S::Input,
|
||||||
|
_exit_kind: &ExitKind,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
unsafe {
|
||||||
|
CMPLOG_ENABLED = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.add_meta {
|
||||||
|
self.add_cmpvalues_meta(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for CmpLogObserver {
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CmpLogObserver {
|
||||||
|
/// Creates a new [`CmpLogObserver`] with the given map and name.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
/// Will keep a ptr to the map. The map may not move in memory!
|
||||||
|
#[must_use]
|
||||||
|
pub unsafe fn with_map_ptr(name: &'static str, map: *mut CmpLogMap, add_meta: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
size: None,
|
||||||
|
add_meta,
|
||||||
|
map: OwnedMutPtr::Ptr(map),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`CmpLogObserver`] with the given name from the default cmplog map
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(name: &'static str, add_meta: bool) -> Self {
|
||||||
|
unsafe { Self::with_map_ptr(name, libafl_cmplog_map_ptr, add_meta) }
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO with_size
|
||||||
|
}
|
6
libafl_targets/src/cmps/observers/mod.rs
Normal file
6
libafl_targets/src/cmps/observers/mod.rs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
pub mod cmplog;
|
||||||
|
pub use cmplog::*;
|
||||||
|
|
||||||
|
/// aflpp style cmplog observer
|
||||||
|
pub mod aflpp;
|
||||||
|
pub use aflpp::*;
|
152
libafl_targets/src/cmps/stages/aflpptracing.rs
Normal file
152
libafl_targets/src/cmps/stages/aflpptracing.rs
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
use alloc::string::{String, ToString};
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
corpus::{Corpus, CorpusId},
|
||||||
|
executors::{Executor, HasObservers},
|
||||||
|
inputs::{BytesInput, UsesInput},
|
||||||
|
observers::ObserversTuple,
|
||||||
|
stages::{colorization::TaintMetadata, Stage},
|
||||||
|
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, UsesState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use libafl_bolts::tuples::MatchName;
|
||||||
|
|
||||||
|
use crate::cmps::observers::AFLppCmpLogObserver;
|
||||||
|
|
||||||
|
/// Trace with tainted input
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct AFLppCmplogTracingStage<EM, TE, Z> {
|
||||||
|
tracer_executor: TE,
|
||||||
|
cmplog_observer_name: Option<String>,
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
phantom: PhantomData<(EM, TE, Z)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<EM, TE, Z> UsesState for AFLppCmplogTracingStage<EM, TE, Z>
|
||||||
|
where
|
||||||
|
TE: UsesState,
|
||||||
|
{
|
||||||
|
type State = TE::State;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, TE, Z> Stage<E, EM, Z> for AFLppCmplogTracingStage<EM, TE, Z>
|
||||||
|
where
|
||||||
|
E: UsesState<State = TE::State>,
|
||||||
|
TE: Executor<EM, Z> + HasObservers,
|
||||||
|
TE::State: HasClientPerfMonitor
|
||||||
|
+ HasExecutions
|
||||||
|
+ HasCorpus
|
||||||
|
+ HasMetadata
|
||||||
|
+ UsesInput<Input = BytesInput>,
|
||||||
|
EM: UsesState<State = TE::State>,
|
||||||
|
Z: UsesState<State = TE::State>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn perform(
|
||||||
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
|
_executor: &mut E,
|
||||||
|
state: &mut TE::State,
|
||||||
|
manager: &mut EM,
|
||||||
|
corpus_idx: CorpusId,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// First run with the un-mutated input
|
||||||
|
|
||||||
|
let unmutated_input = state.corpus().cloned_input_for_id(corpus_idx)?;
|
||||||
|
|
||||||
|
if let Some(name) = &self.cmplog_observer_name {
|
||||||
|
if let Some(ob) = self
|
||||||
|
.tracer_executor
|
||||||
|
.observers_mut()
|
||||||
|
.match_name_mut::<AFLppCmpLogObserver<TE::State>>(name)
|
||||||
|
{
|
||||||
|
// This is not the original input,
|
||||||
|
// Set it to false
|
||||||
|
ob.set_original(true);
|
||||||
|
}
|
||||||
|
// I can't think of any use of this stage if you don't use AFLppCmpLogObserver
|
||||||
|
// but do nothing ofcourse
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tracer_executor
|
||||||
|
.observers_mut()
|
||||||
|
.pre_exec_all(state, &unmutated_input)?;
|
||||||
|
|
||||||
|
let exit_kind =
|
||||||
|
self.tracer_executor
|
||||||
|
.run_target(fuzzer, state, manager, &unmutated_input)?;
|
||||||
|
|
||||||
|
*state.executions_mut() += 1;
|
||||||
|
|
||||||
|
self.tracer_executor
|
||||||
|
.observers_mut()
|
||||||
|
.post_exec_all(state, &unmutated_input, &exit_kind)?;
|
||||||
|
|
||||||
|
// Second run with the mutated input
|
||||||
|
let mutated_input = match state.metadata_map().get::<TaintMetadata>() {
|
||||||
|
Some(meta) => BytesInput::from(meta.input_vec().as_ref()),
|
||||||
|
None => return Err(Error::unknown("No metadata found")),
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(name) = &self.cmplog_observer_name {
|
||||||
|
if let Some(ob) = self
|
||||||
|
.tracer_executor
|
||||||
|
.observers_mut()
|
||||||
|
.match_name_mut::<AFLppCmpLogObserver<TE::State>>(name)
|
||||||
|
{
|
||||||
|
// This is not the original input,
|
||||||
|
// Set it to false
|
||||||
|
ob.set_original(false);
|
||||||
|
}
|
||||||
|
// I can't think of any use of this stage if you don't use AFLppCmpLogObserver
|
||||||
|
// but do nothing ofcourse
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tracer_executor
|
||||||
|
.observers_mut()
|
||||||
|
.pre_exec_all(state, &mutated_input)?;
|
||||||
|
|
||||||
|
let exit_kind = self
|
||||||
|
.tracer_executor
|
||||||
|
.run_target(fuzzer, state, manager, &mutated_input)?;
|
||||||
|
|
||||||
|
*state.executions_mut() += 1;
|
||||||
|
|
||||||
|
self.tracer_executor
|
||||||
|
.observers_mut()
|
||||||
|
.post_exec_all(state, &mutated_input, &exit_kind)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<EM, TE, Z> AFLppCmplogTracingStage<EM, TE, Z> {
|
||||||
|
/// Creates a new default stage
|
||||||
|
pub fn new(tracer_executor: TE) -> Self {
|
||||||
|
Self {
|
||||||
|
cmplog_observer_name: None,
|
||||||
|
tracer_executor,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// With cmplog observer
|
||||||
|
pub fn with_cmplog_observer_name(tracer_executor: TE, name: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
cmplog_observer_name: Some(name.to_string()),
|
||||||
|
tracer_executor,
|
||||||
|
phantom: PhantomData,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the underlying tracer executor
|
||||||
|
pub fn executor(&self) -> &TE {
|
||||||
|
&self.tracer_executor
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the underlying tracer executor (mut)
|
||||||
|
pub fn executor_mut(&mut self) -> &mut TE {
|
||||||
|
&mut self.tracer_executor
|
||||||
|
}
|
||||||
|
}
|
3
libafl_targets/src/cmps/stages/mod.rs
Normal file
3
libafl_targets/src/cmps/stages/mod.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
/// cmplog tracing for aflpp style cmplog
|
||||||
|
pub mod aflpptracing;
|
||||||
|
pub use aflpptracing::*;
|
@ -108,8 +108,9 @@ pub use coverage::*;
|
|||||||
pub mod value_profile;
|
pub mod value_profile;
|
||||||
pub use value_profile::*;
|
pub use value_profile::*;
|
||||||
|
|
||||||
pub mod cmplog;
|
/// runtime related to comparisons
|
||||||
pub use cmplog::*;
|
pub mod cmps;
|
||||||
|
pub use cmps::*;
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod drcov;
|
pub mod drcov;
|
||||||
|
@ -19,7 +19,7 @@ void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef SANCOV_CMPLOG
|
#ifdef SANCOV_CMPLOG
|
||||||
k &= CMPLOG_MAP_W - 1;
|
k &= CMPLOG_MAP_W - 1;
|
||||||
__libafl_targets_cmplog(k, 1, (uint64_t)arg1, (uint64_t)arg2);
|
__libafl_targets_cmplog_instructions(k, 1, (uint64_t)arg1, (uint64_t)arg2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -33,7 +33,7 @@ void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef SANCOV_CMPLOG
|
#ifdef SANCOV_CMPLOG
|
||||||
k &= CMPLOG_MAP_W - 1;
|
k &= CMPLOG_MAP_W - 1;
|
||||||
__libafl_targets_cmplog(k, 2, (uint64_t)arg1, (uint64_t)arg2);
|
__libafl_targets_cmplog_instructions(k, 2, (uint64_t)arg1, (uint64_t)arg2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -47,7 +47,7 @@ void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef SANCOV_CMPLOG
|
#ifdef SANCOV_CMPLOG
|
||||||
k &= CMPLOG_MAP_W - 1;
|
k &= CMPLOG_MAP_W - 1;
|
||||||
__libafl_targets_cmplog(k, 4, (uint64_t)arg1, (uint64_t)arg2);
|
__libafl_targets_cmplog_instructions(k, 4, (uint64_t)arg1, (uint64_t)arg2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,7 +61,7 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef SANCOV_CMPLOG
|
#ifdef SANCOV_CMPLOG
|
||||||
k &= CMPLOG_MAP_W - 1;
|
k &= CMPLOG_MAP_W - 1;
|
||||||
__libafl_targets_cmplog(k, 8, (uint64_t)arg1, (uint64_t)arg2);
|
__libafl_targets_cmplog_instructions(k, 8, (uint64_t)arg1, (uint64_t)arg2);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,7 +95,7 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
|
|||||||
#endif
|
#endif
|
||||||
#ifdef SANCOV_CMPLOG
|
#ifdef SANCOV_CMPLOG
|
||||||
k &= CMPLOG_MAP_W - 1;
|
k &= CMPLOG_MAP_W - 1;
|
||||||
__libafl_targets_cmplog(k, cases[1] / 8, val, cases[i + 2]);
|
__libafl_targets_cmplog_instructions(k, cases[1] / 8, val, cases[i + 2]);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user