Refactor cmplog observers (#1603)

* refactor

* Rename Everything

* fmt

* chg

* test

* aa

* doc fix

* fix?

* doc
This commit is contained in:
Dongjia "toka" Zhang 2023-10-06 15:22:11 +02:00 committed by GitHub
parent 0bba8535b8
commit bc91436ef4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 1260 additions and 1066 deletions

View File

@ -22,5 +22,6 @@ which = "4.4"
[dependencies]
libafl = { path = "../../libafl/" }
libafl_bolts = { path = "../../libafl_bolts/" }
libafl_targets = { path = "../../libafl_targets/" }
clap = { version = "4.0", features = ["default"] }
nix = "0.26"

View File

@ -21,9 +21,7 @@ use libafl::{
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
StdMOptMutator, StdScheduledMutator, Tokens,
},
observers::{
AFLppCmpMap, HitcountsMapObserver, StdCmpValuesObserver, StdMapObserver, TimeObserver,
},
observers::{HitcountsMapObserver, StdCmpValuesObserver, StdMapObserver, TimeObserver},
schedulers::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
},
@ -41,6 +39,7 @@ use libafl_bolts::{
tuples::{tuple_list, Merge},
AsMutSlice,
};
use libafl_targets::cmps::AFLppCmpLogMap;
use nix::sys::signal::Signal;
pub fn main() {
@ -342,11 +341,11 @@ fn fuzz(
if let Some(exec) = &cmplog_exec {
// The cmplog map shared between observer and executor
let mut cmplog_shmem = shmem_provider
.new_shmem(core::mem::size_of::<AFLppCmpMap>())
.new_shmem(core::mem::size_of::<AFLppCmpLogMap>())
.unwrap();
// let the forkserver know the shmid
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);

View File

@ -22,5 +22,6 @@ which = "4.4"
[dependencies]
libafl = { path = "../../libafl/" }
libafl_bolts = { path = "../../libafl_bolts/" }
libafl_targets = { path = "../../libafl_targets/" }
clap = { version = "4.0", features = ["default"] }
nix = "0.26"

View File

@ -21,16 +21,13 @@ use libafl::{
scheduled::havoc_mutations, token_mutations::AFLppRedQueen, tokens_mutations,
StdMOptMutator, Tokens,
},
observers::{
AFLppCmpMap, AFLppCmpObserver, HitcountsMapObserver, StdMapObserver, TimeObserver,
},
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
schedulers::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
},
stages::{
calibrate::CalibrationStage, mutational::MultiMutationalStage,
power::StdPowerMutationalStage, tracing::AFLppCmplogTracingStage, ColorizationStage,
IfStage,
power::StdPowerMutationalStage, ColorizationStage, IfStage,
},
state::{HasCorpus, HasMetadata, StdState},
Error,
@ -42,6 +39,10 @@ use libafl_bolts::{
tuples::{tuple_list, Merge},
AsMutSlice,
};
use libafl_targets::{
cmps::{observers::AFLppCmpLogObserver, stages::AFLppCmplogTracingStage},
AFLppCmpLogMap,
};
use nix::sys::signal::Signal;
pub fn main() {
@ -344,13 +345,13 @@ fn fuzz(
if let Some(exec) = &cmplog_exec {
// The cmplog map shared between observer and executor
let mut cmplog_shmem = shmem_provider
.new_shmem(core::mem::size_of::<AFLppCmpMap>())
.new_shmem(core::mem::size_of::<AFLppCmpLogMap>())
.unwrap();
// let the forkserver know the shmid
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()
.program(exec)

View 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 ..

View 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;
}

View File

@ -1,17 +1,15 @@
//! 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::{alloc::Layout, fmt::Debug, marker::PhantomData};
use core::{fmt::Debug, marker::PhantomData};
use c2rust_bitfields::BitfieldStruct;
use hashbrown::HashMap;
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::{
executors::ExitKind, inputs::UsesInput, observers::Observer, state::HasMetadata, Error,
@ -24,7 +22,7 @@ where
CM: CmpMap + Debug,
{
/// 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;
/// 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
/// 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);
}
@ -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.
#[derive(Debug, Default, Serialize, Deserialize)]
#[cfg_attr(
@ -613,15 +463,15 @@ where
allow(clippy::unsafe_derive_deserialize)
)] // for SerdeAny
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)]
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)]
pub new_cmpvals: HashMap<usize, Vec<CmpValues>>,
/// The list of logged idx and headers retrieved by runnning the mutated input
#[serde(skip)]
pub headers: Vec<(usize, AFLppCmpHeader)>,
pub headers: Vec<(usize, AFLppCmpLogHeader)>,
}
libafl_bolts::impl_serdeany!(AFLppCmpValuesMetadata);
@ -651,113 +501,11 @@ impl AFLppCmpValuesMetadata {
/// Getter for `headers`
#[must_use]
pub fn headers(&self) -> &Vec<(usize, AFLppCmpHeader)> {
pub fn headers(&self) -> &Vec<(usize, AFLppCmpLogHeader)> {
&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)]
#[repr(C, packed)]
/// 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
/// - overflow: Whether the comparison overflows
/// - reserved: Reserved for future use
pub struct AFLppCmpHeader {
pub struct AFLppCmpLogHeader {
/// The header values
#[bitfield(name = "hits", ty = "u32", bits = "0..=23")]
#[bitfield(name = "id", ty = "u32", bits = "24..=47")]
#[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 = "overflow", ty = "u32", bits = "59..=59")]
#[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,
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(())
}
pub data: [u8; 8],
}

View File

@ -1,21 +1,17 @@
//! 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 libafl_bolts::tuples::MatchName;
#[cfg(feature = "introspection")]
use crate::monitors::PerfFeature;
use crate::{
corpus::{Corpus, CorpusId},
executors::{Executor, HasObservers, ShadowExecutor},
inputs::{BytesInput, UsesInput},
mark_feature_time,
observers::{AFLppCmpObserver, ObserversTuple},
stages::{colorization::TaintMetadata, Stage},
observers::ObserversTuple,
stages::Stage,
start_timer,
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, HasMetadata, State, UsesState},
state::{HasClientPerfMonitor, HasCorpus, HasExecutions, State, UsesState},
Error,
};
@ -99,144 +95,6 @@ impl<EM, TE, Z> TracingStage<EM, TE, Z> {
&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
#[derive(Clone, Debug)]
pub struct ShadowTracingStage<E, EM, SOT, Z> {

View File

@ -1,7 +1,7 @@
use hashbrown::HashMap;
use libafl::{inputs::UsesInput, state::HasMetadata};
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,
};
use serde::{Deserialize, Serialize};

View File

@ -18,6 +18,12 @@ fn main() {
let cmp_map_size: usize = option_env!("LIBAFL_CMP_MAP_SIZE")
.map_or(Ok(65536), str::parse)
.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")
.map_or(Ok(65536), str::parse)
.expect("Could not parse LIBAFL_CMPLOG_MAP_W");
@ -36,6 +42,10 @@ fn main() {
pub const EDGES_MAP_SIZE: usize = {edges_map_size};
/// The size of the cmps map
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
pub const CMPLOG_MAP_W: usize = {cmplog_map_w};
/// 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_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_H");
println!("cargo:rerun-if-env-changed=LIBAFL_ACCOUNTING_MAP_SIZE");
@ -80,6 +92,14 @@ fn main() {
sancov_cmp
.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_H", Some(&*format!("{cmplog_map_h}")))
.file(src_dir.join("sancov_cmp.c"))
@ -158,6 +178,15 @@ fn main() {
cc::Build::new()
.flag("-Wno-pointer-sign") // UNIX ONLY FLAGS
.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_H", Some(&*format!("{cmplog_map_h}")))
.file(src_dir.join("cmplog.c"))
@ -167,6 +196,15 @@ fn main() {
#[cfg(not(unix))]
{
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_H", Some(&*format!("{cmplog_map_h}")))
.file(src_dir.join("cmplog.c"))

View File

@ -40,11 +40,27 @@ __attribute__((weak)) void *__asan_region_is_poisoned(const void *beg,
CmpLogMap *libafl_cmplog_map_ptr = &libafl_cmplog_map;
void __libafl_targets_cmplog_instructions(uintptr_t k, uint8_t shape,
uint64_t arg1, uint64_t arg2) {
STATIC_ASSERT(sizeof(libafl_cmplog_map_ptr->vals.operands) ==
sizeof(libafl_cmplog_map_ptr->vals.routines));
uint64_t arg1, uint64_t arg2) {
if (!libafl_cmplog_enabled) { return; }
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.

View File

@ -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,
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

View File

@ -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
}

View 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(())
}
}

View 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);
}
}
}
}
}

