Observers lifetime (#89)

* introduce MatchName and alow lifetimes in observers

* adapt fuzzers to observers with lifetime

* introduce type_eq when on nightly

* fix no_std

* fmt
This commit is contained in:
Andrea Fioraldi 2021-05-05 15:38:24 +02:00
parent 08a32c3856
commit bfa3fffc18
16 changed files with 193 additions and 129 deletions

View File

@ -46,9 +46,8 @@ pub fn main() {
// such as the notification of the addition of a new item to the corpus // such as the notification of the addition of a new item to the corpus
let mut mgr = SimpleEventManager::new(stats); let mut mgr = SimpleEventManager::new(stats);
// Create an observation channel using the siganls map // Create an observation channel using the signals map
let observer = let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
StdMapObserver::new("signals", unsafe { &mut SIGNALS }, unsafe { SIGNALS.len() });
// create a State from scratch // create a State from scratch
let mut state = State::new( let mut state = State::new(
@ -76,13 +75,9 @@ pub fn main() {
let scheduler = QueueCorpusScheduler::new(); let scheduler = QueueCorpusScheduler::new();
// Create the executor for an in-process function with just one observer // Create the executor for an in-process function with just one observer
let mut executor = InProcessExecutor::new( let mut executor =
&mut harness, InProcessExecutor::new(&mut harness, tuple_list!(observer), &mut state, &mut mgr)
tuple_list!(observer), .expect("Failed to create the Executor".into());
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor".into());
// Generator of printable bytearrays of max size 32 // Generator of printable bytearrays of max size 32
let mut generator = RandPrintablesGenerator::new(32); let mut generator = RandPrintablesGenerator::new(32);

View File

@ -258,12 +258,8 @@ unsafe fn fuzz(
}; };
let frida_options = FridaOptions::parse_env_options(); let frida_options = FridaOptions::parse_env_options();
let mut frida_helper = FridaInstrumentationHelper::new( let mut frida_helper =
&gum, FridaInstrumentationHelper::new(&gum, &frida_options, module_name, &modules_to_instrument);
&frida_options,
module_name,
&modules_to_instrument,
);
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr( let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(

View File

@ -22,7 +22,7 @@ use libafl::{
}; };
use libafl_targets::{ use libafl_targets::{
libfuzzer_initialize, libfuzzer_test_one_input, CMP_MAP, CMP_MAP_SIZE, EDGES_MAP, MAX_EDGES_NUM, libfuzzer_initialize, libfuzzer_test_one_input, CMP_MAP, EDGES_MAP, MAX_EDGES_NUM,
}; };
const ALLOC_MAP_SIZE: usize = 16 * 1024; const ALLOC_MAP_SIZE: usize = 16 * 1024;
@ -59,15 +59,14 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
setup_restarting_mgr_std(stats, broker_port).expect("Failed to setup the restarter".into()); setup_restarting_mgr_std(stats, broker_port).expect("Failed to setup the restarter".into());
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
StdMapObserver::new("edges", unsafe { &mut EDGES_MAP }, unsafe { MAX_EDGES_NUM }); let edges_observer = StdMapObserver::new("edges", edges);
// Create an observation channel using the cmp map // Create an observation channel using the cmp map
let cmps_observer = StdMapObserver::new("cmps", unsafe { &mut CMP_MAP }, CMP_MAP_SIZE); let cmps_observer = StdMapObserver::new("cmps", unsafe { &mut CMP_MAP });
// Create an observation channel using the allocations map // Create an observation channel using the allocations map
let allocs_observer = let allocs_observer = StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map });
StdMapObserver::new("allocs", unsafe { &mut libafl_alloc_map }, ALLOC_MAP_SIZE);
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {

View File

@ -63,11 +63,13 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
} }
}, },
}; };
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(unsafe { let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
StdMapObserver::new("edges", &mut EDGES_MAP, MAX_EDGES_NUM) let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("edges", edges));
});
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {
@ -79,7 +81,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Feedbacks to rate the interestingness of an input // Feedbacks to rate the interestingness of an input
feedback_or!( feedback_or!(
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
TimeFeedback::new() TimeFeedback::new_with_observer(&time_observer)
), ),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
@ -122,7 +124,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
let mut executor = TimeoutExecutor::new( let mut executor = TimeoutExecutor::new(
InProcessExecutor::new( InProcessExecutor::new(
&mut harness, &mut harness,
tuple_list!(edges_observer, TimeObserver::new("time")), tuple_list!(edges_observer, time_observer),
&mut state, &mut state,
&mut restarting_mgr, &mut restarting_mgr,
)?, )?,

View File

@ -1,38 +1,28 @@
//! A libfuzzer-like fuzzer with llmp-multithreading support and restarts //! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
//! The example harness is built for libpng. //! The example harness is built for libpng.
use core::time::Duration;
use std::{env, path::PathBuf}; use std::{env, path::PathBuf};
use libafl::{ use libafl::{
bolts::tuples::tuple_list, bolts::tuples::tuple_list,
corpus::{ corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
Corpus, InMemoryCorpus, IndexesLenTimeMinimizerCorpusScheduler, OnDiskCorpus,
QueueCorpusScheduler,
},
events::{setup_restarting_mgr_std, EventManager}, events::{setup_restarting_mgr_std, EventManager},
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor}, executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or, feedbacks::{MaxMapFeedback, ReachabilityFeedback},
feedbacks::{
CrashFeedback, MaxMapFeedback, ReachabilityFeedback, TimeFeedback, TimeoutFeedback,
},
fuzzer::{Fuzzer, StdFuzzer}, fuzzer::{Fuzzer, StdFuzzer},
mutators::scheduled::{havoc_mutations, StdScheduledMutator}, mutators::scheduled::{havoc_mutations, StdScheduledMutator},
mutators::token_mutations::Tokens, mutators::token_mutations::Tokens,
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, observers::{HitcountsMapObserver, StdMapObserver},
stages::mutational::StdMutationalStage, stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, State}, state::{HasCorpus, HasMetadata, State},
stats::SimpleStats, stats::SimpleStats,
utils::{current_nanos, StdRand}, utils::{current_nanos, StdRand},
Error, Error,
}; };
use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM}; use libafl_targets::{libfuzzer_initialize, libfuzzer_test_one_input, EDGES_MAP, MAX_EDGES_NUM};
#[cfg(unix)]
const TARGET_SIZE: usize = 4; const TARGET_SIZE: usize = 4;
#[cfg(unix)]
extern "C" { extern "C" {
static __libafl_target_list: *mut usize; static __libafl_target_list: *mut usize;
} }
@ -75,12 +65,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
}; };
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(unsafe { let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
StdMapObserver::new("edges", &mut EDGES_MAP, MAX_EDGES_NUM) let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("edges", edges));
});
let reachability_observer = let reachability_observer =
unsafe { StdMapObserver::new_from_ptr("png.c", __libafl_target_list, TARGET_SIZE) }; unsafe { StdMapObserver::new_from_ptr("png.c", __libafl_target_list, TARGET_SIZE) };
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {
State::new( State::new(
@ -89,19 +79,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Corpus that will be evolved, we keep it in memory for performance // Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(), InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input // Feedbacks to rate the interestingness of an input
feedback_or!( MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
TimeFeedback::new()
),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(), OnDiskCorpus::new(objective_dir).unwrap(),
// Feedbacks to recognize an input as solution // Feedbacks to recognize an input as solution
feedback_or!( ReachabilityFeedback::new_with_observer(&reachability_observer),
CrashFeedback::new(),
TimeoutFeedback::new(),
ReachabilityFeedback::new_with_observer(&reachability_observer)
),
) )
}); });
@ -125,8 +108,8 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// A fuzzer with just one stage // A fuzzer with just one stage
let mut fuzzer = StdFuzzer::new(tuple_list!(stage)); let mut fuzzer = StdFuzzer::new(tuple_list!(stage));
// A minimization+queue policy to get testcasess from the corpus // A random policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerCorpusScheduler::new(QueueCorpusScheduler::new()); let scheduler = RandCorpusScheduler::new();
// The wrapped harness function, calling out to the LLVM-style harness // The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |buf: &[u8]| { let mut harness = |buf: &[u8]| {
@ -135,20 +118,12 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
}; };
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time // Create the executor for an in-process function with one observer for edge coverage and one for the execution time
let mut executor = TimeoutExecutor::new( let mut executor = InProcessExecutor::new(
InProcessExecutor::new( &mut harness,
&mut harness, tuple_list!(edges_observer, reachability_observer,),
tuple_list!( &mut state,
edges_observer, &mut restarting_mgr,
reachability_observer, )?;
TimeObserver::new("time")
),
&mut state,
&mut restarting_mgr,
)?,
// 10 seconds timeout
Duration::new(10, 0),
);
// The actual target run starts here. // The actual target run starts here.
// Call LLVMFUzzerInitialize() if present. // Call LLVMFUzzerInitialize() if present.

