Dominik Maier bb21ab7a63
Autolaunch ShMemService, add MacOS fuzzers to CI, various fixes (#246)
* starting to fix macos linker bugs

* mdetailed error prints

* start shmem service manually

* not a doc comment

* Some fixes

* only send exit msg to shmemservice when start was successful

* incorporated shmem service into provider

* removed unused imports

* trying to fix fuzzers

* fixed build

* check if join_handle is_some

* more debug prints

* fixed shmem service autolaunch

* fixed macos linker

* ignoring broken libpng testcase on macos for now (see #252)

* fmt

* try to fix missing llvm_config (see #253)

* empty issue template added

* Mmanually look for llvm-config on MacOS

* fixing CI

* fixed docs

* ignoring libmozjpg for CI, see #254
2021-08-09 01:10:29 +02:00

251 lines
7.3 KiB
Rust

//! The `CmpObserver` provides access to the logged values of CMP instructions
use alloc::{
string::{String, ToString},
vec::Vec,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
use crate::{
bolts::{ownedref::OwnedRefMut, tuples::Named, AsSlice},
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,
}
}
}
/// A state metadata holding a list of values logged from comparisons
#[derive(Default, Serialize, Deserialize)]
pub struct CmpValuesMetadata {
/// A `list` of values.
#[serde(skip)]
pub list: Vec<CmpValues>,
}
crate::impl_serdeany!(CmpValuesMetadata);
impl AsSlice<CmpValues> for CmpValuesMetadata {
/// Convert to a slice
#[must_use]
fn as_slice(&self) -> &[CmpValues] {
self.list.as_slice()
}
}
impl CmpValuesMetadata {
/// Creates a new [`struct@CmpValuesMetadata`]
#[must_use]
pub fn new() -> Self {
Self { list: vec![] }
}
}
/// A [`CmpMap`] traces comparisons during the current execution
pub trait CmpMap {
/// Get the number of cmps
fn len(&self) -> usize;
/// Get if it is empty
#[must_use]
fn is_empty(&self) -> bool {
self.len() == 0
}
// Get the number of executions for a cmp
fn executions_for(&self, idx: usize) -> usize;
// Get the number of logged executions for a cmp
fn usable_executions_for(&self, idx: usize) -> usize;
// Get the logged values for a cmp
fn values_of(&self, idx: usize, execution: usize) -> CmpValues;
/// Reset the state
fn reset(&mut self) -> Result<(), Error>;
}
/// A [`CmpObserver`] observes the traced comparisons during the current execution using a [`CmpMap`]
pub trait CmpObserver<CM, I, S>: Observer<I, S>
where
CM: CmpMap,
{
/// Get the number of usable cmps (all by default)
fn usable_count(&self) -> usize;
/// Get the `CmpMap`
fn map(&self) -> &CM;
/// Get the `CmpMap` (mut)
fn map_mut(&mut self) -> &mut CM;
/// 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_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.map().usable_executions_for(i);
if execs > 0 {
// 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 {
let val = self.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;
}
}
for j in 0..execs {
meta.list.push(self.map().values_of(i, j));
}
}
}
}
}
/// A standard [`CmpObserver`] observer
#[derive(Serialize, Deserialize, Debug)]
#[serde(bound = "CM: serde::de::DeserializeOwned")]
pub struct StdCmpObserver<'a, CM>
where
CM: CmpMap + Serialize + DeserializeOwned,
{
map: OwnedRefMut<'a, CM>,
size: Option<OwnedRefMut<'a, usize>>,
name: String,
}
impl<'a, CM, I, S> CmpObserver<CM, I, S> for StdCmpObserver<'a, CM>
where
CM: CmpMap + Serialize + DeserializeOwned,
{
/// 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 map(&self) -> &CM {
self.map.as_ref()
}
fn map_mut(&mut self) -> &mut CM {
self.map.as_mut()
}
}
impl<'a, CM, I, S> Observer<I, S> for StdCmpObserver<'a, CM>
where
CM: CmpMap + Serialize + DeserializeOwned,
{
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
self.map.as_mut().reset()?;
Ok(())
}
}
impl<'a, CM> Named for StdCmpObserver<'a, CM>
where
CM: CmpMap + Serialize + DeserializeOwned,
{
fn name(&self) -> &str {
&self.name
}
}
impl<'a, CM> StdCmpObserver<'a, CM>
where
CM: CmpMap + Serialize + DeserializeOwned,
{
/// Creates a new [`StdCmpObserver`] with the given name and map.
#[must_use]
pub fn new(name: &'static str, map: &'a mut CM) -> Self {
Self {
name: name.to_string(),
size: None,
map: OwnedRefMut::Ref(map),
}
}
/// Creates a new [`StdCmpObserver`] with the given name, map and reference to variable size.
#[must_use]
pub fn with_size(name: &'static str, map: &'a mut CM, size: &'a mut usize) -> Self {
Self {
name: name.to_string(),
size: Some(OwnedRefMut::Ref(size)),
map: OwnedRefMut::Ref(map),
}
}
}