More usable ListFeedback (#1959)
* real list feedback * add * fox * obs * fix * FMT * more * fmt * rev
This commit is contained in:
parent
7abc26ebc9
commit
15f4613894
@ -40,7 +40,7 @@ fn main() {
|
||||
// let args = vec!["test.exe".to_string(), "-f".to_string(), "@@".to_string()];
|
||||
|
||||
let observer = unsafe { ListObserver::new("cov", &mut COVERAGE) };
|
||||
let mut feedback = ListFeedback::with_observer(&observer);
|
||||
let mut feedback = ListFeedback::new(&observer);
|
||||
#[cfg(windows)]
|
||||
let mut shmem_provider = Win32ShMemProvider::new().unwrap();
|
||||
|
||||
|
172
libafl/src/feedbacks/list.rs
Normal file
172
libafl/src/feedbacks/list.rs
Normal file
@ -0,0 +1,172 @@
|
||||
use alloc::string::{String, ToString};
|
||||
use core::{fmt::Debug, hash::Hash, marker::PhantomData};
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use libafl_bolts::{Error, HasRefCnt, Named};
|
||||
use serde::{de::DeserializeOwned, Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
events::EventFirer,
|
||||
executors::ExitKind,
|
||||
feedbacks::Feedback,
|
||||
observers::{ListObserver, ObserversTuple},
|
||||
state::{HasNamedMetadata, State},
|
||||
};
|
||||
|
||||
/// The metadata to remember past observed value
|
||||
#[derive(Default, Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(bound = "T: DeserializeOwned")]
|
||||
#[cfg_attr(
|
||||
any(not(feature = "serdeany_autoreg"), miri),
|
||||
allow(clippy::unsafe_derive_deserialize)
|
||||
)]
|
||||
pub struct ListFeedbackMetadata<T>
|
||||
where
|
||||
T: Default + Copy + 'static + Serialize + Eq + Hash,
|
||||
{
|
||||
/// Contains the information of past observed set of values.
|
||||
pub set: HashSet<T>,
|
||||
/// A refcount used to know when we can remove this metadata
|
||||
pub tcref: isize,
|
||||
}
|
||||
|
||||
impl<T> ListFeedbackMetadata<T>
|
||||
where
|
||||
T: Default + Copy + 'static + Serialize + Eq + Hash,
|
||||
{
|
||||
/// The constructor
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
set: HashSet::<T>::new(),
|
||||
tcref: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Reset the inner hashset
|
||||
pub fn reset(&mut self) -> Result<(), Error> {
|
||||
self.set.clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> HasRefCnt for ListFeedbackMetadata<T>
|
||||
where
|
||||
T: Default + Copy + 'static + Serialize + Eq + Hash,
|
||||
{
|
||||
fn refcnt(&self) -> isize {
|
||||
self.tcref
|
||||
}
|
||||
|
||||
fn refcnt_mut(&mut self) -> &mut isize {
|
||||
&mut self.tcref
|
||||
}
|
||||
}
|
||||
|
||||
/// Consider interesting a testcase if the list in `ListObserver` is not empty.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ListFeedback<T>
|
||||
where
|
||||
T: Hash + Eq,
|
||||
{
|
||||
name: String,
|
||||
observer_name: String,
|
||||
novelty: HashSet<T>,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
libafl_bolts::impl_serdeany!(
|
||||
ListFeedbackMetadata<T: Debug + Default + Copy + 'static + Serialize + DeserializeOwned + Eq + Hash>,
|
||||
<u8>,<u16>,<u32>,<u64>,<i8>,<i16>,<i32>,<i64>,<bool>,<char>,<usize>
|
||||
);
|
||||
|
||||
impl<S, T> Feedback<S> for ListFeedback<T>
|
||||
where
|
||||
S: State + HasNamedMetadata,
|
||||
T: Debug + Serialize + Hash + Eq + DeserializeOwned + Default + Copy + 'static,
|
||||
{
|
||||
fn init_state(&mut self, state: &mut S) -> Result<(), Error> {
|
||||
// eprintln!("self.name {:#?}", &self.name);
|
||||
state.add_named_metadata(&self.name, ListFeedbackMetadata::<T>::default());
|
||||
Ok(())
|
||||
}
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &S::Input,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<S>,
|
||||
{
|
||||
// TODO Replace with match_name_type when stable
|
||||
let observer = observers
|
||||
.match_name::<ListObserver<T>>(&self.observer_name)
|
||||
.unwrap();
|
||||
// TODO register the list content in a testcase metadata
|
||||
self.novelty.clear();
|
||||
// can't fail
|
||||
let history_set = state
|
||||
.named_metadata_map_mut()
|
||||
.get_mut::<ListFeedbackMetadata<T>>(&self.name)
|
||||
.unwrap();
|
||||
for v in observer.list() {
|
||||
if !history_set.set.contains(v) {
|
||||
self.novelty.insert(*v);
|
||||
}
|
||||
}
|
||||
Ok(!self.novelty.is_empty())
|
||||
}
|
||||
|
||||
fn append_metadata<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_observers: &OT,
|
||||
_testcase: &mut crate::corpus::Testcase<<S>::Input>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
OT: ObserversTuple<S>,
|
||||
EM: EventFirer<State = S>,
|
||||
{
|
||||
let history_set = state
|
||||
.named_metadata_map_mut()
|
||||
.get_mut::<ListFeedbackMetadata<T>>(&self.name)
|
||||
.unwrap();
|
||||
|
||||
for v in &self.novelty {
|
||||
history_set.set.insert(*v);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Named for ListFeedback<T>
|
||||
where
|
||||
T: Debug + Serialize + Hash + Eq + DeserializeOwned,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ListFeedback<T>
|
||||
where
|
||||
T: Debug + Serialize + Hash + Eq + DeserializeOwned,
|
||||
{
|
||||
/// Creates a new [`ListFeedback`], deciding if the given [`ListObserver`] value of a run is interesting.
|
||||
#[must_use]
|
||||
pub fn new(observer: &ListObserver<T>) -> Self {
|
||||
Self {
|
||||
name: observer.name().to_string(),
|
||||
observer_name: observer.name().to_string(),
|
||||
novelty: HashSet::<T>::new(),
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
@ -28,9 +28,6 @@ use crate::{
|
||||
Error,
|
||||
};
|
||||
|
||||
/// The prefix of the metadata names
|
||||
pub const MAPFEEDBACK_PREFIX: &str = "mapfeedback_metadata_";
|
||||
|
||||
/// A [`MapFeedback`] that implements the AFL algorithm using an [`OrReducer`] combining the bits for the history map and the bit from ``HitcountsMapObserver``.
|
||||
pub type AflMapFeedback<O, S, T> = MapFeedback<DifferentIsNovel, O, OrReducer, S, T>;
|
||||
|
||||
@ -700,7 +697,7 @@ where
|
||||
Self {
|
||||
indexes: false,
|
||||
novelties: None,
|
||||
name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(),
|
||||
name: map_observer.name().to_string(),
|
||||
observer_name: map_observer.name().to_string(),
|
||||
stats_name: create_stats_name(map_observer.name()),
|
||||
phantom: PhantomData,
|
||||
@ -713,7 +710,7 @@ where
|
||||
Self {
|
||||
indexes: track_indexes,
|
||||
novelties: if track_novelties { Some(vec![]) } else { None },
|
||||
name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(),
|
||||
name: map_observer.name().to_string(),
|
||||
observer_name: map_observer.name().to_string(),
|
||||
stats_name: create_stats_name(map_observer.name()),
|
||||
phantom: PhantomData,
|
||||
|
@ -25,6 +25,8 @@ pub use new_hash_feedback::NewHashFeedbackMetadata;
|
||||
pub mod nautilus;
|
||||
pub mod transferred;
|
||||
|
||||
/// The module for list feedback
|
||||
pub mod list;
|
||||
use alloc::string::{String, ToString};
|
||||
use core::{
|
||||
fmt::{self, Debug, Formatter},
|
||||
@ -32,6 +34,7 @@ use core::{
|
||||
};
|
||||
|
||||
use libafl_bolts::Named;
|
||||
pub use list::*;
|
||||
#[cfg(feature = "nautilus")]
|
||||
pub use nautilus::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -40,7 +43,7 @@ use crate::{
|
||||
corpus::Testcase,
|
||||
events::EventFirer,
|
||||
executors::ExitKind,
|
||||
observers::{ListObserver, ObserversTuple, TimeObserver},
|
||||
observers::{ObserversTuple, TimeObserver},
|
||||
state::State,
|
||||
Error,
|
||||
};
|
||||
@ -972,79 +975,6 @@ impl TimeFeedback {
|
||||
}
|
||||
}
|
||||
|
||||
/// Consider interesting a testcase if the list in `ListObserver` is not empty.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ListFeedback<T>
|
||||
where
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
name: String,
|
||||
last_addr: usize,
|
||||
phantom: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<S, T> Feedback<S> for ListFeedback<T>
|
||||
where
|
||||
S: State,
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &S::Input,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<S>,
|
||||
{
|
||||
// TODO Replace with match_name_type when stable
|
||||
let observer = observers
|
||||
.match_name::<ListObserver<T>>(self.name())
|
||||
.unwrap();
|
||||
// TODO register the list content in a testcase metadata
|
||||
Ok(!observer.list().is_empty())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Named for ListFeedback<T>
|
||||
where
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> ListFeedback<T>
|
||||
where
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
/// Creates a new [`ListFeedback`], deciding if the value of a [`ListObserver`] with the given `name` of a run is interesting.
|
||||
#[must_use]
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
last_addr: 0,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`TimeFeedback`], deciding if the given [`ListObserver`] value of a run is interesting.
|
||||
#[must_use]
|
||||
pub fn with_observer(observer: &ListObserver<T>) -> Self {
|
||||
Self {
|
||||
name: observer.name().to_string(),
|
||||
last_addr: 0,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The [`ConstFeedback`] reports the same value, always.
|
||||
/// It can be used to enable or disable feedback results through composition.
|
||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
||||
|
73
libafl/src/observers/list.rs
Normal file
73
libafl/src/observers/list.rs
Normal file
@ -0,0 +1,73 @@
|
||||
use alloc::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::fmt::Debug;
|
||||
|
||||
use libafl_bolts::{ownedref::OwnedMutPtr, Error, Named};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{inputs::UsesInput, observers::Observer};
|
||||
|
||||
/// A simple observer with a list of things.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||
#[allow(clippy::unsafe_derive_deserialize)]
|
||||
pub struct ListObserver<T>
|
||||
where
|
||||
T: Debug + Serialize,
|
||||
{
|
||||
name: String,
|
||||
/// The list
|
||||
list: OwnedMutPtr<Vec<T>>,
|
||||
}
|
||||
|
||||
impl<T> ListObserver<T>
|
||||
where
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
/// Creates a new [`ListObserver`] with the given name.
|
||||
///
|
||||
/// # Safety
|
||||
/// Will dereference the list.
|
||||
/// The list may not move in memory.
|
||||
#[must_use]
|
||||
pub unsafe fn new(name: &'static str, list: *mut Vec<T>) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
list: OwnedMutPtr::Ptr(list),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a list ref
|
||||
#[must_use]
|
||||
pub fn list(&self) -> &Vec<T> {
|
||||
self.list.as_ref()
|
||||
}
|
||||
|
||||
/// Get a list mut
|
||||
#[must_use]
|
||||
pub fn list_mut(&mut self) -> &mut Vec<T> {
|
||||
self.list.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> Observer<S> for ListObserver<T>
|
||||
where
|
||||
S: UsesInput,
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||
self.list.as_mut().clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Named for ListObserver<T>
|
||||
where
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
@ -20,17 +20,17 @@ pub mod concolic;
|
||||
|
||||
pub mod value;
|
||||
|
||||
use alloc::{
|
||||
string::{String, ToString},
|
||||
vec::Vec,
|
||||
};
|
||||
/// List observer
|
||||
pub mod list;
|
||||
use alloc::string::{String, ToString};
|
||||
use core::{fmt::Debug, time::Duration};
|
||||
#[cfg(feature = "std")]
|
||||
use std::time::Instant;
|
||||
|
||||
#[cfg(feature = "no_std")]
|
||||
use libafl_bolts::current_time;
|
||||
use libafl_bolts::{ownedref::OwnedMutPtr, tuples::MatchName, Named};
|
||||
use libafl_bolts::{tuples::MatchName, Named};
|
||||
pub use list::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use value::*;
|
||||
|
||||
@ -526,73 +526,11 @@ where
|
||||
{
|
||||
}
|
||||
|
||||
/// A simple observer with a list of things.
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||
#[allow(clippy::unsafe_derive_deserialize)]
|
||||
pub struct ListObserver<T>
|
||||
where
|
||||
T: Debug + Serialize,
|
||||
{
|
||||
name: String,
|
||||
/// The list
|
||||
list: OwnedMutPtr<Vec<T>>,
|
||||
}
|
||||
|
||||
impl<T> ListObserver<T>
|
||||
where
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
/// Creates a new [`ListObserver`] with the given name.
|
||||
///
|
||||
/// # Safety
|
||||
/// Will dereference the list.
|
||||
/// The list may not move in memory.
|
||||
#[must_use]
|
||||
pub unsafe fn new(name: &'static str, list: *mut Vec<T>) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
list: OwnedMutPtr::Ptr(list),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a list ref
|
||||
#[must_use]
|
||||
pub fn list(&self) -> &Vec<T> {
|
||||
self.list.as_ref()
|
||||
}
|
||||
|
||||
/// Get a list mut
|
||||
#[must_use]
|
||||
pub fn list_mut(&mut self) -> &mut Vec<T> {
|
||||
self.list.as_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, T> Observer<S> for ListObserver<T>
|
||||
where
|
||||
S: UsesInput,
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||
self.list.as_mut().clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Named for ListObserver<T>
|
||||
where
|
||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
/// `Observer` Python bindings
|
||||
#[cfg(feature = "python")]
|
||||
#[allow(missing_docs)]
|
||||
pub mod pybind {
|
||||
use alloc::vec::Vec;
|
||||
use core::ptr;
|
||||
use std::cell::UnsafeCell;
|
||||
|
||||
@ -603,7 +541,7 @@ pub mod pybind {
|
||||
use pyo3::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{Debug, Observer, ObserversTuple, String, Vec};
|
||||
use super::{Debug, Observer, ObserversTuple, String};
|
||||
use crate::{
|
||||
executors::{pybind::PythonExitKind, ExitKind},
|
||||
inputs::{BytesInput, HasBytesVec},
|
||||
|
@ -3,7 +3,7 @@ use std::ffi::c_int;
|
||||
use libafl::{
|
||||
events::{ProgressReporter, SimpleEventManager},
|
||||
executors::HasObservers,
|
||||
feedbacks::{MapFeedbackMetadata, MAPFEEDBACK_PREFIX},
|
||||
feedbacks::MapFeedbackMetadata,
|
||||
inputs::UsesInput,
|
||||
monitors::SimpleMonitor,
|
||||
stages::{HasCurrentStage, StagesTuple},
|
||||
@ -35,7 +35,7 @@ where
|
||||
ST: StagesTuple<E, EM, S, F>,
|
||||
{
|
||||
let meta = state
|
||||
.named_metadata::<MapFeedbackMetadata<u8>>(&(MAPFEEDBACK_PREFIX.to_string() + "edges"))
|
||||
.named_metadata::<MapFeedbackMetadata<u8>>("edges")
|
||||
.unwrap();
|
||||
let observed = meta.history_map.iter().filter(|&&e| e != 0).count();
|
||||
let total = meta.history_map.len();
|
||||
|
Loading…
x
Reference in New Issue
Block a user