Add generic cmp observer metadata, rename cmp observers, fix cmplogmap reset (#1461)
* Make cmp metadata generic, rename ForkserverCmpObservers with more accurate names * Fix zeroed assignment in cmplogmap * Dont use prelude in libafl_targets * Make _mut functions actually return mut references * Fix fuzzbench forkserver build * Add type alias for easier construction of the standard cmp observer and add aux data accessors
This commit is contained in:
parent
6df415438d
commit
8d8fcdd8db
@ -22,7 +22,7 @@ use libafl::{
|
||||
StdMOptMutator, StdScheduledMutator, Tokens,
|
||||
},
|
||||
observers::{
|
||||
AFLppCmpMap, ForkserverCmpObserver, HitcountsMapObserver, StdMapObserver, TimeObserver,
|
||||
AFLppCmpMap, HitcountsMapObserver, StdCmpValuesObserver, StdMapObserver, TimeObserver,
|
||||
},
|
||||
schedulers::{
|
||||
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
||||
@ -348,7 +348,7 @@ fn fuzz(
|
||||
cmplog_shmem.write_to_env("__AFL_CMPLOG_SHM_ID").unwrap();
|
||||
let cmpmap = unsafe { cmplog_shmem.as_object_mut::<AFLppCmpMap>() };
|
||||
|
||||
let cmplog_observer = ForkserverCmpObserver::new("cmplog", cmpmap, true);
|
||||
let cmplog_observer = StdCmpValuesObserver::new("cmplog", cmpmap, true);
|
||||
|
||||
let cmplog_forkserver = ForkserverExecutor::builder()
|
||||
.program(exec)
|
||||
|
@ -22,7 +22,7 @@ use libafl::{
|
||||
StdMOptMutator, Tokens,
|
||||
},
|
||||
observers::{
|
||||
AFLppCmpMap, AFLppForkserverCmpObserver, HitcountsMapObserver, StdMapObserver, TimeObserver,
|
||||
AFLppCmpMap, AFLppCmpObserver, HitcountsMapObserver, StdMapObserver, TimeObserver,
|
||||
},
|
||||
schedulers::{
|
||||
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
|
||||
@ -350,7 +350,7 @@ fn fuzz(
|
||||
cmplog_shmem.write_to_env("__AFL_CMPLOG_SHM_ID").unwrap();
|
||||
let cmpmap = unsafe { cmplog_shmem.as_object_mut::<AFLppCmpMap>() };
|
||||
|
||||
let cmplog_observer = AFLppForkserverCmpObserver::new("cmplog", cmpmap, true);
|
||||
let cmplog_observer = AFLppCmpObserver::new("cmplog", cmpmap, true);
|
||||
|
||||
let cmplog_forkserver = ForkserverExecutor::builder()
|
||||
.program(exec)
|
||||
|
@ -1,20 +1,43 @@
|
||||
//! The `CmpObserver` provides access to the logged values of CMP instructions
|
||||
|
||||
use alloc::{
|
||||
alloc::alloc_zeroed,
|
||||
boxed::Box,
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{fmt::Debug, marker::PhantomData};
|
||||
use core::{alloc::Layout, fmt::Debug, marker::PhantomData};
|
||||
|
||||
use c2rust_bitfields::BitfieldStruct;
|
||||
use hashbrown::HashMap;
|
||||
use libafl_bolts::{ownedref::OwnedRefMut, AsMutSlice, AsSlice, Named};
|
||||
use libafl_bolts::{ownedref::OwnedRefMut, serdeany::SerdeAny, AsMutSlice, AsSlice, Named};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::{
|
||||
executors::ExitKind, inputs::UsesInput, observers::Observer, state::HasMetadata, Error,
|
||||
};
|
||||
|
||||
/// Generic metadata trait for use in a `CmpObserver`, which adds comparisons from a `CmpObserver`
|
||||
/// primarily intended for use with `AFLppCmpValuesMetadata` or `CmpValuesMetadata`
|
||||
pub trait CmpObserverMetadata<'a, CM>: SerdeAny + Debug
|
||||
where
|
||||
CM: CmpMap + Debug,
|
||||
{
|
||||
/// Extra data used by the metadata when adding information from a `CmpObserver`, for example
|
||||
/// the `original` field in `AFLppCmpObserver`
|
||||
type Data: 'a + Debug + Default + Serialize + DeserializeOwned;
|
||||
|
||||
/// Instantiate a new metadata instance. This is used by `CmpObserver` to create a new
|
||||
/// metadata if one is missing and `add_meta` is specified. This will typically juse call
|
||||
/// `new()`
|
||||
fn new_metadata() -> Self;
|
||||
|
||||
/// 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
|
||||
/// `AFLppCmpObserver`.
|
||||
fn add_from(&mut self, usable_count: usize, cmp_map: &mut CM, cmp_observer_data: Self::Data);
|
||||
}
|
||||
|
||||
/// Compare values collected during a run
|
||||
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
|
||||
pub enum CmpValues {
|
||||
@ -75,6 +98,7 @@ impl AsSlice for CmpValuesMetadata {
|
||||
self.list.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsMutSlice for CmpValuesMetadata {
|
||||
type Entry = CmpValues;
|
||||
/// Convert to a slice
|
||||
@ -92,65 +116,22 @@ impl CmpValuesMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`CmpMap`] traces comparisons during the current execution
|
||||
pub trait CmpMap: Debug {
|
||||
/// Get the number of cmps
|
||||
fn len(&self) -> usize;
|
||||
|
||||
/// Get if it is empty
|
||||
#[must_use]
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Get the number of executions for a cmp
|
||||
fn executions_for(&self, idx: usize) -> usize;
|
||||
|
||||
/// Get the number of logged executions for a cmp
|
||||
fn usable_executions_for(&self, idx: usize) -> usize;
|
||||
|
||||
/// Get the logged values for a cmp
|
||||
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues>;
|
||||
|
||||
/// Reset the state
|
||||
fn reset(&mut self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// A [`CmpObserver`] observes the traced comparisons during the current execution using a [`CmpMap`]
|
||||
pub trait CmpObserver<CM, S>: Observer<S>
|
||||
impl<'a, CM> CmpObserverMetadata<'a, CM> for CmpValuesMetadata
|
||||
where
|
||||
CM: CmpMap,
|
||||
S: UsesInput,
|
||||
{
|
||||
/// Get the number of usable cmps (all by default)
|
||||
fn usable_count(&self) -> usize;
|
||||
type Data = bool;
|
||||
|
||||
/// Get the `CmpMap`
|
||||
fn cmp_map(&self) -> &CM;
|
||||
#[must_use]
|
||||
fn new_metadata() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
|
||||
/// Get the `CmpMap` (mutable)
|
||||
fn cmp_map_mut(&mut self) -> &mut CM;
|
||||
|
||||
/// 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::<CmpValuesMetadata>() {
|
||||
meta
|
||||
} else {
|
||||
state.add_metadata(CmpValuesMetadata::new());
|
||||
state
|
||||
.metadata_map_mut()
|
||||
.get_mut::<CmpValuesMetadata>()
|
||||
.unwrap()
|
||||
};
|
||||
meta.list.clear();
|
||||
let count = self.usable_count();
|
||||
fn add_from(&mut self, usable_count: usize, cmp_map: &mut CM, _: Self::Data) {
|
||||
self.list.clear();
|
||||
let count = usable_count;
|
||||
for i in 0..count {
|
||||
let execs = self.cmp_map().usable_executions_for(i);
|
||||
let execs = cmp_map.usable_executions_for(i);
|
||||
if execs > 0 {
|
||||
// Recongize loops and discard if needed
|
||||
if execs > 4 {
|
||||
@ -161,7 +142,7 @@ where
|
||||
|
||||
let mut last: Option<CmpValues> = None;
|
||||
for j in 0..execs {
|
||||
if let Some(val) = self.cmp_map().values_of(i, j) {
|
||||
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 {
|
||||
@ -192,8 +173,8 @@ where
|
||||
}
|
||||
}
|
||||
for j in 0..execs {
|
||||
if let Some(val) = self.cmp_map().values_of(i, j) {
|
||||
meta.list.push(val);
|
||||
if let Some(val) = cmp_map.values_of(i, j) {
|
||||
self.list.push(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -201,25 +182,94 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`CmpMap`] traces comparisons during the current execution
|
||||
pub trait CmpMap: Debug {
|
||||
/// Get the number of cmps
|
||||
fn len(&self) -> usize;
|
||||
|
||||
/// Get if it is empty
|
||||
#[must_use]
|
||||
fn is_empty(&self) -> bool {
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
/// Get the number of executions for a cmp
|
||||
fn executions_for(&self, idx: usize) -> usize;
|
||||
|
||||
/// Get the number of logged executions for a cmp
|
||||
fn usable_executions_for(&self, idx: usize) -> usize;
|
||||
|
||||
/// Get the logged values for a cmp
|
||||
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues>;
|
||||
|
||||
/// Reset the state
|
||||
fn reset(&mut self) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
/// A [`CmpObserver`] observes the traced comparisons during the current execution using a [`CmpMap`]
|
||||
pub trait CmpObserver<'a, CM, S, M>: Observer<S>
|
||||
where
|
||||
CM: CmpMap,
|
||||
S: UsesInput,
|
||||
M: CmpObserverMetadata<'a, CM>,
|
||||
{
|
||||
/// Get the number of usable cmps (all by default)
|
||||
fn usable_count(&self) -> usize;
|
||||
|
||||
/// Get the `CmpMap`
|
||||
fn cmp_map(&self) -> &CM;
|
||||
|
||||
/// Get the `CmpMap` (mutable)
|
||||
fn cmp_map_mut(&mut self) -> &mut CM;
|
||||
|
||||
/// Get the observer data. By default, this is the default metadata aux data, which is `()`.
|
||||
fn cmp_observer_data(&self) -> M::Data {
|
||||
M::Data::default()
|
||||
}
|
||||
|
||||
/// 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::<M>() {
|
||||
meta
|
||||
} else {
|
||||
state.add_metadata(M::new_metadata());
|
||||
state.metadata_map_mut().get_mut::<M>().unwrap()
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// A standard [`CmpObserver`] observer
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(bound = "CM: serde::de::DeserializeOwned")]
|
||||
pub struct ForkserverCmpObserver<'a, CM, S>
|
||||
pub struct StdCmpObserver<'a, CM, S, M>
|
||||
where
|
||||
CM: CmpMap + Serialize,
|
||||
S: UsesInput + HasMetadata,
|
||||
M: CmpObserverMetadata<'a, CM>,
|
||||
{
|
||||
cmp_map: OwnedRefMut<'a, CM>,
|
||||
size: Option<OwnedRefMut<'a, usize>>,
|
||||
name: String,
|
||||
add_meta: bool,
|
||||
data: M::Data,
|
||||
phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<'a, CM, S> CmpObserver<CM, S> for ForkserverCmpObserver<'a, CM, S>
|
||||
impl<'a, CM, S, M> CmpObserver<'a, CM, S, M> for StdCmpObserver<'a, CM, S, M>
|
||||
where
|
||||
CM: CmpMap + Serialize + DeserializeOwned,
|
||||
S: UsesInput + Debug + HasMetadata,
|
||||
M: CmpObserverMetadata<'a, CM>,
|
||||
{
|
||||
/// Get the number of usable cmps (all by default)
|
||||
fn usable_count(&self) -> usize {
|
||||
@ -236,12 +286,17 @@ where
|
||||
fn cmp_map_mut(&mut self) -> &mut CM {
|
||||
self.cmp_map.as_mut()
|
||||
}
|
||||
|
||||
fn cmp_observer_data(&self) -> <M as CmpObserverMetadata<'a, CM>>::Data {
|
||||
<M as CmpObserverMetadata<CM>>::Data::default()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, CM, S> Observer<S> for ForkserverCmpObserver<'a, CM, S>
|
||||
impl<'a, CM, S, M> Observer<S> for StdCmpObserver<'a, CM, S, M>
|
||||
where
|
||||
CM: CmpMap + Serialize + DeserializeOwned,
|
||||
S: UsesInput + Debug + HasMetadata,
|
||||
M: CmpObserverMetadata<'a, CM>,
|
||||
{
|
||||
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||
self.cmp_map.as_mut().reset()?;
|
||||
@ -261,22 +316,24 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, CM, S> Named for ForkserverCmpObserver<'a, CM, S>
|
||||
impl<'a, CM, S, M> Named for StdCmpObserver<'a, CM, S, M>
|
||||
where
|
||||
CM: CmpMap + Serialize + DeserializeOwned,
|
||||
S: UsesInput + HasMetadata,
|
||||
M: CmpObserverMetadata<'a, CM>,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, CM, S> ForkserverCmpObserver<'a, CM, S>
|
||||
impl<'a, CM, S, M> StdCmpObserver<'a, CM, S, M>
|
||||
where
|
||||
CM: CmpMap + Serialize + DeserializeOwned,
|
||||
S: UsesInput + HasMetadata,
|
||||
M: CmpObserverMetadata<'a, CM>,
|
||||
{
|
||||
/// Creates a new [`ForkserverCmpObserver`] with the given name and map.
|
||||
/// Creates a new [`StdCmpObserver`] with the given name and map.
|
||||
#[must_use]
|
||||
pub fn new(name: &'static str, map: &'a mut CM, add_meta: bool) -> Self {
|
||||
Self {
|
||||
@ -284,11 +341,26 @@ where
|
||||
size: None,
|
||||
cmp_map: OwnedRefMut::Ref(map),
|
||||
add_meta,
|
||||
data: M::Data::default(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`ForkserverCmpObserver`] with the given name, map and reference to variable size.
|
||||
/// Creates a new [`StdCmpObserver`] with the given name, map, and auxiliary data used to
|
||||
/// populate metadata
|
||||
#[must_use]
|
||||
pub fn with_data(name: &'static str, map: &'a mut CM, add_meta: bool, data: M::Data) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
size: None,
|
||||
cmp_map: OwnedRefMut::Ref(map),
|
||||
add_meta,
|
||||
data,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`StdCmpObserver`] with the given name, map and reference to variable size.
|
||||
#[must_use]
|
||||
pub fn with_size(
|
||||
name: &'static str,
|
||||
@ -301,11 +373,45 @@ where
|
||||
size: Some(OwnedRefMut::Ref(size)),
|
||||
cmp_map: OwnedRefMut::Ref(map),
|
||||
add_meta,
|
||||
data: M::Data::default(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`StdCmpObserver`] with the given name, map, auxiliary data, and
|
||||
/// reference to variable size.
|
||||
#[must_use]
|
||||
pub fn with_size_data(
|
||||
name: &'static str,
|
||||
map: &'a mut CM,
|
||||
add_meta: bool,
|
||||
data: M::Data,
|
||||
size: &'a mut usize,
|
||||
) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
size: Some(OwnedRefMut::Ref(size)),
|
||||
cmp_map: OwnedRefMut::Ref(map),
|
||||
add_meta,
|
||||
data,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reference the stored auxiliary data associated with the [`CmpObserverMetadata`]
|
||||
pub fn data(&self) -> &M::Data {
|
||||
&self.data
|
||||
}
|
||||
|
||||
/// Mutably reference the stored auxiliary data associated with the [`CmpObserverMetadata`]
|
||||
pub fn data_mut(&mut self) -> &mut M::Data {
|
||||
&mut self.data
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`StdCmpObserver`] that optionally adds comparisons into a [`CmpValuesMetadata`]
|
||||
pub type StdCmpValuesObserver<'a, CM, S> = StdCmpObserver<'a, CM, S, CmpValuesMetadata>;
|
||||
|
||||
/* From AFL++ cmplog.h
|
||||
|
||||
#define CMP_MAP_W 65536
|
||||
@ -354,7 +460,7 @@ struct cmp_map {
|
||||
|
||||
/// A [`CmpObserver`] observer for AFL++ redqueen
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
pub struct AFLppForkserverCmpObserver<'a, S>
|
||||
pub struct AFLppCmpObserver<'a, S>
|
||||
where
|
||||
S: UsesInput + HasMetadata,
|
||||
{
|
||||
@ -362,11 +468,11 @@ where
|
||||
size: Option<OwnedRefMut<'a, usize>>,
|
||||
name: String,
|
||||
add_meta: bool,
|
||||
original: bool,
|
||||
original: <AFLppCmpValuesMetadata as CmpObserverMetadata<'a, AFLppCmpMap>>::Data,
|
||||
phantom: PhantomData<S>,
|
||||
}
|
||||
|
||||
impl<'a, S> CmpObserver<AFLppCmpMap, S> for AFLppForkserverCmpObserver<'a, S>
|
||||
impl<'a, S> CmpObserver<'a, AFLppCmpMap, S, AFLppCmpValuesMetadata> for AFLppCmpObserver<'a, S>
|
||||
where
|
||||
S: UsesInput + Debug + HasMetadata,
|
||||
{
|
||||
@ -386,6 +492,12 @@ where
|
||||
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)
|
||||
@ -415,84 +527,14 @@ where
|
||||
meta.new_cmpvals.clear();
|
||||
}
|
||||
|
||||
let count = self.usable_count();
|
||||
for i in 0..count {
|
||||
let execs = self.cmp_map().usable_executions_for(i);
|
||||
if execs > 0 {
|
||||
if self.original {
|
||||
// Update header
|
||||
meta.headers.push((i, self.cmp_map().headers[i]));
|
||||
}
|
||||
let usable_count = self.usable_count();
|
||||
let cmp_observer_data = self.cmp_observer_data();
|
||||
|
||||
// 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) = self.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 self.original {
|
||||
// push into orig_cmpvals
|
||||
// println!("Adding to orig_cmpvals");
|
||||
for j in 0..execs {
|
||||
if let Some(val) = self.cmp_map().values_of(i, j) {
|
||||
cmp_values.push(val);
|
||||
}
|
||||
}
|
||||
// println!("idx: {cmpmap_idx} cmp_values: {:#?}", cmp_values);
|
||||
meta.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) = self.cmp_map().values_of(i, j) {
|
||||
cmp_values.push(val);
|
||||
}
|
||||
}
|
||||
// println!("idx: {cmpmap_idx} cmp_values: {:#?}", cmp_values);
|
||||
meta.new_cmpvals.insert(cmpmap_idx, cmp_values);
|
||||
}
|
||||
}
|
||||
}
|
||||
meta.add_from(usable_count, self.cmp_map_mut(), cmp_observer_data);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Observer<S> for AFLppForkserverCmpObserver<'a, S>
|
||||
impl<'a, S> Observer<S> for AFLppCmpObserver<'a, S>
|
||||
where
|
||||
S: UsesInput + Debug + HasMetadata,
|
||||
{
|
||||
@ -514,7 +556,7 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> Named for AFLppForkserverCmpObserver<'a, S>
|
||||
impl<'a, S> Named for AFLppCmpObserver<'a, S>
|
||||
where
|
||||
S: UsesInput + HasMetadata,
|
||||
{
|
||||
@ -523,11 +565,11 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, S> AFLppForkserverCmpObserver<'a, S>
|
||||
impl<'a, S> AFLppCmpObserver<'a, S>
|
||||
where
|
||||
S: UsesInput + HasMetadata,
|
||||
{
|
||||
/// Creates a new [`ForkserverCmpObserver`] with the given name and map.
|
||||
/// 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 {
|
||||
@ -544,7 +586,7 @@ where
|
||||
self.original = v;
|
||||
}
|
||||
|
||||
/// Creates a new [`ForkserverCmpObserver`] with the given name, map and reference to variable size.
|
||||
/// Creates a new [`AFLppCmpObserver`] with the given name, map and reference to variable size.
|
||||
#[must_use]
|
||||
pub fn with_size(
|
||||
name: &'static str,
|
||||
@ -614,6 +656,96 @@ impl AFLppCmpValuesMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
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`
|
||||
@ -626,9 +758,20 @@ pub const AFL_CMP_TYPE_INS: u32 = 1;
|
||||
/// The AFL++ `CMP_TYPE_RTN`
|
||||
pub const AFL_CMP_TYPE_RTN: u32 = 2;
|
||||
|
||||
/// The AFL++ `cmp_header` struct
|
||||
#[derive(Debug, Copy, Clone, BitfieldStruct)]
|
||||
#[repr(C, packed)]
|
||||
/// Comparison header, used to describe a set of comparison values efficiently.
|
||||
///
|
||||
/// # Bitfields
|
||||
///
|
||||
/// - hits: The number of hits of a particular comparison
|
||||
/// - id: Unused by ``LibAFL``, a unique ID for a particular comparison
|
||||
/// - shape: Whether a comparison is u8/u8, u16/u16, etc.
|
||||
/// - _type: Whether the comparison value represents an instruction (like a `cmp`) or function
|
||||
/// call arguments
|
||||
/// - attribute: OR-ed bitflags describing whether the comparison is <, >, =, <=, >=, or transform
|
||||
/// - overflow: Whether the comparison overflows
|
||||
/// - reserved: Reserved for future use
|
||||
pub struct AFLppCmpHeader {
|
||||
#[bitfield(name = "hits", ty = "u32", bits = "0..=23")]
|
||||
#[bitfield(name = "id", ty = "u32", bits = "24..=47")]
|
||||
@ -639,9 +782,14 @@ pub struct AFLppCmpHeader {
|
||||
#[bitfield(name = "reserved", ty = "u32", bits = "60..=63")]
|
||||
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,
|
||||
@ -650,6 +798,33 @@ pub struct AFLppCmpOperands {
|
||||
}
|
||||
|
||||
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 {
|
||||
@ -673,11 +848,36 @@ impl AFLppCmpOperands {
|
||||
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,
|
||||
@ -686,6 +886,26 @@ pub struct AFLppCmpFnOperands {
|
||||
}
|
||||
|
||||
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] {
|
||||
@ -709,11 +929,23 @@ impl AFLppCmpFnOperands {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/// A proxy union to avoid casting operands as in AFL++
|
||||
#[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],
|
||||
@ -725,14 +957,76 @@ impl Debug for AFLppCmpVals {
|
||||
}
|
||||
}
|
||||
|
||||
/// The AFL++ `cmp_map` struct, use with `ForkserverCmpObserver`
|
||||
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
|
||||
|
@ -12,7 +12,7 @@ use crate::{
|
||||
executors::{Executor, HasObservers, ShadowExecutor},
|
||||
inputs::{BytesInput, UsesInput},
|
||||
mark_feature_time,
|
||||
observers::{AFLppForkserverCmpObserver, ObserversTuple},
|
||||
observers::{AFLppCmpObserver, ObserversTuple},
|
||||
stages::{colorization::TaintMetadata, Stage},
|
||||
start_timer,
|
||||
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, State, UsesState},
|
||||
@ -145,13 +145,13 @@ where
|
||||
if let Some(ob) = self
|
||||
.tracer_executor
|
||||
.observers_mut()
|
||||
.match_name_mut::<AFLppForkserverCmpObserver<TE::State>>(name)
|
||||
.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 AFLForkserverCmpObserver
|
||||
// I can't think of any use of this stage if you don't use AFLppCmpObserver
|
||||
// but do nothing ofcourse
|
||||
}
|
||||
|
||||
@ -179,13 +179,13 @@ where
|
||||
if let Some(ob) = self
|
||||
.tracer_executor
|
||||
.observers_mut()
|
||||
.match_name_mut::<AFLppForkserverCmpObserver<TE::State>>(name)
|
||||
.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 AFLForkserverCmpObserver
|
||||
// I can't think of any use of this stage if you don't use AFLppCmpObserver
|
||||
// but do nothing ofcourse
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,7 @@ use core::fmt::{self, Debug, Formatter};
|
||||
use libafl::{
|
||||
executors::ExitKind,
|
||||
inputs::UsesInput,
|
||||
observers::{CmpMap, CmpObserver, CmpValues, Observer},
|
||||
observers::{cmp::CmpValuesMetadata, CmpMap, CmpObserver, CmpValues, Observer},
|
||||
state::HasMetadata,
|
||||
Error,
|
||||
};
|
||||
@ -148,8 +148,12 @@ impl CmpMap for CmpLogMap {
|
||||
|
||||
fn reset(&mut self) -> Result<(), Error> {
|
||||
// For performance, we reset just the headers
|
||||
self.headers = unsafe { core::mem::zeroed() };
|
||||
// self.vals.operands = unsafe { core::mem::zeroed() };
|
||||
self.headers.fill(CmpLogHeader {
|
||||
hits: 0,
|
||||
shape: 0,
|
||||
kind: 0,
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -185,7 +189,7 @@ pub struct CmpLogObserver {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl<S> CmpObserver<CmpLogMap, S> for CmpLogObserver
|
||||
impl<'a, S> CmpObserver<'a, CmpLogMap, S, CmpValuesMetadata> for CmpLogObserver
|
||||
where
|
||||
S: UsesInput + HasMetadata,
|
||||
{
|
||||
@ -206,10 +210,10 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Observer<S> for CmpLogObserver
|
||||
impl<'a, S> Observer<S> for CmpLogObserver
|
||||
where
|
||||
S: UsesInput + HasMetadata,
|
||||
Self: CmpObserver<CmpLogMap, S>,
|
||||
Self: CmpObserver<'a, CmpLogMap, S, CmpValuesMetadata>,
|
||||
{
|
||||
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||
self.map.as_mut().reset()?;
|
||||
@ -228,9 +232,11 @@ where
|
||||
unsafe {
|
||||
CMPLOG_ENABLED = 0;
|
||||
}
|
||||
|
||||
if self.add_meta {
|
||||
self.add_cmpvalues_meta(state);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user