View File

@ -63,8 +63,11 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
// We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges)
let edges_observer = let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
StdMapObserver::new("edges", unsafe { &mut EDGES_MAP }, unsafe { MAX_EDGES_NUM }); let edges_observer = StdMapObserver::new("edges", edges);
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// If not restarting, create a State from scratch // If not restarting, create a State from scratch
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {
@ -76,7 +79,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Feedbacks to rate the interestingness of an input // Feedbacks to rate the interestingness of an input
feedback_or!( feedback_or!(
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false), MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
TimeFeedback::new() TimeFeedback::new_with_observer(&time_observer)
), ),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
@ -118,7 +121,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Create the executor for an in-process function with just one observer for edge coverage // Create the executor for an in-process function with just one observer for edge coverage
let mut executor = InProcessExecutor::new( let mut executor = InProcessExecutor::new(
&mut harness, &mut harness,
tuple_list!(edges_observer, TimeObserver::new("time")), tuple_list!(edges_observer, time_observer),
&mut state, &mut state,
&mut restarting_mgr, &mut restarting_mgr,
)?; )?;

View File

@ -10,7 +10,8 @@ keywords = ["fuzzing", "testing", "security"]
edition = "2018" edition = "2018"
build = "build.rs" build = "build.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [build-dependencies]
rustc_version = "0.3.3"
[dev-dependencies] [dev-dependencies]
criterion = "0.3" # Benchmarking criterion = "0.3" # Benchmarking

