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:
Rowan Hart 2023-08-26 00:54:31 -07:00 committed by GitHub
parent 6df415438d
commit 8d8fcdd8db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 464 additions and 164 deletions

View File

@ -22,7 +22,7 @@ use libafl::{
StdMOptMutator, StdScheduledMutator, Tokens, StdMOptMutator, StdScheduledMutator, Tokens,
}, },
observers::{ observers::{
AFLppCmpMap, ForkserverCmpObserver, HitcountsMapObserver, StdMapObserver, TimeObserver, AFLppCmpMap, HitcountsMapObserver, StdCmpValuesObserver, StdMapObserver, TimeObserver,
}, },
schedulers::{ schedulers::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler, powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
@ -348,7 +348,7 @@ fn fuzz(
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::<AFLppCmpMap>() };
let cmplog_observer = ForkserverCmpObserver::new("cmplog", cmpmap, true); let cmplog_observer = StdCmpValuesObserver::new("cmplog", cmpmap, true);
let cmplog_forkserver = ForkserverExecutor::builder() let cmplog_forkserver = ForkserverExecutor::builder()
.program(exec) .program(exec)

View File

@ -22,7 +22,7 @@ use libafl::{
StdMOptMutator, Tokens, StdMOptMutator, Tokens,
}, },
observers::{ observers::{
AFLppCmpMap, AFLppForkserverCmpObserver, HitcountsMapObserver, StdMapObserver, TimeObserver, AFLppCmpMap, AFLppCmpObserver, HitcountsMapObserver, StdMapObserver, TimeObserver,
}, },
schedulers::{ schedulers::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler, powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
@ -350,7 +350,7 @@ fn fuzz(
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::<AFLppCmpMap>() };
let cmplog_observer = AFLppForkserverCmpObserver::new("cmplog", cmpmap, true); let cmplog_observer = AFLppCmpObserver::new("cmplog", cmpmap, true);
let cmplog_forkserver = ForkserverExecutor::builder() let cmplog_forkserver = ForkserverExecutor::builder()
.program(exec) .program(exec)

View File

@ -1,20 +1,43 @@
//! 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::{fmt::Debug, marker::PhantomData}; use core::{alloc::Layout, fmt::Debug, marker::PhantomData};
use c2rust_bitfields::BitfieldStruct; use c2rust_bitfields::BitfieldStruct;
use hashbrown::HashMap; 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 serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
use crate::{ use crate::{
executors::ExitKind, inputs::UsesInput, observers::Observer, state::HasMetadata, Error, 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 /// Compare values collected during a run
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)] #[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
pub enum CmpValues { pub enum CmpValues {
@ -75,6 +98,7 @@ impl AsSlice for CmpValuesMetadata {
self.list.as_slice() self.list.as_slice()
} }
} }
impl AsMutSlice for CmpValuesMetadata { impl AsMutSlice for CmpValuesMetadata {
type Entry = CmpValues; type Entry = CmpValues;
/// Convert to a slice /// Convert to a slice
@ -92,65 +116,22 @@ impl CmpValuesMetadata {
} }
} }
/// A [`CmpMap`] traces comparisons during the current execution impl<'a, CM> CmpObserverMetadata<'a, CM> for CmpValuesMetadata
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>
where where
CM: CmpMap, CM: CmpMap,
S: UsesInput,
{ {
/// Get the number of usable cmps (all by default) type Data = bool;
fn usable_count(&self) -> usize;
/// Get the `CmpMap` #[must_use]
fn cmp_map(&self) -> &CM; fn new_metadata() -> Self {
Self::new()
}
/// Get the `CmpMap` (mutable) fn add_from(&mut self, usable_count: usize, cmp_map: &mut CM, _: Self::Data) {
fn cmp_map_mut(&mut self) -> &mut CM; self.list.clear();
let count = usable_count;
/// 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();
for i in 0..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 { if execs > 0 {
// Recongize loops and discard if needed // Recongize loops and discard if needed
if execs > 4 { if execs > 4 {
@ -161,7 +142,7 @@ where
let mut last: Option<CmpValues> = None; let mut last: Option<CmpValues> = None;
for j in 0..execs { 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(l) = last.and_then(|x| x.to_u64_tuple()) {
if let Some(v) = val.to_u64_tuple() { if let Some(v) = val.to_u64_tuple() {
if l.0.wrapping_add(1) == v.0 { if l.0.wrapping_add(1) == v.0 {
@ -192,8 +173,8 @@ where
} }
} }
for j in 0..execs { 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) {
meta.list.push(val); 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 /// A standard [`CmpObserver`] observer
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(bound = "CM: serde::de::DeserializeOwned")] #[serde(bound = "CM: serde::de::DeserializeOwned")]
pub struct ForkserverCmpObserver<'a, CM, S> pub struct StdCmpObserver<'a, CM, S, M>
where where
CM: CmpMap + Serialize, CM: CmpMap + Serialize,
S: UsesInput + HasMetadata, S: UsesInput + HasMetadata,
M: CmpObserverMetadata<'a, CM>,
{ {
cmp_map: OwnedRefMut<'a, CM>, cmp_map: OwnedRefMut<'a, CM>,
size: Option<OwnedRefMut<'a, usize>>, size: Option<OwnedRefMut<'a, usize>>,
name: String, name: String,
add_meta: bool, add_meta: bool,
data: M::Data,
phantom: PhantomData<S>, 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 where
CM: CmpMap + Serialize + DeserializeOwned, CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput + Debug + HasMetadata, S: UsesInput + Debug + HasMetadata,
M: CmpObserverMetadata<'a, CM>,
{ {
/// Get the number of usable cmps (all by default) /// Get the number of usable cmps (all by default)
fn usable_count(&self) -> usize { fn usable_count(&self) -> usize {
@ -236,12 +286,17 @@ where
fn cmp_map_mut(&mut self) -> &mut CM { fn cmp_map_mut(&mut self) -> &mut CM {
self.cmp_map.as_mut() 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 where
CM: CmpMap + Serialize + DeserializeOwned, CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput + Debug + HasMetadata, S: UsesInput + Debug + HasMetadata,
M: CmpObserverMetadata<'a, CM>,
{ {
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> { fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.cmp_map.as_mut().reset()?; 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 where
CM: CmpMap + Serialize + DeserializeOwned, CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput + HasMetadata, S: UsesInput + HasMetadata,
M: CmpObserverMetadata<'a, CM>,
{ {
fn name(&self) -> &str { fn name(&self) -> &str {
&self.name &self.name
} }
} }
impl<'a, CM, S> ForkserverCmpObserver<'a, CM, S> impl<'a, CM, S, M> StdCmpObserver<'a, CM, S, M>
where where
CM: CmpMap + Serialize + DeserializeOwned, CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput + HasMetadata, 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] #[must_use]
pub fn new(name: &'static str, map: &'a mut CM, add_meta: bool) -> Self { pub fn new(name: &'static str, map: &'a mut CM, add_meta: bool) -> Self {
Self { Self {
@ -284,11 +341,26 @@ where
size: None, size: None,
cmp_map: OwnedRefMut::Ref(map), cmp_map: OwnedRefMut::Ref(map),
add_meta, add_meta,
data: M::Data::default(),
phantom: PhantomData, 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] #[must_use]
pub fn with_size( pub fn with_size(
name: &'static str, name: &'static str,
@ -301,10 +373,44 @@ where
size: Some(OwnedRefMut::Ref(size)), size: Some(OwnedRefMut::Ref(size)),
cmp_map: OwnedRefMut::Ref(map), cmp_map: OwnedRefMut::Ref(map),
add_meta, add_meta,
data: M::Data::default(),
phantom: PhantomData, 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 /* From AFL++ cmplog.h
@ -354,7 +460,7 @@ struct cmp_map {
/// A [`CmpObserver`] observer for AFL++ redqueen /// A [`CmpObserver`] observer for AFL++ redqueen
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub struct AFLppForkserverCmpObserver<'a, S> pub struct AFLppCmpObserver<'a, S>
where where
S: UsesInput + HasMetadata, S: UsesInput + HasMetadata,
{ {
@ -362,11 +468,11 @@ where
size: Option<OwnedRefMut<'a, usize>>, size: Option<OwnedRefMut<'a, usize>>,
name: String, name: String,
add_meta: bool, add_meta: bool,
original: bool, original: <AFLppCmpValuesMetadata as CmpObserverMetadata<'a, AFLppCmpMap>>::Data,
phantom: PhantomData<S>, 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 where
S: UsesInput + Debug + HasMetadata, S: UsesInput + Debug + HasMetadata,
{ {
@ -386,6 +492,12 @@ where
self.cmp_map.as_mut() 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. /// Add [`struct@CmpValuesMetadata`] to the State including the logged values.
/// This routine does a basic loop filtering because loop index cmps are not interesting. /// This routine does a basic loop filtering because loop index cmps are not interesting.
fn add_cmpvalues_meta(&mut self, state: &mut S) fn add_cmpvalues_meta(&mut self, state: &mut S)
@ -415,84 +527,14 @@ where
meta.new_cmpvals.clear(); meta.new_cmpvals.clear();
} }
let count = self.usable_count(); let usable_count = self.usable_count();
for i in 0..count { let cmp_observer_data = self.cmp_observer_data();
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]));
}
// Recongize loops and discard if needed meta.add_from(usable_count, self.cmp_map_mut(), cmp_observer_data);
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; impl<'a, S> Observer<S> for AFLppCmpObserver<'a, S>
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);
}
}
}
}
}
impl<'a, S> Observer<S> for AFLppForkserverCmpObserver<'a, S>
where where
S: UsesInput + Debug + HasMetadata, 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 where
S: UsesInput + HasMetadata, S: UsesInput + HasMetadata,
{ {
@ -523,11 +565,11 @@ where
} }
} }
impl<'a, S> AFLppForkserverCmpObserver<'a, S> impl<'a, S> AFLppCmpObserver<'a, S>
where where
S: UsesInput + HasMetadata, 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] #[must_use]
pub fn new(name: &'static str, map: &'a mut AFLppCmpMap, add_meta: bool) -> Self { pub fn new(name: &'static str, map: &'a mut AFLppCmpMap, add_meta: bool) -> Self {
Self { Self {
@ -544,7 +586,7 @@ where
self.original = v; 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] #[must_use]
pub fn with_size( pub fn with_size(
name: &'static str, 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` /// The AFL++ `CMP_MAP_W`
pub const AFL_CMP_MAP_W: usize = 65536; pub const AFL_CMP_MAP_W: usize = 65536;
/// The AFL++ `CMP_MAP_H` /// The AFL++ `CMP_MAP_H`
@ -626,9 +758,20 @@ pub const AFL_CMP_TYPE_INS: u32 = 1;
/// The AFL++ `CMP_TYPE_RTN` /// The AFL++ `CMP_TYPE_RTN`
pub const AFL_CMP_TYPE_RTN: u32 = 2; pub const AFL_CMP_TYPE_RTN: u32 = 2;
/// The AFL++ `cmp_header` struct
#[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.
///
/// # 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 { pub struct AFLppCmpHeader {
#[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")]
@ -639,9 +782,14 @@ pub struct AFLppCmpHeader {
#[bitfield(name = "reserved", ty = "u32", bits = "60..=63")] #[bitfield(name = "reserved", ty = "u32", bits = "60..=63")]
data: [u8; 8], data: [u8; 8],
} }
/// The AFL++ `cmp_operands` struct /// The AFL++ `cmp_operands` struct
#[derive(Default, Debug, Clone, Copy)] #[derive(Default, Debug, Clone, Copy)]
#[repr(C, packed)] #[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 { pub struct AFLppCmpOperands {
v0: u64, v0: u64,
v1: u64, v1: u64,
@ -650,6 +798,33 @@ pub struct AFLppCmpOperands {
} }
impl 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] #[must_use]
/// 64bit first cmp operand /// 64bit first cmp operand
pub fn v0(&self) -> u64 { pub fn v0(&self) -> u64 {
@ -673,11 +848,36 @@ impl AFLppCmpOperands {
pub fn v1_128(&self) -> u64 { pub fn v1_128(&self) -> u64 {
self.v1_128 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 /// The AFL++ `cmpfn_operands` struct
#[derive(Default, Debug, Clone, Copy)] #[derive(Default, Debug, Clone, Copy)]
#[repr(C, packed)] #[repr(C, packed)]
/// Comparison function operands, like for strcmp/memcmp, represented as two byte arrays.
pub struct AFLppCmpFnOperands { pub struct AFLppCmpFnOperands {
v0: [u8; 31], v0: [u8; 31],
v0_len: u8, v0_len: u8,
@ -686,6 +886,26 @@ pub struct AFLppCmpFnOperands {
} }
impl 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] #[must_use]
/// first rtn operand /// first rtn operand
pub fn v0(&self) -> &[u8; 31] { pub fn v0(&self) -> &[u8; 31] {
@ -709,11 +929,23 @@ impl AFLppCmpFnOperands {
pub fn v1_len(&self) -> u8 { pub fn v1_len(&self) -> u8 {
self.v1_len 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)] #[derive(Clone, Copy)]
#[repr(C, packed)] #[repr(C, packed)]
/// Comparison values
pub union AFLppCmpVals { pub union AFLppCmpVals {
operands: [[AFLppCmpOperands; AFL_CMP_MAP_H]; AFL_CMP_MAP_W], operands: [[AFLppCmpOperands; AFL_CMP_MAP_H]; AFL_CMP_MAP_W],
fn_operands: [[AFLppCmpFnOperands; AFL_CMP_MAP_RTN_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)] #[derive(Debug, Clone, Copy)]
#[repr(C, packed)] #[repr(C, packed)]
/// Comparison map compatible with AFL++ cmplog instrumentation
pub struct AFLppCmpMap { pub struct AFLppCmpMap {
headers: [AFLppCmpHeader; AFL_CMP_MAP_W], headers: [AFLppCmpHeader; AFL_CMP_MAP_W],
vals: AFLppCmpVals, 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 { impl Serialize for AFLppCmpMap {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where where

View File

@ -12,7 +12,7 @@ use crate::{
executors::{Executor, HasObservers, ShadowExecutor}, executors::{Executor, HasObservers, ShadowExecutor},
inputs::{BytesInput, UsesInput}, inputs::{BytesInput, UsesInput},
mark_feature_time, mark_feature_time,
observers::{AFLppForkserverCmpObserver, ObserversTuple}, observers::{AFLppCmpObserver, ObserversTuple},
stages::{colorization::TaintMetadata, Stage}, stages::{colorization::TaintMetadata, Stage},
start_timer, start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, State, UsesState}, state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, State, UsesState},
@ -145,13 +145,13 @@ where
if let Some(ob) = self if let Some(ob) = self
.tracer_executor .tracer_executor
.observers_mut() .observers_mut()
.match_name_mut::<AFLppForkserverCmpObserver<TE::State>>(name) .match_name_mut::<AFLppCmpObserver<TE::State>>(name)
{ {
// This is not the original input, // This is not the original input,
// Set it to false // Set it to false
ob.set_original(true); 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 // but do nothing ofcourse
} }
@ -179,13 +179,13 @@ where
if let Some(ob) = self if let Some(ob) = self
.tracer_executor .tracer_executor
.observers_mut() .observers_mut()
.match_name_mut::<AFLppForkserverCmpObserver<TE::State>>(name) .match_name_mut::<AFLppCmpObserver<TE::State>>(name)
{ {
// This is not the original input, // This is not the original input,
// Set it to false // Set it to false
ob.set_original(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 // but do nothing ofcourse
} }

View File

@ -8,7 +8,7 @@ use core::fmt::{self, Debug, Formatter};
use libafl::{ use libafl::{
executors::ExitKind, executors::ExitKind,
inputs::UsesInput, inputs::UsesInput,
observers::{CmpMap, CmpObserver, CmpValues, Observer}, observers::{cmp::CmpValuesMetadata, CmpMap, CmpObserver, CmpValues, Observer},
state::HasMetadata, state::HasMetadata,
Error, Error,
}; };
@ -148,8 +148,12 @@ impl CmpMap for CmpLogMap {
fn reset(&mut self) -> Result<(), Error> { fn reset(&mut self) -> Result<(), Error> {
// For performance, we reset just the headers // For performance, we reset just the headers
self.headers = unsafe { core::mem::zeroed() }; self.headers.fill(CmpLogHeader {
// self.vals.operands = unsafe { core::mem::zeroed() }; hits: 0,
shape: 0,
kind: 0,
});
Ok(()) Ok(())
} }
} }
@ -185,7 +189,7 @@ pub struct CmpLogObserver {
name: String, name: String,
} }
impl<S> CmpObserver<CmpLogMap, S> for CmpLogObserver impl<'a, S> CmpObserver<'a, CmpLogMap, S, CmpValuesMetadata> for CmpLogObserver
where where
S: UsesInput + HasMetadata, S: UsesInput + HasMetadata,
{ {
@ -206,10 +210,10 @@ where
} }
} }
impl<S> Observer<S> for CmpLogObserver impl<'a, S> Observer<S> for CmpLogObserver
where where
S: UsesInput + HasMetadata, 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> { fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
self.map.as_mut().reset()?; self.map.as_mut().reset()?;
@ -228,9 +232,11 @@ where
unsafe { unsafe {
CMPLOG_ENABLED = 0; CMPLOG_ENABLED = 0;
} }
if self.add_meta { if self.add_meta {
self.add_cmpvalues_meta(state); self.add_cmpvalues_meta(state);
} }
Ok(()) Ok(())
} }
} }