diff --git a/fuzzers/fuzzbench_forkserver/Cargo.toml b/fuzzers/fuzzbench_forkserver/Cargo.toml index b4946543f4..fd876c04aa 100644 --- a/fuzzers/fuzzbench_forkserver/Cargo.toml +++ b/fuzzers/fuzzbench_forkserver/Cargo.toml @@ -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" diff --git a/fuzzers/fuzzbench_forkserver/src/main.rs b/fuzzers/fuzzbench_forkserver/src/main.rs index 6ccbb2d17f..1bac2f050c 100644 --- a/fuzzers/fuzzbench_forkserver/src/main.rs +++ b/fuzzers/fuzzbench_forkserver/src/main.rs @@ -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::()) + .new_shmem(core::mem::size_of::()) .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::() }; + let cmpmap = unsafe { cmplog_shmem.as_object_mut::() }; let cmplog_observer = StdCmpValuesObserver::new("cmplog", cmpmap, true); diff --git a/fuzzers/fuzzbench_forkserver_cmplog/Cargo.toml b/fuzzers/fuzzbench_forkserver_cmplog/Cargo.toml index a26c02b59b..7a82d7986b 100644 --- a/fuzzers/fuzzbench_forkserver_cmplog/Cargo.toml +++ b/fuzzers/fuzzbench_forkserver_cmplog/Cargo.toml @@ -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" \ No newline at end of file diff --git a/fuzzers/fuzzbench_forkserver_cmplog/src/main.rs b/fuzzers/fuzzbench_forkserver_cmplog/src/main.rs index 8957403e7a..c2f64560ec 100644 --- a/fuzzers/fuzzbench_forkserver_cmplog/src/main.rs +++ b/fuzzers/fuzzbench_forkserver_cmplog/src/main.rs @@ -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::()) + .new_shmem(core::mem::size_of::()) .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::() }; + let cmpmap = unsafe { cmplog_shmem.as_object_mut::() }; - let cmplog_observer = AFLppCmpObserver::new("cmplog", cmpmap, true); + let cmplog_observer = AFLppCmpLogObserver::new("cmplog", cmpmap, true); let cmplog_forkserver = ForkserverExecutor::builder() .program(exec) diff --git a/fuzzers/fuzzbench_forkserver_cmplog/test/compile.sh b/fuzzers/fuzzbench_forkserver_cmplog/test/compile.sh new file mode 100755 index 0000000000..c58d3101df --- /dev/null +++ b/fuzzers/fuzzbench_forkserver_cmplog/test/compile.sh @@ -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 .. diff --git a/fuzzers/fuzzbench_forkserver_cmplog/test/test-cmplog.c b/fuzzers/fuzzbench_forkserver_cmplog/test/test-cmplog.c new file mode 100644 index 0000000000..950a6b0ee6 --- /dev/null +++ b/fuzzers/fuzzbench_forkserver_cmplog/test/test-cmplog.c @@ -0,0 +1,37 @@ +#include +#include +#include +#include +#include +#include +#include + +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; + +} + diff --git a/libafl/src/observers/cmp.rs b/libafl/src/observers/cmp.rs index fd8a4f83de..746f33ea6e 100644 --- a/libafl/src/observers/cmp.rs +++ b/libafl/src/observers/cmp.rs @@ -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>, - name: String, - add_meta: bool, - original: >::Data, - phantom: PhantomData, -} - -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, - ) -> >::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::() - { - meta - } else { - state.add_metadata(AFLppCmpValuesMetadata::new()); - state - .metadata_map_mut() - .get_mut::() - .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 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>, - /// 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>, /// 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 = 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 { - unsafe { Box::from_raw(alloc_zeroed(Layout::new::()) 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(&self, serializer: S) -> Result - where - S: Serializer, - { - let slice = unsafe { - core::slice::from_raw_parts( - (self as *const Self) as *const u8, - core::mem::size_of::(), - ) - }; - serializer.serialize_bytes(slice) - } -} - -impl<'de> Deserialize<'de> for AFLppCmpMap { - fn deserialize(deserializer: D) -> Result - where - D: Deserializer<'de>, - { - let bytes = Vec::::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 { - 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], } diff --git a/libafl/src/stages/tracing.rs b/libafl/src/stages/tracing.rs index 1a248bf1c1..0dcf99012b 100644 --- a/libafl/src/stages/tracing.rs +++ b/libafl/src/stages/tracing.rs @@ -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 TracingStage { &mut self.tracer_executor } } - -/// Trace with tainted input -#[derive(Clone, Debug)] -pub struct AFLppCmplogTracingStage { - tracer_executor: TE, - cmplog_observer_name: Option, - #[allow(clippy::type_complexity)] - phantom: PhantomData<(EM, TE, Z)>, -} - -impl UsesState for AFLppCmplogTracingStage -where - TE: UsesState, -{ - type State = TE::State; -} - -impl Stage for AFLppCmplogTracingStage -where - E: UsesState, - TE: Executor + HasObservers, - TE::State: HasClientPerfMonitor - + HasExecutions - + HasCorpus - + HasMetadata - + UsesInput, - EM: UsesState, - Z: UsesState, -{ - #[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::>(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::() { - 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::>(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 AFLppCmplogTracingStage { - /// 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 { diff --git a/libafl_qemu/src/cmplog.rs b/libafl_qemu/src/cmplog.rs index e6df0c2a4a..f2716a09e7 100644 --- a/libafl_qemu/src/cmplog.rs +++ b/libafl_qemu/src/cmplog.rs @@ -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}; diff --git a/libafl_targets/build.rs b/libafl_targets/build.rs index 6d2415998e..e131a2cb73 100644 --- a/libafl_targets/build.rs +++ b/libafl_targets/build.rs @@ -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")) diff --git a/libafl_targets/src/cmplog.c b/libafl_targets/src/cmplog.c index af825f9514..e56e91972d 100644 --- a/libafl_targets/src/cmplog.c +++ b/libafl_targets/src/cmplog.c @@ -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. diff --git a/libafl_targets/src/cmplog.h b/libafl_targets/src/cmplog.h index d8cbcb1b88..72eb8bb86c 100644 --- a/libafl_targets/src/cmplog.h +++ b/libafl_targets/src/cmplog.h @@ -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 diff --git a/libafl_targets/src/cmplog.rs b/libafl_targets/src/cmplog.rs deleted file mode 100644 index ba914aea4d..0000000000 --- a/libafl_targets/src/cmplog.rs +++ /dev/null @@ -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::()) - / core::mem::size_of::(); - -/// `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 { - 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, - size: Option>, - 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 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 -} diff --git a/libafl_targets/src/cmps/mod.rs b/libafl_targets/src/cmps/mod.rs new file mode 100644 index 0000000000..5f13908ec4 --- /dev/null +++ b/libafl_targets/src/cmps/mod.rs @@ -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::()) + / core::mem::size_of::(); + +/// `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 { + 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 { + unsafe { + Box::from_raw(alloc_zeroed(Layout::new::()) 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(&self, serializer: S) -> Result + where + S: Serializer, + { + let slice = unsafe { + core::slice::from_raw_parts( + (self as *const Self) as *const u8, + core::mem::size_of::(), + ) + }; + serializer.serialize_bytes(slice) + } +} + +impl<'de> Deserialize<'de> for AFLppCmpLogMap { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + let bytes = Vec::::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 { + 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(()) + } +} diff --git a/libafl_targets/src/cmps/observers/aflpp.rs b/libafl_targets/src/cmps/observers/aflpp.rs new file mode 100644 index 0000000000..93701b9304 --- /dev/null +++ b/libafl_targets/src/cmps/observers/aflpp.rs @@ -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>, + name: String, + add_meta: bool, + original: >::Data, + phantom: PhantomData, +} + +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, + ) -> >::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::() + { + meta + } else { + state.add_metadata(AFLppCmpValuesMetadata::new()); + state + .metadata_map_mut() + .get_mut::() + .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 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 = 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); + } + } + } + } +} diff --git a/libafl_targets/src/cmps/observers/cmplog.rs b/libafl_targets/src/cmps/observers/cmplog.rs new file mode 100644 index 0000000000..82b8f686fc --- /dev/null +++ b/libafl_targets/src/cmps/observers/cmplog.rs @@ -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, + size: Option>, + 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 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 +} diff --git a/libafl_targets/src/cmps/observers/mod.rs b/libafl_targets/src/cmps/observers/mod.rs new file mode 100644 index 0000000000..a960efbaf3 --- /dev/null +++ b/libafl_targets/src/cmps/observers/mod.rs @@ -0,0 +1,6 @@ +pub mod cmplog; +pub use cmplog::*; + +/// aflpp style cmplog observer +pub mod aflpp; +pub use aflpp::*; diff --git a/libafl_targets/src/cmps/stages/aflpptracing.rs b/libafl_targets/src/cmps/stages/aflpptracing.rs new file mode 100644 index 0000000000..f293554dcb --- /dev/null +++ b/libafl_targets/src/cmps/stages/aflpptracing.rs @@ -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 { + tracer_executor: TE, + cmplog_observer_name: Option, + #[allow(clippy::type_complexity)] + phantom: PhantomData<(EM, TE, Z)>, +} + +impl UsesState for AFLppCmplogTracingStage +where + TE: UsesState, +{ + type State = TE::State; +} + +impl Stage for AFLppCmplogTracingStage +where + E: UsesState, + TE: Executor + HasObservers, + TE::State: HasClientPerfMonitor + + HasExecutions + + HasCorpus + + HasMetadata + + UsesInput, + EM: UsesState, + Z: UsesState, +{ + #[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::>(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::() { + 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::>(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 AFLppCmplogTracingStage { + /// 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 + } +} diff --git a/libafl_targets/src/cmps/stages/mod.rs b/libafl_targets/src/cmps/stages/mod.rs new file mode 100644 index 0000000000..f74c027de3 --- /dev/null +++ b/libafl_targets/src/cmps/stages/mod.rs @@ -0,0 +1,3 @@ +/// cmplog tracing for aflpp style cmplog +pub mod aflpptracing; +pub use aflpptracing::*; diff --git a/libafl_targets/src/lib.rs b/libafl_targets/src/lib.rs index 7e6467b03d..546a99d2cb 100644 --- a/libafl_targets/src/lib.rs +++ b/libafl_targets/src/lib.rs @@ -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; diff --git a/libafl_targets/src/sancov_cmp.c b/libafl_targets/src/sancov_cmp.c index 35304153a1..a00a954e02 100644 --- a/libafl_targets/src/sancov_cmp.c +++ b/libafl_targets/src/sancov_cmp.c @@ -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 } }