Composing feedback (#85)

* composing feedbacks as logic operations and bump to 0.2

* adapt fuzzers and libafl_frida

* fix windows build
This commit is contained in:
Andrea Fioraldi 2021-05-04 16:00:39 +02:00
parent 9e9d95f93d
commit 9f3b0984c3
19 changed files with 399 additions and 273 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "baby_fuzzer"
version = "0.1.0"
version = "0.2.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"

View File

@ -56,13 +56,13 @@ pub fn main() {
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
tuple_list!(MaxMapFeedback::new_with_observer(&observer)),
// Feedback to rate the interestingness of an input
MaxMapFeedback::new_with_observer(&observer),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
// Feedbacks to recognize an input as solution
tuple_list!(CrashFeedback::new()),
CrashFeedback::new(),
);
// Setup a basic mutator with a mutational stage

View File

@ -1,6 +1,6 @@
[package]
name = "frida_libpng"
version = "0.1.0"
version = "0.2.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"
build = "build.rs"
@ -25,7 +25,7 @@ libafl = { path = "../../libafl/", features = [ "std", "llmp_compression" ] } #,
capstone = "0.8.0"
frida-gum = { version = "0.4", git = "https://github.com/s1341/frida-rust", features = [ "auto-download", "event-sink", "invocation-listener"] }
#frida-gum = { version = "0.4", path = "../../../frida-rust/frida-gum", features = [ "auto-download", "event-sink", "invocation-listener"] }
libafl_frida = { path = "../../libafl_frida", version = "0.1.0" }
libafl_frida = { path = "../../libafl_frida", version = "0.2.0" }
lazy_static = "1.4.0"
libc = "0.2"
libloading = "0.7.0"

View File

@ -12,6 +12,7 @@ use libafl::{
inprocess::InProcessExecutor, timeout::TimeoutExecutor, Executor, ExitKind, HasExecHooks,
HasExecHooksTuple, HasObservers, HasObserversHooks,
},
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{HasTargetBytes, Input},
@ -276,17 +277,13 @@ unsafe fn fuzz(
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
tuple_list!(MaxMapFeedback::new_with_observer_track(
&edges_observer,
true,
false
)),
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new_save_meta(objective_dir, Some(OnDiskMetadataFormat::JsonPretty))
.unwrap(),
// Feedbacks to recognize an input as solution
tuple_list!(
feedback_or!(
CrashFeedback::new(),
TimeoutFeedback::new(),
AsanErrorsFeedback::new()

View File

@ -1,6 +1,6 @@
[package]
name = "libfuzzer_libmozjpeg"
version = "0.1.0"
version = "0.2.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"

View File

@ -8,6 +8,7 @@ use libafl::{
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus, RandCorpusScheduler},
events::setup_restarting_mgr_std,
executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
@ -76,7 +77,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
tuple_list!(
feedback_or!(
MaxMapFeedback::new_with_observer(&edges_observer),
MaxMapFeedback::new_with_observer(&cmps_observer),
MaxMapFeedback::new_with_observer(&allocs_observer)
@ -85,7 +86,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// Feedbacks to recognize an input as solution
tuple_list!(CrashFeedback::new()),
CrashFeedback::new(),
)
});

View File

@ -1,6 +1,6 @@
[package]
name = "libfuzzer_libpng"
version = "0.1.0"
version = "0.2.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"

View File

@ -12,6 +12,7 @@ use libafl::{
},
events::{setup_restarting_mgr_std, EventManager},
executors::{inprocess::InProcessExecutor, ExitKind, TimeoutExecutor},
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
@ -76,7 +77,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
tuple_list!(
feedback_or!(
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
TimeFeedback::new()
),
@ -84,7 +85,7 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// Feedbacks to recognize an input as solution
tuple_list!(CrashFeedback::new(), TimeoutFeedback::new()),
feedback_or!(CrashFeedback::new(), TimeoutFeedback::new()),
)
});

View File

@ -1,6 +1,6 @@
[package]
name = "libfuzzer_stb_image"
version = "0.1.0"
version = "0.2.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"
build = "build.rs"

View File

@ -11,6 +11,7 @@ use libafl::{
},
events::setup_restarting_mgr_std,
executors::{inprocess::InProcessExecutor, ExitKind},
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
@ -73,15 +74,15 @@ fn fuzz(corpus_dirs: Vec<PathBuf>, objective_dir: PathBuf, broker_port: u16) ->
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Feedbacks to rate the interestingness of an input
tuple_list!(
feedback_or!(
MaxMapFeedback::new_with_observer_track(&edges_observer, true, false),
TimeFeedback::new()
),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(objective_dir).unwrap(),
// Feedbacks to recognize an input as solution
tuple_list!(CrashFeedback::new()),
// Feedback to recognize an input as solution
CrashFeedback::new(),
)
});