View File

@ -1,5 +1,7 @@
//! special handling to build and link libafl //! special handling to build and link libafl
use rustc_version::{version_meta, Channel};
fn main() { fn main() {
#[cfg(target_os = "windows")] #[cfg(target_os = "windows")]
windows::build!( windows::build!(
@ -9,4 +11,20 @@ fn main() {
windows::win32::system_services::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile}, windows::win32::system_services::{CreateFileMappingA, OpenFileMappingA, MapViewOfFile, UnmapViewOfFile},
windows::win32::debug::{SetUnhandledExceptionFilter, EXCEPTION_POINTERS, EXCEPTION_RECORD, LPTOP_LEVEL_EXCEPTION_FILTER} windows::win32::debug::{SetUnhandledExceptionFilter, EXCEPTION_POINTERS, EXCEPTION_RECORD, LPTOP_LEVEL_EXCEPTION_FILTER}
); );
// Set cfg flags depending on release channel
match version_meta().unwrap().channel {
Channel::Stable => {
println!("cargo:rustc-cfg=RUSTC_IS_STABLE");
}
Channel::Beta => {
println!("cargo:rustc-cfg=RUSTC_IS_BETA");
}
Channel::Nightly => {
println!("cargo:rustc-cfg=RUSTC_IS_NIGHTLY");
}
Channel::Dev => {
println!("cargo:rustc-cfg=RUSTC_IS_DEV");
}
}
} }

View File

