use alloc::{borrow::Cow, vec::Vec};
use core::{
fmt::Debug,
ops::{Deref, DerefMut},
};
use arbitrary_int::{u1, u4, u5, u6};
use bitbybit::bitfield;
use hashbrown::HashMap;
use libafl_bolts::{AsSlice, HasLen, Named, ownedref::OwnedRefMut};
use serde::{Deserialize, Serialize};
use crate::{Error, HasMetadata, executors::ExitKind, observers::Observer};
#[derive(Debug, Copy, Clone, Serialize, Deserialize, Eq, PartialEq)]
pub struct CmplogBytes {
buf: [u8; 32],
len: u8,
}
impl CmplogBytes {
#[must_use]
pub fn from_buf_and_len(buf: [u8; 32], len: u8) -> Self {
debug_assert!(len <= 32, "Len too big: {len}, max: 32");
CmplogBytes { buf, len }
}
}
impl<'a> AsSlice<'a> for CmplogBytes {
type Entry = u8;
type SliceRef = &'a [u8];
fn as_slice(&'a self) -> Self::SliceRef {
&self.buf[0..(self.len as usize)]
}
}
impl HasLen for CmplogBytes {
fn len(&self) -> usize {
self.len as usize
}
}
#[derive(Eq, PartialEq, Debug, Serialize, Deserialize, Clone)]
pub enum CmpValues {
U8((u8, u8, bool)),
U16((u16, u16, bool)),
U32((u32, u32, bool)),
U64((u64, u64, bool)),
Bytes((CmplogBytes, CmplogBytes)),
}
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, bool)> {
match self {
CmpValues::U8(t) => Some((u64::from(t.0), u64::from(t.1), t.2)),
CmpValues::U16(t) => Some((u64::from(t.0), u64::from(t.1), t.2)),
CmpValues::U32(t) => Some((u64::from(t.0), u64::from(t.1), t.2)),
CmpValues::U64(t) => Some(*t),
CmpValues::Bytes(_) => None,
}
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[cfg_attr(
any(not(feature = "serdeany_autoreg"), miri),
expect(clippy::unsafe_derive_deserialize)
)] pub struct CmpValuesMetadata {
#[serde(skip)]
pub list: Vec<CmpValues>,
}
libafl_bolts::impl_serdeany!(CmpValuesMetadata);
impl Deref for CmpValuesMetadata {
type Target = [CmpValues];
fn deref(&self) -> &[CmpValues] {
&self.list
}
}
impl DerefMut for CmpValuesMetadata {
fn deref_mut(&mut self) -> &mut [CmpValues] {
&mut self.list
}
}
impl CmpValuesMetadata {
#[must_use]
pub fn new() -> Self {
Self { list: vec![] }
}
pub fn add_from<CM>(&mut self, usable_count: usize, cmp_map: &mut CM)
where
CM: CmpMap,
{
self.list.clear();
let count = usable_count;
for i in 0..count {
let execs = 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) = 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) = cmp_map.values_of(i, j) {
self.list.push(val);
}
}
}
}
}
}
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 {
type Map;
fn usable_count(&self) -> usize;
fn cmp_map(&self) -> &Self::Map;
fn cmp_map_mut(&mut self) -> &mut Self::Map;
}
#[derive(Serialize, Deserialize, Debug)]
#[serde(bound = "CM: serde::de::DeserializeOwned + Serialize")]
pub struct StdCmpObserver<'a, CM> {
cmp_map: OwnedRefMut<'a, CM>,
size: Option<OwnedRefMut<'a, usize>>,
name: Cow<'static, str>,
add_meta: bool,
}
impl<CM> CmpObserver for StdCmpObserver<'_, CM>
where
CM: HasLen,
{
type Map = CM;
fn usable_count(&self) -> usize {
match &self.size {
None => self.cmp_map.as_ref().len(),
Some(o) => *o.as_ref(),
}
}
fn cmp_map(&self) -> &Self::Map {
self.cmp_map.as_ref()
}
fn cmp_map_mut(&mut self) -> &mut Self::Map {
self.cmp_map.as_mut()
}
}
impl<CM, I, S> Observer<I, S> for StdCmpObserver<'_, CM>
where
CM: Serialize + CmpMap + HasLen,
S: HasMetadata,
{
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
self.cmp_map.as_mut().reset()?;
Ok(())
}
fn post_exec(&mut self, state: &mut S, _input: &I, _exit_kind: &ExitKind) -> Result<(), Error> {
if self.add_meta {
let meta = state.metadata_or_insert_with(CmpValuesMetadata::new);
meta.add_from(self.usable_count(), self.cmp_map_mut());
}
Ok(())
}
}
impl<CM> Named for StdCmpObserver<'_, CM> {
fn name(&self) -> &Cow<'static, str> {
&self.name
}
}
impl<'a, CM> StdCmpObserver<'a, CM>
where
CM: CmpMap,
{
#[must_use]
pub fn new(name: &'static str, map: OwnedRefMut<'a, CM>, add_meta: bool) -> Self {
Self {
name: Cow::from(name),
size: None,
cmp_map: map,
add_meta,
}
}
#[must_use]
pub fn with_size(
name: &'static str,
cmp_map: OwnedRefMut<'a, CM>,
add_meta: bool,
size: OwnedRefMut<'a, usize>,
) -> Self {
Self {
name: Cow::from(name),
size: Some(size),
cmp_map,
add_meta,
}
}
}
#[derive(Debug, Default, Serialize, Deserialize)]
#[cfg_attr(
any(not(feature = "serdeany_autoreg"), miri),
expect(clippy::unsafe_derive_deserialize)
)] pub struct AflppCmpValuesMetadata {
#[serde(skip)]
pub orig_cmpvals: HashMap<usize, Vec<CmpValues>>,
#[serde(skip)]
pub new_cmpvals: HashMap<usize, Vec<CmpValues>>,
#[serde(skip)]
pub headers: Vec<(usize, AflppCmpLogHeader)>,
}
libafl_bolts::impl_serdeany!(AflppCmpValuesMetadata);
impl AflppCmpValuesMetadata {
#[must_use]
pub fn new() -> Self {
Self {
orig_cmpvals: HashMap::new(),
new_cmpvals: HashMap::new(),
headers: Vec::new(),
}
}
#[must_use]
pub fn orig_cmpvals(&self) -> &HashMap<usize, Vec<CmpValues>> {
&self.orig_cmpvals
}
#[must_use]
pub fn new_cmpvals(&self) -> &HashMap<usize, Vec<CmpValues>> {
&self.new_cmpvals
}
#[must_use]
pub fn headers(&self) -> &Vec<(usize, AflppCmpLogHeader)> {
&self.headers
}
}
#[bitfield(u16)]
#[derive(Debug)]
pub struct AflppCmpLogHeader {
#[bits(0..=5, r)]
hits: u6,
#[bits(6..=10, r)]
shape: u5,
#[bit(11, r)]
type_: u1,
#[bits(12..=15, r)]
attribute: u4,
}