View 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
}

View File

@ -0,0 +1,6 @@
pub mod cmplog;
pub use cmplog::*;
/// aflpp style cmplog observer
pub mod aflpp;
pub use aflpp::*;

View 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
}
}

View File

@ -0,0 +1,3 @@
/// cmplog tracing for aflpp style cmplog
pub mod aflpptracing;
pub use aflpptracing::*;

View File

@ -108,8 +108,9 @@ pub use coverage::*;
pub mod value_profile;
pub use value_profile::*;
pub mod cmplog;
pub use cmplog::*;
/// runtime related to comparisons
pub mod cmps;
pub use cmps::*;
#[cfg(feature = "std")]
pub mod drcov;

View File

@ -19,7 +19,7 @@ void __sanitizer_cov_trace_cmp1(uint8_t arg1, uint8_t arg2) {
#endif
#ifdef SANCOV_CMPLOG
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
}
@ -33,7 +33,7 @@ void __sanitizer_cov_trace_cmp2(uint16_t arg1, uint16_t arg2) {
#endif
#ifdef SANCOV_CMPLOG
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
}
@ -47,7 +47,7 @@ void __sanitizer_cov_trace_cmp4(uint32_t arg1, uint32_t arg2) {
#endif
#ifdef SANCOV_CMPLOG
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
}
@ -61,7 +61,7 @@ void __sanitizer_cov_trace_cmp8(uint64_t arg1, uint64_t arg2) {
#endif
#ifdef SANCOV_CMPLOG
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
}
@ -95,7 +95,7 @@ void __sanitizer_cov_trace_switch(uint64_t val, uint64_t *cases) {
#endif
#ifdef SANCOV_CMPLOG
k &= CMPLOG_MAP_W - 1;
__libafl_targets_cmplog(k, cases[1] / 8, val, cases[i + 2]);
__libafl_targets_cmplog_instructions(k, cases[1] / 8, val, cases[i + 2]);
#endif
}
}