@ -6,6 +6,34 @@ use core::any::TypeId;
use xxhash_rust::const_xxh3::xxh3_64; use xxhash_rust::const_xxh3::xxh3_64;
#[cfg(feature = "RUSTC_IS_NIGHTLY")]
/// From https://stackoverflow.com/a/60138532/7658998
const fn type_eq<T: ?Sized, U: ?Sized>() -> bool {
// Helper trait. `VALUE` is false, except for the specialization of the
// case where `T == U`.
trait TypeEq<U: ?Sized> {
const VALUE: bool;
}
// Default implementation.
impl<T: ?Sized, U: ?Sized> TypeEq<U> for T {
default const VALUE: bool = false;
}
// Specialization for `T == U`.
impl<T: ?Sized> TypeEq<T> for T {
const VALUE: bool = true;
}
<T as TypeEq<U>>::VALUE
}
#[cfg(not(feature = "RUSTC_IS_NIGHTLY"))]
const fn type_eq<T: ?Sized, U: ?Sized>() -> bool {
// BEWARE! This is not unsafe, it is SUPER UNSAFE
true
}
pub trait HasLen { pub trait HasLen {
const LEN: usize; const LEN: usize;
@ -60,7 +88,7 @@ impl HasNameIdTuple for () {
impl<Head, Tail> HasNameIdTuple for (Head, Tail) impl<Head, Tail> HasNameIdTuple for (Head, Tail)
where where
Head: 'static + HasNameId, Head: HasNameId,
Tail: HasNameIdTuple, Tail: HasNameIdTuple,
{ {
fn get_const_name(&self, index: usize) -> Option<&'static str> { fn get_const_name(&self, index: usize) -> Option<&'static str> {
@ -132,6 +160,7 @@ where
Tail: MatchType, Tail: MatchType,
{ {
fn match_type<T: 'static>(&self, f: fn(t: &T)) { fn match_type<T: 'static>(&self, f: fn(t: &T)) {
// Switch this check to https://stackoverflow.com/a/60138532/7658998 when in stable and remove 'static
if TypeId::of::<T>() == TypeId::of::<Head>() { if TypeId::of::<T>() == TypeId::of::<Head>() {
f(unsafe { (&self.0 as *const _ as *const T).as_ref() }.unwrap()); f(unsafe { (&self.0 as *const _ as *const T).as_ref() }.unwrap());
} }
@ -139,6 +168,7 @@ where
} }
fn match_type_mut<T: 'static>(&mut self, f: fn(t: &mut T)) { fn match_type_mut<T: 'static>(&mut self, f: fn(t: &mut T)) {
// Switch this check to https://stackoverflow.com/a/60138532/7658998 when in stable and remove 'static
if TypeId::of::<T>() == TypeId::of::<Head>() { if TypeId::of::<T>() == TypeId::of::<Head>() {
f(unsafe { (&mut self.0 as *mut _ as *mut T).as_mut() }.unwrap()); f(unsafe { (&mut self.0 as *mut _ as *mut T).as_mut() }.unwrap());
} }
@ -164,7 +194,7 @@ impl NamedTuple for () {
impl<Head, Tail> NamedTuple for (Head, Tail) impl<Head, Tail> NamedTuple for (Head, Tail)
where where
Head: 'static + Named, Head: Named,
Tail: NamedTuple, Tail: NamedTuple,
{ {
fn get_name(&self, index: usize) -> Option<&str> { fn get_name(&self, index: usize) -> Option<&str> {
@ -176,6 +206,43 @@ where
} }
} }
/// This operation is unsafe with Rust stable, wait for https://stackoverflow.com/a/60138532/7658998
pub trait MatchName {
fn match_name<T>(&self, name: &str) -> Option<&T>;
fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T>;
}
impl MatchName for () {
fn match_name<T>(&self, _name: &str) -> Option<&T> {
None
}
fn match_name_mut<T>(&mut self, _name: &str) -> Option<&mut T> {
None
}
}
impl<Head, Tail> MatchName for (Head, Tail)
where
Head: Named,
Tail: MatchName,
{
fn match_name<T>(&self, name: &str) -> Option<&T> {
if type_eq::<Head, T>() && name == self.0.name() {
unsafe { (&self.0 as *const _ as *const T).as_ref() }
} else {
self.1.match_name::<T>(name)
}
}
fn match_name_mut<T>(&mut self, name: &str) -> Option<&mut T> {
if type_eq::<Head, T>() && name == self.0.name() {
unsafe { (&mut self.0 as *mut _ as *mut T).as_mut() }
} else {
self.1.match_name_mut::<T>(name)
}
}
}
pub trait MatchNameAndType { pub trait MatchNameAndType {
fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T>; fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T>;
fn match_name_type_mut<T: 'static>(&mut self, name: &str) -> Option<&mut T>; fn match_name_type_mut<T: 'static>(&mut self, name: &str) -> Option<&mut T>;
@ -196,6 +263,7 @@ where
Tail: MatchNameAndType, Tail: MatchNameAndType,
{ {
fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T> { fn match_name_type<T: 'static>(&self, name: &str) -> Option<&T> {
// Switch this check to https://stackoverflow.com/a/60138532/7658998 when in stable and remove 'static
if TypeId::of::<T>() == TypeId::of::<Head>() && name == self.0.name() { if TypeId::of::<T>() == TypeId::of::<Head>() && name == self.0.name() {
unsafe { (&self.0 as *const _ as *const T).as_ref() } unsafe { (&self.0 as *const _ as *const T).as_ref() }
} else { } else {
@ -204,6 +272,7 @@ where
} }
fn match_name_type_mut<T: 'static>(&mut self, name: &str) -> Option<&mut T> { fn match_name_type_mut<T: 'static>(&mut self, name: &str) -> Option<&mut T> {
// Switch this check to https://stackoverflow.com/a/60138532/7658998 when in stable and remove 'static
if TypeId::of::<T>() == TypeId::of::<Head>() && name == self.0.name() { if TypeId::of::<T>() == TypeId::of::<Head>() && name == self.0.name() {
unsafe { (&mut self.0 as *mut _ as *mut T).as_mut() } unsafe { (&mut self.0 as *mut _ as *mut T).as_mut() }
} else { } else {

View File

@ -247,7 +247,7 @@ mod tests {
#[test] #[test]
fn test_event_serde() { fn test_event_serde() {
let obv = StdMapObserver::new("test", unsafe { &mut MAP }, unsafe { MAP.len() }); let obv = StdMapObserver::new("test", unsafe { &mut MAP });
let map = tuple_list!(obv); let map = tuple_list!(obv);
let observers_buf = postcard::to_allocvec(&map).unwrap(); let observers_buf = postcard::to_allocvec(&map).unwrap();

View File

@ -144,8 +144,8 @@ where
OT: ObserversTuple, OT: ObserversTuple,
{ {
let mut interesting = false; let mut interesting = false;
// TODO optimize // TODO Replace with match_name_type when stable
let observer = observers.match_name_type::<O>(&self.name).unwrap(); let observer = observers.match_name::<O>(&self.name).unwrap();
let size = observer.usable_count(); let size = observer.usable_count();
let initial = observer.initial(); let initial = observer.initial();
@ -359,7 +359,8 @@ where
observers: &OT, observers: &OT,
_exit_kind: &ExitKind, _exit_kind: &ExitKind,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let observer = observers.match_name_type::<O>(&self.name).unwrap(); // TODO Replace with match_name_type when stable
let observer = observers.match_name::<O>(&self.name).unwrap();
let size = observer.usable_count(); let size = observer.usable_count();
let mut hit_target: bool = false; let mut hit_target: bool = false;
//check if we've hit any targets. //check if we've hit any targets.

View File

@ -4,6 +4,7 @@
pub mod map; pub mod map;
pub use map::*; pub use map::*;
use alloc::string::{String, ToString};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
@ -409,6 +410,7 @@ impl Default for TimeoutFeedback {
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeFeedback { pub struct TimeFeedback {
exec_time: Option<Duration>, exec_time: Option<Duration>,
name: String,
} }
impl<I> Feedback<I> for TimeFeedback impl<I> Feedback<I> for TimeFeedback
@ -424,7 +426,8 @@ where
where where
OT: ObserversTuple, OT: ObserversTuple,
{ {
let observer = observers.match_first_type::<TimeObserver>().unwrap(); // TODO Replace with match_name_type when stable
let observer = observers.match_name::<TimeObserver>(self.name()).unwrap();
self.exec_time = *observer.last_runtime(); self.exec_time = *observer.last_runtime();
Ok(false) Ok(false)
} }
@ -448,18 +451,22 @@ where
impl Named for TimeFeedback { impl Named for TimeFeedback {
#[inline] #[inline]
fn name(&self) -> &str { fn name(&self) -> &str {
"TimeFeedback" self.name.as_str()
} }
} }
impl TimeFeedback { impl TimeFeedback {
pub fn new() -> Self { pub fn new(name: &'static str) -> Self {
Self { exec_time: None } Self {
exec_time: None,
name: name.to_string(),
}
} }
}
impl Default for TimeFeedback { pub fn new_with_observer(observer: &TimeObserver) -> Self {
fn default() -> Self { Self {
Self::new() exec_time: None,
name: observer.name().to_string(),
}
} }
} }

View File

@ -3,6 +3,7 @@ Welcome to libAFL
*/ */
#![cfg_attr(not(feature = "std"), no_std)] #![cfg_attr(not(feature = "std"), no_std)]
#![cfg_attr(feature = "RUSTC_IS_NIGHTLY", feature(specialization))]
#[macro_use] #[macro_use]
extern crate alloc; extern crate alloc;

View File

@ -4,11 +4,12 @@ use alloc::{
string::{String, ToString}, string::{String, ToString},
vec::Vec, vec::Vec,
}; };
use core::slice::from_raw_parts_mut;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
bolts::{ bolts::{
ownedref::{OwnedArrayPtrMut, OwnedPtr}, ownedref::{OwnedRefMut, OwnedSliceMut},
tuples::Named, tuples::Named,
}, },
executors::HasExecHooks, executors::HasExecHooks,
@ -57,24 +58,24 @@ where
/// The Map Observer retrieves the state of a map, /// The Map Observer retrieves the state of a map,
/// that will get updated by the target. /// that will get updated by the target.
/// A well-known example is the AFL-Style coverage map. /// A well-known example is the AFL-Style coverage map.
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(bound = "T: serde::de::DeserializeOwned")] #[serde(bound = "T: serde::de::DeserializeOwned")]
#[allow(clippy::unsafe_derive_deserialize)] #[allow(clippy::unsafe_derive_deserialize)]
pub struct StdMapObserver<T> pub struct StdMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{ {
map: OwnedArrayPtrMut<T>, map: OwnedSliceMut<'a, T>,
initial: T, initial: T,
name: String, name: String,
} }
impl<T> Observer for StdMapObserver<T> where impl<'a, T> Observer for StdMapObserver<'a, T> where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned
{ {
} }
impl<EM, I, S, T> HasExecHooks<EM, I, S> for StdMapObserver<T> impl<'a, EM, I, S, T> HasExecHooks<EM, I, S> for StdMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
Self: MapObserver<T>, Self: MapObserver<T>,
@ -85,7 +86,7 @@ where
} }
} }
impl<T> Named for StdMapObserver<T> impl<'a, T> Named for StdMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{ {
@ -95,7 +96,7 @@ where
} }
} }
impl<T> MapObserver<T> for StdMapObserver<T> impl<'a, T> MapObserver<T> for StdMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{ {
@ -125,16 +126,15 @@ where
} }
} }
impl<T> StdMapObserver<T> impl<'a, T> StdMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{ {
/// Creates a new MapObserver /// Creates a new MapObserver
pub fn new(name: &'static str, map: &'static mut [T], len: usize) -> Self { pub fn new(name: &'static str, map: &'a mut [T]) -> Self {
assert!(map.len() >= len);
let initial = if map.is_empty() { T::default() } else { map[0] }; let initial = if map.is_empty() { T::default() } else { map[0] };
Self { Self {
map: OwnedArrayPtrMut::ArrayPtr((map.as_mut_ptr(), len)), map: OwnedSliceMut::Ref(map),
name: name.to_string(), name: name.to_string(),
initial, initial,
} }
@ -144,7 +144,7 @@ where
pub fn new_owned(name: &'static str, map: Vec<T>) -> Self { pub fn new_owned(name: &'static str, map: Vec<T>) -> Self {
let initial = if map.is_empty() { T::default() } else { map[0] }; let initial = if map.is_empty() { T::default() } else { map[0] };
Self { Self {
map: OwnedArrayPtrMut::Owned(map), map: OwnedSliceMut::Owned(map),
name: name.to_string(), name: name.to_string(),
initial, initial,
} }
@ -156,7 +156,7 @@ where
pub unsafe fn new_from_ptr(name: &'static str, map_ptr: *mut T, len: usize) -> Self { pub unsafe fn new_from_ptr(name: &'static str, map_ptr: *mut T, len: usize) -> Self {
let initial = if len > 0 { *map_ptr } else { T::default() }; let initial = if len > 0 { *map_ptr } else { T::default() };
StdMapObserver { StdMapObserver {
map: OwnedArrayPtrMut::ArrayPtr((map_ptr, len)), map: OwnedSliceMut::Ref(from_raw_parts_mut(map_ptr, len)),
name: name.to_string(), name: name.to_string(),
initial, initial,
} }
@ -164,25 +164,25 @@ where
} }
/// Overlooking a variable bitmap /// Overlooking a variable bitmap
#[derive(Serialize, Deserialize, Clone, Debug)] #[derive(Serialize, Deserialize, Debug)]
#[serde(bound = "T: serde::de::DeserializeOwned")] #[serde(bound = "T: serde::de::DeserializeOwned")]
#[allow(clippy::unsafe_derive_deserialize)] #[allow(clippy::unsafe_derive_deserialize)]
pub struct VariableMapObserver<T> pub struct VariableMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{ {
map: OwnedArrayPtrMut<T>, map: OwnedSliceMut<'a, T>,
size: OwnedPtr<usize>, size: OwnedRefMut<'a, usize>,
initial: T, initial: T,
name: String, name: String,
} }
impl<T> Observer for VariableMapObserver<T> where impl<'a, T> Observer for VariableMapObserver<'a, T> where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned
{ {
} }
impl<EM, I, S, T> HasExecHooks<EM, I, S> for VariableMapObserver<T> impl<'a, EM, I, S, T> HasExecHooks<EM, I, S> for VariableMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{ {
@ -192,7 +192,7 @@ where
} }
} }
impl<T> Named for VariableMapObserver<T> impl<'a, T> Named for VariableMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{ {
@ -202,7 +202,7 @@ where
} }
} }
impl<T> MapObserver<T> for VariableMapObserver<T> impl<'a, T> MapObserver<T> for VariableMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{ {
@ -237,16 +237,16 @@ where
} }
} }
impl<T> VariableMapObserver<T> impl<'a, T> VariableMapObserver<'a, T>
where where
T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned, T: Default + Copy + 'static + serde::Serialize + serde::de::DeserializeOwned,
{ {
/// Creates a new MapObserver /// Creates a new MapObserver
pub fn new(name: &'static str, map: &'static mut [T], size: *const usize) -> Self { pub fn new(name: &'static str, map: &'a mut [T], size: &'a mut usize) -> Self {
let initial = if map.is_empty() { T::default() } else { map[0] }; let initial = if map.is_empty() { T::default() } else { map[0] };
Self { Self {
map: OwnedArrayPtrMut::ArrayPtr((map.as_mut_ptr(), map.len())), map: OwnedSliceMut::Ref(map),
size: OwnedPtr::Ptr(size), size: OwnedRefMut::Ref(size),
name: name.into(), name: name.into(),
initial, initial,
} }
@ -254,17 +254,17 @@ where
/// Creates a new MapObserver from a raw pointer /// Creates a new MapObserver from a raw pointer
/// # Safety /// # Safety
/// Dereferences map_ptr with up to max_len elements of size_ptr. /// Dereferences map_ptr with up to max_len elements of size.
pub unsafe fn new_from_ptr( pub unsafe fn new_from_ptr(
name: &'static str, name: &'static str,
map_ptr: *mut T, map_ptr: *mut T,
max_len: usize, max_len: usize,
size_ptr: *const usize, size: &'a mut usize,
) -> Self { ) -> Self {
let initial = if max_len > 0 { *map_ptr } else { T::default() }; let initial = if max_len > 0 { *map_ptr } else { T::default() };
VariableMapObserver { VariableMapObserver {
map: OwnedArrayPtrMut::ArrayPtr((map_ptr, max_len)), map: OwnedSliceMut::Ref(from_raw_parts_mut(map_ptr, max_len)),
size: OwnedPtr::Ptr(size_ptr), size: OwnedRefMut::Ref(size),
name: name.into(), name: name.into(),
initial, initial,
} }

View File

@ -8,7 +8,7 @@ use core::time::Duration;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
bolts::tuples::{MatchFirstType, MatchNameAndType, MatchType, Named}, bolts::tuples::{MatchName, Named},
executors::HasExecHooks, executors::HasExecHooks,
utils::current_time, utils::current_time,
Error, Error,
@ -16,7 +16,7 @@ use crate::{
/// Observers observe different information about the target. /// Observers observe different information about the target.
/// They can then be used by various sorts of feedback. /// They can then be used by various sorts of feedback.
pub trait Observer: Named + serde::Serialize + serde::de::DeserializeOwned + 'static { pub trait Observer: Named + serde::Serialize + serde::de::DeserializeOwned {
/// The testcase finished execution, calculate any changes. /// The testcase finished execution, calculate any changes.
/// Reserved for future use. /// Reserved for future use.
#[inline] #[inline]
@ -26,10 +26,7 @@ pub trait Observer: Named + serde::Serialize + serde::de::DeserializeOwned + 'st
} }
/// A haskell-style tuple of observers /// A haskell-style tuple of observers
pub trait ObserversTuple: pub trait ObserversTuple: MatchName + serde::Serialize + serde::de::DeserializeOwned {}
MatchNameAndType + MatchType + MatchFirstType + serde::Serialize + serde::de::DeserializeOwned
{
}
impl ObserversTuple for () {} impl ObserversTuple for () {}
@ -99,7 +96,7 @@ mod tests {
fn test_observer_serde() { fn test_observer_serde() {
let obv = tuple_list!( let obv = tuple_list!(
TimeObserver::new("time"), TimeObserver::new("time"),
StdMapObserver::new("map", unsafe { &mut MAP }, unsafe { MAP.len() }) StdMapObserver::new("map", unsafe { &mut MAP })
); );
let vec = postcard::to_allocvec(&obv).unwrap(); let vec = postcard::to_allocvec(&obv).unwrap();
println!("{:?}", vec); println!("{:?}", vec);

View File

@ -1663,7 +1663,7 @@ impl<EM, I, S> HasExecHooks<EM, I, S> for AsanErrorsObserver {
impl Named for AsanErrorsObserver { impl Named for AsanErrorsObserver {
#[inline] #[inline]
fn name(&self) -> &str { fn name(&self) -> &str {
"AsanErrorsObserver" "AsanErrors"
} }
} }
@ -1710,7 +1710,7 @@ where
_exit_kind: &ExitKind, _exit_kind: &ExitKind,
) -> Result<bool, Error> { ) -> Result<bool, Error> {
let observer = observers let observer = observers
.match_first_type::<AsanErrorsObserver>() .match_name::<AsanErrorsObserver>("AsanErrors")
.expect("An AsanErrorsFeedback needs an AsanErrorsObserver"); .expect("An AsanErrorsFeedback needs an AsanErrorsObserver");
match observer.errors() { match observer.errors() {
None => Ok(false), None => Ok(false),
@ -1742,7 +1742,7 @@ where
impl Named for AsanErrorsFeedback { impl Named for AsanErrorsFeedback {
#[inline] #[inline]
fn name(&self) -> &str { fn name(&self) -> &str {
"AsanErrorsFeedback" "AsanErrors"
} }
} }