View File

@ -1,6 +1,6 @@
[package]
name = "libafl"
version = "0.1.0"
version = "0.2.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
description = "Slot your own fuzzers together and extend their features using Rust"
documentation = "https://docs.rs/libafl"

View File

@ -23,8 +23,6 @@ where
input: Option<I>,
/// Filename, if this testcase is backed by a file in the filesystem
filename: Option<String>,
/// Accumulated fitness from all the feedbacks
fitness: u32,
/// Map of metadata associated with this testcase
metadata: SerdeAnyMap,
/// Time needed to execute the input
@ -120,24 +118,6 @@ where
self.filename = Some(filename);
}
/// Get the fitness
#[inline]
pub fn fitness(&self) -> u32 {
self.fitness
}
/// Get the fitness (mutable)
#[inline]
pub fn fitness_mut(&mut self) -> &mut u32 {
&mut self.fitness
}
/// Set the fitness
#[inline]
pub fn set_fitness(&mut self, fitness: u32) {
self.fitness = fitness;
}
/// Get the execution time of the testcase
pub fn exec_time(&self) -> &Option<Duration> {
&self.exec_time
@ -157,7 +137,6 @@ where
Testcase {
input: Some(input.into()),
filename: None,
fitness: 0,
metadata: SerdeAnyMap::new(),
exec_time: None,
cached_len: None,
@ -170,20 +149,6 @@ where
Testcase {
input: Some(input),
filename: Some(filename),
fitness: 0,
metadata: SerdeAnyMap::new(),
exec_time: None,
cached_len: None,
}
}
/// Create a new Testcase instace given an input and a fitness
#[inline]
pub fn with_fitness(input: I, fitness: u32) -> Self {
Testcase {
input: Some(input),
filename: None,
fitness,
metadata: SerdeAnyMap::new(),
exec_time: None,
cached_len: None,
@ -195,7 +160,6 @@ where
Testcase {
input: None,
filename: None,
fitness: 0,
metadata: SerdeAnyMap::new(),
exec_time: None,
cached_len: None,

View File

@ -292,11 +292,10 @@ where
let observers: OT = postcard::from_bytes(&observers_buf)?;
// TODO include ExitKind in NewTestcase
let fitness = state.is_interesting(&input, &observers, &ExitKind::Ok)?;
if fitness > 0
&& state
.add_if_interesting(&input, fitness, scheduler)?
.is_some()
let is_interesting = state.is_interesting(&input, &observers, &ExitKind::Ok)?;
if state
.add_if_interesting(&input, is_interesting, scheduler)?
.is_some()
{
#[cfg(feature = "std")]
println!("Added received Testcase");

View File

@ -19,10 +19,10 @@ use crate::{
executors::{
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
},
feedbacks::FeedbacksTuple,
feedbacks::Feedback,
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
state::{HasObjectives, HasSolutions},
state::{HasObjective, HasSolutions},
Error,
};
@ -158,7 +158,7 @@ where
/// * `harness_fn` - the harness, executiong the function
/// * `observers` - the observers observing the target during execution
/// This may return an error on unix, if signal handler setup fails
pub fn new<OC, OFT>(
pub fn new<OC, OF>(
harness_fn: &'a mut H,
observers: OT,
_state: &mut S,
@ -167,19 +167,19 @@ where
where
EM: EventManager<I, S>,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
OF: Feedback<I>,
S: HasObjective<OF, I> + HasSolutions<OC, I>,
{
#[cfg(unix)]
unsafe {
let data = &mut unix_signal_handler::GLOBAL_STATE;
write_volatile(
&mut data.crash_handler,
unix_signal_handler::inproc_crash_handler::<EM, I, OC, OFT, OT, S>,
unix_signal_handler::inproc_crash_handler::<EM, I, OC, OF, OT, S>,
);
write_volatile(
&mut data.timeout_handler,
unix_signal_handler::inproc_timeout_handler::<EM, I, OC, OFT, OT, S>,
unix_signal_handler::inproc_timeout_handler::<EM, I, OC, OF, OT, S>,
);
setup_signal_handler(data)?;
@ -190,11 +190,11 @@ where
let data = &mut windows_exception_handler::GLOBAL_STATE;
write_volatile(
&mut data.crash_handler,
windows_exception_handler::inproc_crash_handler::<EM, I, OC, OFT, OT, S>,
windows_exception_handler::inproc_crash_handler::<EM, I, OC, OF, OT, S>,
);
//write_volatile(
// &mut data.timeout_handler,
// windows_exception_handler::inproc_timeout_handler::<EM, I, OC, OFT, OT, S>,
// windows_exception_handler::inproc_timeout_handler::<EM, I, OC, OF, OT, S>,
//);
setup_exception_handler(data)?;
@ -234,10 +234,10 @@ mod unix_signal_handler {
corpus::{Corpus, Testcase},
events::{Event, EventManager},
executors::ExitKind,
feedbacks::FeedbacksTuple,
feedbacks::Feedback,
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
state::{HasObjectives, HasSolutions},
state::{HasObjective, HasSolutions},
};
// TODO merge GLOBAL_STATE with the Windows one
@ -308,7 +308,7 @@ mod unix_signal_handler {
}
#[cfg(unix)]
pub unsafe fn inproc_timeout_handler<EM, I, OC, OFT, OT, S>(
pub unsafe fn inproc_timeout_handler<EM, I, OC, OF, OT, S>(
_signal: Signal,
_info: siginfo_t,
_context: &mut ucontext_t,
@ -317,8 +317,8 @@ mod unix_signal_handler {
EM: EventManager<I, S>,
OT: ObserversTuple,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
OF: Feedback<I>,
S: HasObjective<OF, I> + HasSolutions<OC, I>,
I: Input + HasTargetBytes,
{
let state = (data.state_ptr as *mut S).as_mut().unwrap();
@ -337,11 +337,11 @@ mod unix_signal_handler {
let input = (data.current_input_ptr as *const I).as_ref().unwrap();
data.current_input_ptr = ptr::null();
let obj_fitness = state
.objectives_mut()
.is_interesting_all(&input, observers, &ExitKind::Timeout)
.expect("In timeout handler objectives failure.");
if obj_fitness > 0 {
let interesting = state
.objective_mut()
.is_interesting(&input, observers, &ExitKind::Timeout)
.expect("In timeout handler objective failure.");
if interesting {
state
.solutions_mut()
.add(Testcase::new(input.clone()))
@ -374,7 +374,7 @@ mod unix_signal_handler {
/// Will be used for signal handling.
/// It will store the current State to shmem, then exit.
#[allow(clippy::too_many_lines)]
pub unsafe fn inproc_crash_handler<EM, I, OC, OFT, OT, S>(
pub unsafe fn inproc_crash_handler<EM, I, OC, OF, OT, S>(
_signal: Signal,
_info: siginfo_t,
_context: &mut ucontext_t,
@ -383,8 +383,8 @@ mod unix_signal_handler {
EM: EventManager<I, S>,
OT: ObserversTuple,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
OF: Feedback<I>,
S: HasObjective<OF, I> + HasSolutions<OC, I>,
I: Input + HasTargetBytes,
{
#[cfg(all(target_os = "android", target_arch = "aarch64"))]
@ -446,11 +446,11 @@ mod unix_signal_handler {
// Make sure we don't crash in the crash handler forever.
data.current_input_ptr = ptr::null();
let obj_fitness = state
.objectives_mut()
.is_interesting_all(&input, observers, &ExitKind::Crash)
.expect("In crash handler objectives failure.");
if obj_fitness > 0 {
let interesting = state
.objective_mut()
.is_interesting(&input, observers, &ExitKind::Crash)
.expect("In crash handler objective failure.");
if interesting {
let new_input = input.clone();
state
.solutions_mut()
@ -528,10 +528,10 @@ mod windows_exception_handler {
corpus::{Corpus, Testcase},
events::{Event, EventManager},
executors::ExitKind,
feedbacks::FeedbacksTuple,
feedbacks::Feedback,
inputs::{HasTargetBytes, Input},
observers::ObserversTuple,
state::{HasObjectives, HasSolutions},
state::{HasObjective, HasSolutions},
};
/// Signal handling on unix systems needs some nasty unsafe.
@ -582,7 +582,7 @@ mod windows_exception_handler {
}
}
pub unsafe fn inproc_crash_handler<EM, I, OC, OFT, OT, S>(
pub unsafe fn inproc_crash_handler<EM, I, OC, OF, OT, S>(
code: ExceptionCode,
exception_pointers: *mut EXCEPTION_POINTERS,
data: &mut InProcessExecutorHandlerData,
@ -590,8 +590,8 @@ mod windows_exception_handler {
EM: EventManager<I, S>,
OT: ObserversTuple,
OC: Corpus<I>,
OFT: FeedbacksTuple<I>,
S: HasObjectives<OFT, I> + HasSolutions<OC, I>,
OF: Feedback<I>,
S: HasObjective<OF, I> + HasSolutions<OC, I>,
I: Input + HasTargetBytes,
{
#[cfg(feature = "std")]
@ -610,11 +610,11 @@ mod windows_exception_handler {
// Make sure we don't crash in the crash handler forever.
data.current_input_ptr = ptr::null();
let obj_fitness = state
.objectives_mut()
.is_interesting_all(&input, observers, &ExitKind::Crash)
.expect("In crash handler objectives failure.");
if obj_fitness > 0 {
let interesting = state
.objective_mut()
.is_interesting(&input, observers, &ExitKind::Crash)
.expect("In crash handler objective failure.");
if interesting {
let new_input = input.clone();
state
.solutions_mut()

View File

@ -139,11 +139,11 @@ where
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<u32, Error>
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
let mut interesting = 0;
let mut interesting = false;
// TODO optimize
let observer = observers.match_name_type::<O>(&self.name).unwrap();
let size = observer.usable_count();
@ -157,7 +157,7 @@ where
let reduced = R::reduce(history, item);
if history != reduced {
self.history_map[i] = reduced;
interesting += 1;
interesting = true;
}
}
} else if self.indexes.is_some() && self.novelties.is_none() {
@ -171,7 +171,7 @@ where
let reduced = R::reduce(history, item);
if history != reduced {
self.history_map[i] = reduced;
interesting += 1;
interesting = true;
}
}
} else if self.indexes.is_none() && self.novelties.is_some() {
@ -182,7 +182,7 @@ where
let reduced = R::reduce(history, item);
if history != reduced {
self.history_map[i] = reduced;
interesting += 1;
interesting = true;
self.novelties.as_mut().unwrap().push(i);
}
}
@ -197,7 +197,7 @@ where
let reduced = R::reduce(history, item);
if history != reduced {
self.history_map[i] = reduced;
interesting += 1;
interesting = true;
self.novelties.as_mut().unwrap().push(i);
}
}

View File

@ -15,12 +15,12 @@ use crate::{
Error,
};
use core::time::Duration;
use core::{marker::PhantomData, time::Duration};
/// Feedbacks evaluate the observers.
/// Basically, they reduce the information provided by an observer to a value,
/// indicating the "interestingness" of the last run.
pub trait Feedback<I>: Named + serde::Serialize + serde::de::DeserializeOwned + 'static
pub trait Feedback<I>: Named + serde::Serialize + serde::de::DeserializeOwned
where
I: Input,
{
@ -30,7 +30,7 @@ where
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error>
) -> Result<bool, Error>
where
OT: ObserversTuple;
@ -47,77 +47,239 @@ where
}
}
pub trait FeedbacksTuple<I>: serde::Serialize + serde::de::DeserializeOwned
/// Compose feedbacks with an AND operation
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct AndFeedback<A, B, I>
where
A: Feedback<I>,
B: Feedback<I>,
I: Input,
{
/// Get the total interestingness value from all feedbacks
fn is_interesting_all<OT>(
pub first: A,
pub second: B,
phantom: PhantomData<I>,
}
impl<A, B, I> Feedback<I> for AndFeedback<A, B, I>
where
A: Feedback<I>,
B: Feedback<I>,
I: Input,
{
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error>
where
OT: ObserversTuple;
/// Write metadata for this testcase
fn append_metadata_all(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error>;
/// Discards metadata - the end of this input's execution
fn discard_metadata_all(&mut self, input: &I) -> Result<(), Error>;
}
impl<I> FeedbacksTuple<I> for ()
where
I: Input,
{
#[inline]
fn is_interesting_all<OT>(&mut self, _: &I, _: &OT, _: &ExitKind) -> Result<u32, Error>
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
Ok(0)
}
#[inline]
fn append_metadata_all(&mut self, _testcase: &mut Testcase<I>) -> Result<(), Error> {
Ok(())
}
#[inline]
fn discard_metadata_all(&mut self, _input: &I) -> Result<(), Error> {
Ok(())
let a = self.first.is_interesting(input, observers, exit_kind)?;
let b = self.second.is_interesting(input, observers, exit_kind)?;
Ok(a && b)
}
}
impl<Head, Tail, I> FeedbacksTuple<I> for (Head, Tail)
impl<A, B, I> Named for AndFeedback<A, B, I>
where
Head: Feedback<I>,
Tail: FeedbacksTuple<I>,
A: Feedback<I>,
B: Feedback<I>,
I: Input,
{
fn is_interesting_all<OT>(
#[inline]
fn name(&self) -> &str {
//format!("And({}, {})", self.first.name(), self.second.name())
"AndFeedback"
}
}
impl<A, B, I> AndFeedback<A, B, I>
where
A: Feedback<I>,
B: Feedback<I>,
I: Input,
{
pub fn new(first: A, second: B) -> Self {
Self {
first,
second,
phantom: PhantomData,
}
}
}
/// Compose feedbacks with an OR operation
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct OrFeedback<A, B, I>
where
A: Feedback<I>,
B: Feedback<I>,
I: Input,
{
pub first: A,
pub second: B,
phantom: PhantomData<I>,
}
impl<A, B, I> Feedback<I> for OrFeedback<A, B, I>
where
A: Feedback<I>,
B: Feedback<I>,
I: Input,
{
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error>
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
Ok(self.0.is_interesting(input, observers, exit_kind)?
+ self.1.is_interesting_all(input, observers, exit_kind)?)
let a = self.first.is_interesting(input, observers, exit_kind)?;
let b = self.second.is_interesting(input, observers, exit_kind)?;
Ok(a || b)
}
}
fn append_metadata_all(&mut self, testcase: &mut Testcase<I>) -> Result<(), Error> {
self.0.append_metadata(testcase)?;
self.1.append_metadata_all(testcase)
impl<A, B, I> Named for OrFeedback<A, B, I>
where
A: Feedback<I>,
B: Feedback<I>,
I: Input,
{
#[inline]
fn name(&self) -> &str {
//format!("Or({}, {})", self.first.name(), self.second.name())
"OrFeedback"
}
}
fn discard_metadata_all(&mut self, input: &I) -> Result<(), Error> {
self.0.discard_metadata(input)?;
self.1.discard_metadata_all(input)
impl<A, B, I> OrFeedback<A, B, I>
where
A: Feedback<I>,
B: Feedback<I>,
I: Input,
{
pub fn new(first: A, second: B) -> Self {
Self {
first,
second,
phantom: PhantomData,
}
}
}
/// Compose feedbacks with an OR operation
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct NotFeedback<A, I>
where
A: Feedback<I>,
I: Input,
{
pub first: A,
phantom: PhantomData<I>,
}
impl<A, I> Feedback<I> for NotFeedback<A, I>
where
A: Feedback<I>,
I: Input,
{
fn is_interesting<OT>(
&mut self,
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
Ok(!self.first.is_interesting(input, observers, exit_kind)?)
}
}
impl<A, I> Named for NotFeedback<A, I>
where
A: Feedback<I>,
I: Input,
{
#[inline]
fn name(&self) -> &str {
//format!("Not({})", self.first.name())
"NotFeedback"
}
}
impl<A, I> NotFeedback<A, I>
where
A: Feedback<I>,
I: Input,
{
pub fn new(first: A) -> Self {
Self {
first,
phantom: PhantomData,
}
}
}
/// Variadic macro to create a chain of AndFeedback
#[macro_export]
macro_rules! feedback_and {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
// recursive call
$crate::feedbacks::AndFeedback::new($head , feedback_and!($($tail),+))
};
}
/// Variadic macro to create a chain of OrFeedback
#[macro_export]
macro_rules! feedback_or {
( $last:expr ) => { $last };
( $head:expr, $($tail:expr), +) => {
// recursive call
$crate::feedbacks::OrFeedback::new($head , feedback_or!($($tail),+))
};
}
/// Variadic macro to create a NotFeedback
#[macro_export]
macro_rules! feedback_not {
( $last:expr ) => {
$crate::feedbacks::NotFeedback::new($last)
};
}
/// Hack to use () as empty Feedback
impl<I> Feedback<I> for ()
where
I: Input,
{
fn is_interesting<OT>(
&mut self,
_input: &I,
_observers: &OT,
_exit_kind: &ExitKind,
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
Ok(false)
}
}
impl Named for () {
#[inline]
fn name(&self) -> &str {
"Empty"
}
}
@ -134,14 +296,14 @@ where
_input: &I,
_observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error>
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
if let ExitKind::Crash = exit_kind {
Ok(1)
Ok(true)
} else {
Ok(0)
Ok(false)
}
}
}
@ -177,14 +339,14 @@ where
_input: &I,
_observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error>
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
if let ExitKind::Timeout = exit_kind {
Ok(1)
Ok(true)
} else {
Ok(0)
Ok(false)
}
}
}
@ -209,6 +371,7 @@ impl Default for TimeoutFeedback {
}
/// Nop feedback that annotates execution time in the new testcase, if any
/// For this Feedback, the testcase is never interesting (use with an OR)
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct TimeFeedback {
exec_time: Option<Duration>,
@ -223,13 +386,13 @@ where
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<u32, Error>
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
let observer = observers.match_first_type::<TimeObserver>().unwrap();
self.exec_time = *observer.last_runtime();
Ok(0)
Ok(false)
}
/// Append to the testcase the generated metadata in case of a new corpus item

View File

@ -15,7 +15,7 @@ use crate::{
executors::{
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
},
feedbacks::FeedbacksTuple,
feedbacks::Feedback,
generators::Generator,
inputs::Input,
observers::ObserversTuple,
@ -98,30 +98,30 @@ pub trait HasMetadata {
}
}
/// Trait for elements offering a feedbacks tuple
pub trait HasFeedbacks<FT, I>: Sized
/// Trait for elements offering a feedback
pub trait HasFeedback<F, I>: Sized
where
FT: FeedbacksTuple<I>,
F: Feedback<I>,
I: Input,
{
/// The feedbacks tuple
fn feedbacks(&self) -> &FT;
/// The feedback
fn feedback(&self) -> &F;
/// The feedbacks tuple (mut)
fn feedbacks_mut(&mut self) -> &mut FT;
/// The feedback (mut)
fn feedback_mut(&mut self) -> &mut F;
}
/// Trait for elements offering an objective feedbacks tuple
pub trait HasObjectives<FT, I>: Sized
/// Trait for elements offering an objective feedback tuple
pub trait HasObjective<OF, I>: Sized
where
FT: FeedbacksTuple<I>,
OF: Feedback<I>,
I: Input,
{
/// The objective feedbacks tuple
fn objectives(&self) -> &FT;
/// The objective feedback
fn objective(&self) -> &OF;
/// The objective feedbacks tuple (mut)
fn objectives_mut(&mut self) -> &mut FT;
/// The objective feedback (mut)
fn objective_mut(&mut self) -> &mut OF;
}
/// Trait for the execution counter
@ -153,7 +153,7 @@ where
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error>
) -> Result<bool, Error>
where
OT: ObserversTuple;
@ -161,7 +161,7 @@ where
fn add_if_interesting<CS>(
&mut self,
input: &I,
fitness: u32,
is_interesting: bool,
scheduler: &CS,
) -> Result<Option<usize>, Error>
where
@ -169,7 +169,7 @@ where
Self: Sized;
}
/// Evaluate an input modyfing the state of the fuzzer and returning a fitness
/// Evaluate an input modyfing the state of the fuzzer
pub trait Evaluator<I>: Sized
where
I: Input,
@ -181,7 +181,7 @@ where
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
) -> Result<(u32, Option<usize>), Error>
) -> Result<(bool, Option<usize>), Error>
where
E: Executor<I>
+ HasObservers<OT>
@ -194,15 +194,15 @@ where
/// The state a fuzz run.
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "FT: serde::de::DeserializeOwned")]
pub struct State<C, FT, I, OFT, R, SC>
#[serde(bound = "F: serde::de::DeserializeOwned")]
pub struct State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// RNG instance
rand: R,
@ -213,11 +213,11 @@ where
/// The corpus
corpus: C,
/// Feedbacks used to evaluate an input
feedbacks: FT,
feedback: F,
// Solutions corpus
solutions: SC,
/// Objective Feedbacks
objectives: OFT,
objective: OF,
/// Metadata stored for this state by one of the components
metadata: SerdeAnyMap,
/// MaxSize testcase size for mutators that appreciate it
@ -226,14 +226,14 @@ where
phantom: PhantomData<I>,
}
impl<C, FT, I, OFT, R, SC> HasRand<R> for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> HasRand<R> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// The rand instance
#[inline]
@ -248,14 +248,14 @@ where
}
}
impl<C, FT, I, OFT, R, SC> HasCorpus<C, I> for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> HasCorpus<C, I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// Returns the corpus
#[inline]
@ -270,14 +270,14 @@ where
}
}
impl<C, FT, I, OFT, R, SC> HasSolutions<SC, I> for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> HasSolutions<SC, I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// Returns the solutions corpus
#[inline]
@ -292,14 +292,14 @@ where
}
}
impl<C, FT, I, OFT, R, SC> HasMetadata for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> HasMetadata for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// Get all the metadata into an HashMap
#[inline]
@ -314,58 +314,58 @@ where
}
}
impl<C, FT, I, OFT, R, SC> HasFeedbacks<FT, I> for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> HasFeedback<F, I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// The feedbacks tuple
/// The feedback
#[inline]
fn feedbacks(&self) -> &FT {
&self.feedbacks
fn feedback(&self) -> &F {
&self.feedback
}
/// The feedbacks tuple (mut)
/// The feedback (mut)
#[inline]
fn feedbacks_mut(&mut self) -> &mut FT {
&mut self.feedbacks
fn feedback_mut(&mut self) -> &mut F {
&mut self.feedback
}
}
impl<C, FT, I, OFT, R, SC> HasObjectives<OFT, I> for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> HasObjective<OF, I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// The objective feedbacks tuple
/// The objective feedback
#[inline]
fn objectives(&self) -> &OFT {
&self.objectives
fn objective(&self) -> &OF {
&self.objective
}
/// The objective feedbacks tuple (mut)
/// The objective feedback (mut)
#[inline]
fn objectives_mut(&mut self) -> &mut OFT {
&mut self.objectives
fn objective_mut(&mut self) -> &mut OF {
&mut self.objective
}
}
impl<C, FT, I, OFT, R, SC> HasExecutions for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> HasExecutions for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// The executions counter
#[inline]
@ -380,14 +380,14 @@ where
}
}
impl<C, FT, I, OFT, R, SC> HasMaxSize for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> HasMaxSize for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
fn max_size(&self) -> usize {
self.max_size
@ -398,14 +398,14 @@ where
}
}
impl<C, FT, I, OFT, R, SC> HasStartTime for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> HasStartTime for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// The starting time
#[inline]
@ -420,14 +420,14 @@ where
}
}
impl<C, FT, I, OFT, R, SC> IfInteresting<I> for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> IfInteresting<I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// Evaluate if a set of observation channels has an interesting state
fn is_interesting<OT>(
@ -435,12 +435,12 @@ where
input: &I,
observers: &OT,
exit_kind: &ExitKind,
) -> Result<u32, Error>
) -> Result<bool, Error>
where
OT: ObserversTuple,
{
self.feedbacks_mut()
.is_interesting_all(input, observers, exit_kind)
self.feedback_mut()
.is_interesting(input, observers, exit_kind)
}
/// Adds this input to the corpus, if it's intersting, and return the index
@ -448,33 +448,33 @@ where
fn add_if_interesting<CS>(
&mut self,
input: &I,
fitness: u32,
is_interesting: bool,
scheduler: &CS,
) -> Result<Option<usize>, Error>
where
CS: CorpusScheduler<I, Self>,
{
if fitness > 0 {
let mut testcase = Testcase::with_fitness(input.clone(), fitness);
self.feedbacks_mut().append_metadata_all(&mut testcase)?;
if is_interesting {
let mut testcase = Testcase::new(input.clone());
self.feedback_mut().append_metadata(&mut testcase)?;
let idx = self.corpus.add(testcase)?;
scheduler.on_add(self, idx)?;
Ok(Some(idx))
} else {
self.feedbacks_mut().discard_metadata_all(&input)?;
self.feedback_mut().discard_metadata(&input)?;
Ok(None)
}
}
}
impl<C, FT, I, OFT, R, SC> Evaluator<I> for State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> Evaluator<I> for State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// Process one input, adding to the respective corpuses if needed and firing the right events
#[inline]
@ -485,7 +485,7 @@ where
executor: &mut E,
manager: &mut EM,
scheduler: &CS,
) -> Result<(u32, Option<usize>), Error>
) -> Result<(bool, Option<usize>), Error>
where
E: Executor<I>
+ HasObservers<OT>
@ -496,19 +496,19 @@ where
EM: EventManager<I, Self>,
CS: CorpusScheduler<I, Self>,
{
let (fitness, is_solution) = self.execute_input(&input, executor, manager)?;
let (is_interesting, is_solution) = self.execute_input(&input, executor, manager)?;
let observers = executor.observers();
if is_solution {
// If the input is a solution, add it to the respective corpus
let mut testcase = Testcase::new(input.clone());
self.objectives_mut().append_metadata_all(&mut testcase)?;
self.objective_mut().append_metadata(&mut testcase)?;
self.solutions_mut().add(testcase)?;
} else {
self.objectives_mut().discard_metadata_all(&input)?;
self.objective_mut().discard_metadata(&input)?;
}
let corpus_idx = self.add_if_interesting(&input, fitness, scheduler)?;
let corpus_idx = self.add_if_interesting(&input, is_interesting, scheduler)?;
if corpus_idx.is_some() {
let observers_buf = manager.serialize_observers(observers)?;
manager.fire(
@ -524,18 +524,18 @@ where
)?;
}
Ok((fitness, corpus_idx))
Ok((is_interesting, corpus_idx))
}
}
#[cfg(feature = "std")]
impl<C, FT, OFT, R, SC> State<C, FT, BytesInput, OFT, R, SC>
impl<C, F, OF, R, SC> State<C, F, BytesInput, OF, R, SC>
where
C: Corpus<BytesInput>,
R: Rand,
FT: FeedbacksTuple<BytesInput>,
F: Feedback<BytesInput>,
SC: Corpus<BytesInput>,
OFT: FeedbacksTuple<BytesInput>,
OF: Feedback<BytesInput>,
{
pub fn load_from_directory<CS, E, OT, EM>(
&mut self,
@ -568,9 +568,10 @@ where
println!("Loading file {:?} ...", &path);
let bytes = fs::read(&path)?;
let input = BytesInput::new(bytes);
let (fitness, is_solution) = self.execute_input(&input, executor, manager)?;
let (is_interesting, is_solution) =
self.execute_input(&input, executor, manager)?;
if self
.add_if_interesting(&input, fitness, scheduler)?
.add_if_interesting(&input, is_interesting, scheduler)?
.is_none()
{
println!("File {:?} was not interesting, skipped.", &path);
@ -618,14 +619,14 @@ where
}
}
impl<C, FT, I, OFT, R, SC> State<C, FT, I, OFT, R, SC>
impl<C, F, I, OF, R, SC> State<C, F, I, OF, R, SC>
where
C: Corpus<I>,
I: Input,
R: Rand,
FT: FeedbacksTuple<I>,
F: Feedback<I>,
SC: Corpus<I>,
OFT: FeedbacksTuple<I>,
OF: Feedback<I>,
{
/// Runs the input and triggers observers and feedback
pub fn execute_input<E, EM, OT>(
@ -633,7 +634,7 @@ where
input: &I,
executor: &mut E,
event_mgr: &mut EM,
) -> Result<(u32, bool), Error>
) -> Result<(bool, bool), Error>
where
E: Executor<I>
+ HasObservers<OT>
@ -653,15 +654,14 @@ where
executor.post_exec_observers(self, event_mgr, input)?;
let observers = executor.observers();
let fitness = self
.feedbacks_mut()
.is_interesting_all(&input, observers, &exit_kind)?;
let is_interesting = self
.feedback_mut()
.is_interesting(&input, observers, &exit_kind)?;
let is_solution = self
.objectives_mut()
.is_interesting_all(&input, observers, &exit_kind)?
> 0;
Ok((fitness, is_solution))
.objective_mut()
.is_interesting(&input, observers, &exit_kind)?;
Ok((is_interesting, is_solution))
}
pub fn generate_initial_inputs<CS, G, E, OT, EM>(
@ -686,8 +686,8 @@ where
let mut added = 0;
for _ in 0..num {
let input = generator.generate(self.rand_mut())?;
let (fitness, _) = self.evaluate_input(input, executor, manager, scheduler)?;
if fitness > 0 {
let (is_interesting, _) = self.evaluate_input(input, executor, manager, scheduler)?;
if is_interesting {
added += 1;
}
}
@ -703,16 +703,16 @@ where
Ok(())
}
pub fn new(rand: R, corpus: C, feedbacks: FT, solutions: SC, objectives: OFT) -> Self {
pub fn new(rand: R, corpus: C, feedback: F, solutions: SC, objective: OF) -> Self {
Self {
rand,
executions: 0,
start_time: Duration::from_millis(0),
metadata: SerdeAnyMap::default(),
corpus,
feedbacks,
feedback,
solutions,
objectives,
objective,
max_size: DEFAULT_MAX_SIZE,
phantom: PhantomData,
}

View File

@ -1,6 +1,6 @@
[package]
name = "libafl_frida"
version = "0.1.0"
version = "0.2.0"
authors = ["s1341 <github@shmarya.net>"]
description = "Frida backend library for LibAFL"
documentation = "https://docs.rs/libafl_frida"
@ -13,7 +13,7 @@ edition = "2018"
cc = { version = "1.0", features = ["parallel"] }
[dependencies]
libafl = { path = "../libafl", version = "0.1.0", features = ["std", "libafl_derive"] }
libafl = { path = "../libafl", version = "0.2.0", features = ["std", "libafl_derive"] }
libafl_targets = { path = "../libafl_targets", version = "0.1.0" }
nix = "0.20.0"
libc = "0.2.92"

View File

@ -1687,18 +1687,18 @@ where
_input: &I,
observers: &OT,
_exit_kind: &ExitKind,
) -> Result<u32, Error> {
) -> Result<bool, Error> {
let observer = observers
.match_first_type::<AsanErrorsObserver>()
.expect("An AsanErrorsFeedback needs an AsanErrorsObserver");
match observer.errors() {
None => Ok(0),
None => Ok(false),
Some(errors) => {
if !errors.errors.is_empty() {
self.errors = Some(errors.clone());
Ok(1)
Ok(true)
} else {
Ok(0)
Ok(false)
}
}
}