use alloc::{
string::{String, ToString},
vec::Vec,
};
use core::{fmt::Debug, marker::PhantomData};
use c2rust_bitfields::BitfieldStruct;
use serde::{de::DeserializeOwned, Deserialize, Deserializer, Serialize, Serializer};
use crate::{
bolts::{ownedref::OwnedRefMut, tuples::Named, AsMutSlice, AsSlice},
executors::ExitKind,
inputs::UsesInput,
observers::Observer,
state::HasMetadata,
Error,
};
#[derive(Debug, Serialize, Deserialize)]
pub enum CmpValues {
U8((u8, u8)),
U16((u16, u16)),
U32((u32, u32)),
U64((u64, u64)),
Bytes((Vec<u8>, Vec<u8>)),
}
impl CmpValues {
#[must_use]
pub fn is_numeric(&self) -> bool {
matches!(
self,
CmpValues::U8(_) | CmpValues::U16(_) | CmpValues::U32(_) | CmpValues::U64(_)
)
}
#[must_use]
pub fn to_u64_tuple(&self) -> Option<(u64, u64)> {
match self {
CmpValues::U8(t) => Some((u64::from(t.0), u64::from(t.1))),
CmpValues::U16(t) => Some((u64::from(t.0), u64::from(t.1))),
CmpValues::U32(t) => Some((u64::from(t.0), u64::from(t.1))),
CmpValues::U64(t) => Some(*t),
CmpValues::Bytes(_) => None,
}
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
pub struct CmpValuesMetadata {
#[serde(skip)]
pub list: Vec<CmpValues>,
}
crate::impl_serdeany!(CmpValuesMetadata);
impl AsSlice for CmpValuesMetadata {
type Entry = CmpValues;
#[must_use]
fn as_slice(&self) -> &[CmpValues] {
self.list.as_slice()
}
}
impl AsMutSlice for CmpValuesMetadata {
type Entry = CmpValues;
#[must_use]
fn as_mut_slice(&mut self) -> &mut [CmpValues] {
self.list.as_mut_slice()
}
}
impl CmpValuesMetadata {
#[must_use]
pub fn new() -> Self {
Self { list: vec![] }
}
}
pub trait CmpMap: Debug {
fn len(&self) -> usize;
#[must_use]
fn is_empty(&self) -> bool {
self.len() == 0
}
fn executions_for(&self, idx: usize) -> usize;
fn usable_executions_for(&self, idx: usize) -> usize;
fn values_of(&self, idx: usize, execution: usize) -> Option<CmpValues>;
fn reset(&mut self) -> Result<(), Error>;
}
pub trait CmpObserver<CM, S>: Observer<S>
where
CM: CmpMap,
S: UsesInput,
{
fn usable_count(&self) -> usize;
fn cmp_map(&self) -> &CM;
fn cmp_map_mut(&mut self) -> &mut CM;
fn add_cmpvalues_meta(&mut self, state: &mut S)
where
S: HasMetadata,
{
#[allow(clippy::option_if_let_else)] let meta = if let Some(meta) = state.metadata_mut().get_mut::<CmpValuesMetadata>() {
meta
} else {
state.add_metadata(CmpValuesMetadata::new());
state.metadata_mut().get_mut::<CmpValuesMetadata>().unwrap()
};
meta.list.clear();
let count = self.usable_count();
for i in 0..count {
let execs = self.cmp_map().usable_executions_for(i);
if execs > 0 {
if execs > 4 {
let mut increasing_v0 = 0;
let mut increasing_v1 = 0;
let mut decreasing_v0 = 0;
let mut decreasing_v1 = 0;
let mut last: Option<CmpValues> = None;
for j in 0..execs {
if let Some(val) = self.cmp_map().values_of(i, j) {
if let Some(l) = last.and_then(|x| x.to_u64_tuple()) {
if let Some(v) = val.to_u64_tuple() {
if l.0.wrapping_add(1) == v.0 {
increasing_v0 += 1;
}
if l.1.wrapping_add(1) == v.1 {
increasing_v1 += 1;
}
if l.0.wrapping_sub(1) == v.0 {
decreasing_v0 += 1;
}
if l.1.wrapping_sub(1) == v.1 {
decreasing_v1 += 1;
}
}
}
last = Some(val);
}
}
if increasing_v0 >= execs - 2
|| increasing_v1 >= execs - 2
|| decreasing_v0 >= execs - 2
|| decreasing_v1 >= execs - 2
{
continue;
}
}
for j in 0..execs {
if let Some(val) = self.cmp_map().values_of(i, j) {
meta.list.push(val);
}
}
}
}
}
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(bound = "CM: serde::de::DeserializeOwned")]
pub struct StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize,
S: UsesInput + HasMetadata,
{
cmp_map: OwnedRefMut<'a, CM>,
size: Option<OwnedRefMut<'a, usize>>,
name: String,
add_meta: bool,
phantom: PhantomData<S>,
}
impl<'a, CM, S> CmpObserver<CM, S> for StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput + Debug + HasMetadata,
{
fn usable_count(&self) -> usize {
match &self.size {
None => self.cmp_map.as_ref().len(),
Some(o) => *o.as_ref(),
}
}
fn cmp_map(&self) -> &CM {
self.cmp_map.as_ref()
}
fn cmp_map_mut(&mut self) -> &mut CM {
self.cmp_map.as_mut()
}
}
impl<'a, CM, S> Observer<S> for StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize + DeserializeOwned,
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, CM, S> Named for StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput + HasMetadata,
{
fn name(&self) -> &str {
&self.name
}
}
impl<'a, CM, S> StdCmpObserver<'a, CM, S>
where
CM: CmpMap + Serialize + DeserializeOwned,
S: UsesInput + HasMetadata,
{
#[must_use]
pub fn new(name: &'static str, map: &'a mut CM, add_meta: bool) -> Self {
Self {
name: name.to_string(),
size: None,
cmp_map: OwnedRefMut::Ref(map),
add_meta,
phantom: PhantomData,
}
}
#[must_use]
pub fn with_size(
name: &'static str,
map: &'a mut CM,
add_meta: bool,
size: &'a mut usize,
) -> Self {
Self {
name: name.to_string(),
size: Some(OwnedRefMut::Ref(size)),
cmp_map: OwnedRefMut::Ref(map),
add_meta,
phantom: PhantomData,
}
}
}
pub const AFL_CMP_MAP_W: usize = 65536;
pub const AFL_CMP_MAP_H: usize = 32;
pub const AFL_CMP_MAP_RTN_H: usize = AFL_CMP_MAP_H / 4;
pub const AFL_CMP_TYPE_INS: u32 = 1;
pub const AFL_CMP_TYPE_RTN: u32 = 2;
#[derive(Debug, Copy, Clone, BitfieldStruct)]
#[repr(C, packed)]
pub struct AFLCmpHeader {
#[bitfield(name = "hits", ty = "u32", bits = "0..=23")]
#[bitfield(name = "id", ty = "u32", bits = "24..=47")]
#[bitfield(name = "shape", ty = "u32", bits = "48..=52")]
#[bitfield(name = "_type", ty = "u32", bits = "53..=54")]
#[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],
}
#[derive(Default, Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct AFLCmpOperands {
v0: u64,
v1: u64,
v0_128: u64,
v1_128: u64,
}
#[derive(Default, Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct AFLCmpFnOperands {
v0: [u8; 31],
v0_len: u8,
v1: [u8; 31],
v1_len: u8,
}
#[derive(Clone, Copy)]
#[repr(C, packed)]
pub union AFLCmpVals {
operands: [[AFLCmpOperands; AFL_CMP_MAP_H]; AFL_CMP_MAP_W],
fn_operands: [[AFLCmpFnOperands; AFL_CMP_MAP_RTN_H]; AFL_CMP_MAP_W],
}
impl Debug for AFLCmpVals {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("AFLCmpVals").finish_non_exhaustive()
}
}
#[derive(Debug, Clone, Copy)]
#[repr(C, packed)]
pub struct AFLCmpMap {
headers: [AFLCmpHeader; AFL_CMP_MAP_W],
vals: AFLCmpVals,
}
impl Serialize for AFLCmpMap {
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 AFLCmpMap {
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 AFLCmpMap {
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,
))),
_ => None,
}
}
} else {
unsafe {
Some(CmpValues::Bytes((
self.vals.fn_operands[idx][execution].v0
[..=(self.headers[idx].shape() as usize)]
.to_vec(),
self.vals.fn_operands[idx][execution].v1
[..=(self.headers[idx].shape() as usize)]
.to_vec(),
)))
}
}
}
fn reset(&mut self) -> Result<(), Error> {
self.headers = unsafe { core::mem::zeroed() };
Ok(())
}
}