UserStats (#114)

* MultiStats

* custom event in MapFeedback

* fix introspection

* fix windows

* clippy

* fix nostd

* bump to 0.3.2
This commit is contained in:
Andrea Fioraldi 2021-05-20 16:49:12 +02:00 committed by GitHub
parent 921baf74b5
commit 2f54e9dc01
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
26 changed files with 329 additions and 74 deletions

View File

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

View File

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

View File

@ -34,7 +34,7 @@ use libafl::{
observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver, TimeObserver}, observers::{HitcountsMapObserver, ObserversTuple, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage, stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState}, state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats, stats::MultiStats,
Error, Error,
}; };
@ -320,13 +320,13 @@ unsafe fn fuzz(
) -> Result<(), Error> { ) -> Result<(), Error> {
let stats_closure = |s| println!("{}", s); let stats_closure = |s| println!("{}", s);
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(stats_closure); let stats = MultiStats::new(stats_closure);
#[cfg(target_os = "android")] #[cfg(target_os = "android")]
AshmemService::start().expect("Failed to start Ashmem service"); AshmemService::start().expect("Failed to start Ashmem service");
let shmem_provider = StdShMemProvider::new()?; let shmem_provider = StdShMemProvider::new()?;
let mut client_init_stats = || Ok(SimpleStats::new(stats_closure)); let mut client_init_stats = || Ok(MultiStats::new(stats_closure));
let mut run_client = |state: Option<StdState<_, _, _, _, _>>, mut mgr| { let mut run_client = |state: Option<StdState<_, _, _, _, _>>, mut mgr| {
// The restarting state will spawn the same process again as child, then restarted it each time it crashes. // The restarting state will spawn the same process again as child, then restarted it each time it crashes.

View File

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

View File

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

View File

@ -21,7 +21,7 @@ use libafl::{
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage, stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState}, state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats, stats::MultiStats,
Error, Error,
}; };
@ -49,7 +49,7 @@ pub fn main() {
/// The actual fuzzer /// The actual fuzzer
fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s)); let stats = MultiStats::new(|s| println!("{}", s));
// The restarting state will spawn the same process again as child, then restarted it each time it crashes. // The restarting state will spawn the same process again as child, then restarted it each time it crashes.
let (state, mut restarting_mgr) = match setup_restarting_mgr_std(stats, broker_port) { let (state, mut restarting_mgr) = match setup_restarting_mgr_std(stats, broker_port) {

View File

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

View File

@ -29,7 +29,7 @@ use libafl::{
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
stages::mutational::StdMutationalStage, stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState}, state::{HasCorpus, HasMetadata, StdState},
stats::SimpleStats, stats::MultiStats,
}; };
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};
@ -58,8 +58,8 @@ pub fn main() {
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
let stats_closure = |s| println!("{}", s); let stats_closure = |s| println!("{}", s);
let stats = SimpleStats::new(stats_closure); let stats = MultiStats::new(stats_closure);
let mut client_init_stats = || Ok(SimpleStats::new(stats_closure)); let mut client_init_stats = || Ok(MultiStats::new(stats_closure));
let mut run_client = |state: Option<StdState<_, _, _, _, _>>, mut restarting_mgr| { let mut run_client = |state: Option<StdState<_, _, _, _, _>>, mut restarting_mgr| {
let corpus_dirs = &[PathBuf::from("./corpus")]; let corpus_dirs = &[PathBuf::from("./corpus")];

View File

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

View File

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

View File

@ -19,7 +19,7 @@ use libafl::{
observers::{StdMapObserver, TimeObserver}, observers::{StdMapObserver, TimeObserver},
stages::{StdMutationalStage, TracingStage}, stages::{StdMutationalStage, TracingStage},
state::{HasCorpus, StdState}, state::{HasCorpus, StdState},
stats::SimpleStats, stats::MultiStats,
Error, Error,
}; };
@ -48,7 +48,7 @@ pub fn main() {
/// The actual fuzzer /// The actual fuzzer
fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> { fn fuzz(corpus_dirs: &[PathBuf], objective_dir: PathBuf, broker_port: u16) -> Result<(), Error> {
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let stats = SimpleStats::new(|s| println!("{}", s)); let stats = MultiStats::new(|s| println!("{}", s));
// The restarting state will spawn the same process again as child, then restarted it each time it crashes. // The restarting state will spawn the same process again as child, then restarted it each time it crashes.
let (state, mut restarting_mgr) = match setup_restarting_mgr_std(stats, broker_port) { let (state, mut restarting_mgr) = match setup_restarting_mgr_std(stats, broker_port) {

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libafl" name = "libafl"
version = "0.3.1" version = "0.3.2"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
description = "Slot your own fuzzers together and extend their features using Rust" description = "Slot your own fuzzers together and extend their features using Rust"
documentation = "https://docs.rs/libafl" documentation = "https://docs.rs/libafl"
@ -52,7 +52,7 @@ path = "./examples/llmp_test/main.rs"
required-features = ["std"] required-features = ["std"]
[dependencies] [dependencies]
libafl_derive = { optional = true, path = "../libafl_derive", version = "0.3.1" } libafl_derive = { optional = true, path = "../libafl_derive", version = "0.3.2" }
tuple_list = "0.1.2" tuple_list = "0.1.2"
hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible hashbrown = { version = "0.9", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
num = "0.4.0" num = "0.4.0"

View File

@ -249,7 +249,7 @@ where
let client = stats.client_stats_mut_for(sender_id); let client = stats.client_stats_mut_for(sender_id);
client.update_corpus_size(*corpus_size as u64); client.update_corpus_size(*corpus_size as u64);
client.update_executions(*executions as u64, *time); client.update_executions(*executions as u64, *time);
stats.display(event.name().to_string() + " #" + &sender_id.to_string()); stats.display(event.name().to_string(), sender_id);
Ok(BrokerEventResult::Forward) Ok(BrokerEventResult::Forward)
} }
Event::UpdateStats { Event::UpdateStats {
@ -260,7 +260,17 @@ where
// TODO: The stats buffer should be added on client add. // TODO: The stats buffer should be added on client add.
let client = stats.client_stats_mut_for(sender_id); let client = stats.client_stats_mut_for(sender_id);
client.update_executions(*executions as u64, *time); client.update_executions(*executions as u64, *time);
stats.display(event.name().to_string() + " #" + &sender_id.to_string()); stats.display(event.name().to_string(), sender_id);
Ok(BrokerEventResult::Handled)
}
Event::UpdateUserStats {
name,
value,
phantom: _,
} => {
let client = stats.client_stats_mut_for(sender_id);
client.update_user_stats(name.clone(), value.clone());
stats.display(event.name().to_string(), sender_id);
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
} }
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
@ -282,9 +292,7 @@ where
client.update_introspection_stats(**introspection_stats); client.update_introspection_stats(**introspection_stats);
// Display the stats via `.display` only on core #1 // Display the stats via `.display` only on core #1
if sender_id == 1 { stats.display(event.name().to_string(), sender_id);
stats.display(event.name().to_string() + " #" + &sender_id.to_string());
}
// Correctly handled the event // Correctly handled the event
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
@ -292,7 +300,7 @@ where
Event::Objective { objective_size } => { Event::Objective { objective_size } => {
let client = stats.client_stats_mut_for(sender_id); let client = stats.client_stats_mut_for(sender_id);
client.update_objective_size(*objective_size as u64); client.update_objective_size(*objective_size as u64);
stats.display(event.name().to_string() + " #" + &sender_id.to_string()); stats.display(event.name().to_string(), sender_id);
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
} }
Event::Log { Event::Log {
@ -301,6 +309,7 @@ where
phantom: _, phantom: _,
} => { } => {
let (_, _) = (severity_level, message); let (_, _) = (severity_level, message);
// TODO rely on Stats
#[cfg(feature = "std")] #[cfg(feature = "std")]
println!("[LOG {}]: {}", severity_level, message); println!("[LOG {}]: {}", severity_level, message);
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
@ -338,7 +347,7 @@ where
let observers: OT = postcard::from_bytes(&observers_buf)?; let observers: OT = postcard::from_bytes(&observers_buf)?;
// TODO include ExitKind in NewTestcase // TODO include ExitKind in NewTestcase
let is_interesting = let is_interesting =
fuzzer.is_interesting(state, &input, &observers, &ExitKind::Ok)?; fuzzer.is_interesting(state, self, &input, &observers, &ExitKind::Ok)?;
if fuzzer if fuzzer
.add_if_interesting(state, &input, is_interesting)? .add_if_interesting(state, &input, is_interesting)?
.is_some() .is_some()

View File

@ -9,7 +9,7 @@ use alloc::{string::String, vec::Vec};
use core::{fmt, marker::PhantomData, time::Duration}; use core::{fmt, marker::PhantomData, time::Duration};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{inputs::Input, observers::ObserversTuple, Error}; use crate::{inputs::Input, observers::ObserversTuple, stats::UserStats, Error};
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
use crate::stats::ClientPerfStats; use crate::stats::ClientPerfStats;
@ -95,6 +95,15 @@ where
/// [`PhantomData`] /// [`PhantomData`]
phantom: PhantomData<I>, phantom: PhantomData<I>,
}, },
/// New stats.
UpdateUserStats {
/// Custom user stats name
name: String,
/// Custom user stats value
value: UserStats,
/// [`PhantomData`]
phantom: PhantomData<I>,
},
/// New stats with performance stats. /// New stats with performance stats.
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
UpdatePerfStats { UpdatePerfStats {
@ -143,11 +152,16 @@ where
observers_buf: _, observers_buf: _,
time: _, time: _,
executions: _, executions: _,
} => "New Testcase", } => "Testcase",
Event::UpdateStats { Event::UpdateStats {
time: _, time: _,
executions: _, executions: _,
phantom: _, phantom: _,
}
| Event::UpdateUserStats {
name: _,
value: _,
phantom: _,
} => "Stats", } => "Stats",
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
Event::UpdatePerfStats { Event::UpdatePerfStats {

View File

@ -100,7 +100,7 @@ where
stats stats
.client_stats_mut_for(0) .client_stats_mut_for(0)
.update_executions(*executions as u64, *time); .update_executions(*executions as u64, *time);
stats.display(event.name().to_string()); stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
} }
Event::UpdateStats { Event::UpdateStats {
@ -112,7 +112,18 @@ where
stats stats
.client_stats_mut_for(0) .client_stats_mut_for(0)
.update_executions(*executions as u64, *time); .update_executions(*executions as u64, *time);
stats.display(event.name().to_string()); stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled)
}
Event::UpdateUserStats {
name,
value,
phantom: _,
} => {
stats
.client_stats_mut_for(0)
.update_user_stats(name.clone(), value.clone());
stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
} }
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
@ -125,14 +136,14 @@ where
// TODO: The stats buffer should be added on client add. // TODO: The stats buffer should be added on client add.
stats.client_stats_mut()[0].update_executions(*executions as u64, *time); stats.client_stats_mut()[0].update_executions(*executions as u64, *time);
stats.client_stats_mut()[0].update_introspection_stats(**introspection_stats); stats.client_stats_mut()[0].update_introspection_stats(**introspection_stats);
stats.display(event.name().to_string()); stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
} }
Event::Objective { objective_size } => { Event::Objective { objective_size } => {
stats stats
.client_stats_mut_for(0) .client_stats_mut_for(0)
.update_objective_size(*objective_size as u64); .update_objective_size(*objective_size as u64);
stats.display(event.name().to_string()); stats.display(event.name().to_string(), 0);
Ok(BrokerEventResult::Handled) Ok(BrokerEventResult::Handled)
} }
Event::Log { Event::Log {

View File

@ -365,7 +365,7 @@ mod unix_signal_handler {
let interesting = fuzzer let interesting = fuzzer
.objective_mut() .objective_mut()
.is_interesting(state, &input, observers, &ExitKind::Timeout) .is_interesting(state, event_mgr, &input, observers, &ExitKind::Timeout)
.expect("In timeout handler objective failure."); .expect("In timeout handler objective failure.");
if interesting { if interesting {
@ -514,7 +514,7 @@ mod unix_signal_handler {
let interesting = fuzzer let interesting = fuzzer
.objective_mut() .objective_mut()
.is_interesting(state, &input, observers, &ExitKind::Crash) .is_interesting(state, event_mgr, &input, observers, &ExitKind::Crash)
.expect("In crash handler objective failure."); .expect("In crash handler objective failure.");
if interesting { if interesting {
@ -658,7 +658,7 @@ mod windows_exception_handler {
let interesting = fuzzer let interesting = fuzzer
.objective_mut() .objective_mut()
.is_interesting(state, &input, observers, &ExitKind::Crash) .is_interesting(state, event_mgr, &input, observers, &ExitKind::Crash)
.expect("In crash handler objective failure."); .expect("In crash handler objective failure.");
if interesting { if interesting {

View File

@ -11,11 +11,13 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
bolts::{tuples::Named, AsSlice}, bolts::{tuples::Named, AsSlice},
corpus::Testcase, corpus::Testcase,
events::{Event, EventFirer},
executors::ExitKind, executors::ExitKind,
feedbacks::{Feedback, FeedbackState, FeedbackStatesTuple}, feedbacks::{Feedback, FeedbackState, FeedbackStatesTuple},
inputs::Input, inputs::Input,
observers::{MapObserver, ObserversTuple}, observers::{MapObserver, ObserversTuple},
state::{HasFeedbackStates, HasMetadata}, state::{HasFeedbackStates, HasMetadata},
stats::UserStats,
Error, Error,
}; };
@ -212,14 +214,16 @@ where
S: HasFeedbackStates<FT>, S: HasFeedbackStates<FT>,
FT: FeedbackStatesTuple, FT: FeedbackStatesTuple,
{ {
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
_input: &I, _input: &I,
observers: &OT, observers: &OT,
_exit_kind: &ExitKind, _exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
let mut interesting = false; let mut interesting = false;
@ -248,6 +252,7 @@ where
for i in 0..size { for i in 0..size {
let history = map_state.history_map[i]; let history = map_state.history_map[i];
let item = observer.map()[i]; let item = observer.map()[i];
// TODO maybe walk again the histroy map only when it is interesting is more efficient
if item != initial { if item != initial {
self.indexes.as_mut().unwrap().push(i); self.indexes.as_mut().unwrap().push(i);
} }
@ -287,6 +292,23 @@ where
} }
} }
if interesting {
let mut filled = 0;
for i in 0..size {
if map_state.history_map[i] != initial {
filled += 1;
}
}
manager.fire(
state,
Event::UpdateUserStats {
name: self.name.to_string(),
value: UserStats::Ratio(filled, size as u64),
phantom: PhantomData,
},
)?;
}
Ok(interesting) Ok(interesting)
} }
@ -433,14 +455,16 @@ where
I: Input, I: Input,
O: MapObserver<usize>, O: MapObserver<usize>,
{ {
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
_state: &mut S, _state: &mut S,
_manager: &mut EM,
_input: &I, _input: &I,
observers: &OT, observers: &OT,
_exit_kind: &ExitKind, _exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
// TODO Replace with match_name_type when stable // TODO Replace with match_name_type when stable

View File

@ -10,6 +10,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
bolts::tuples::{MatchName, Named}, bolts::tuples::{MatchName, Named},
corpus::Testcase, corpus::Testcase,
events::EventFirer,
executors::ExitKind, executors::ExitKind,
inputs::Input, inputs::Input,
observers::{ObserversTuple, TimeObserver}, observers::{ObserversTuple, TimeObserver},
@ -29,20 +30,24 @@ where
I: Input, I: Input,
{ {
/// `is_interesting ` return if an input is worth the addition to the corpus /// `is_interesting ` return if an input is worth the addition to the corpus
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
input: &I, input: &I,
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple; OT: ObserversTuple;
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
fn is_interesting_with_perf<OT>( #[allow(clippy::too_many_arguments)]
fn is_interesting_with_perf<EM, OT>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
input: &I, input: &I,
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
@ -50,13 +55,14 @@ where
feedback_index: usize, feedback_index: usize,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
// Start a timer for this feedback // Start a timer for this feedback
let start_time = crate::cpu::read_time_counter(); let start_time = crate::cpu::read_time_counter();
// Execute this feedback // Execute this feedback
let ret = self.is_interesting(state, input, observers, &exit_kind); let ret = self.is_interesting(state, manager, input, observers, &exit_kind);
// Get the elapsed time for checking this feedback // Get the elapsed time for checking this feedback
let elapsed = crate::cpu::read_time_counter() - start_time; let elapsed = crate::cpu::read_time_counter() - start_time;
@ -124,29 +130,32 @@ where
B: Feedback<I, S>, B: Feedback<I, S>,
I: Input, I: Input,
{ {
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
input: &I, input: &I,
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
let a = self let a = self
.first .first
.is_interesting(state, input, observers, exit_kind)?; .is_interesting(state, manager, input, observers, exit_kind)?;
let b = self let b = self
.second .second
.is_interesting(state, input, observers, exit_kind)?; .is_interesting(state, manager, input, observers, exit_kind)?;
Ok(a && b) Ok(a && b)
} }
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
fn is_interesting_with_perf<OT>( fn is_interesting_with_perf<EM, OT>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
input: &I, input: &I,
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
@ -154,11 +163,13 @@ where
feedback_index: usize, feedback_index: usize,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
// Execute this feedback // Execute this feedback
let a = self.first.is_interesting_with_perf( let a = self.first.is_interesting_with_perf(
state, state,
manager,
input, input,
observers, observers,
&exit_kind, &exit_kind,
@ -167,6 +178,7 @@ where
)?; )?;
let b = self.second.is_interesting_with_perf( let b = self.second.is_interesting_with_perf(
state, state,
manager,
input, input,
observers, observers,
&exit_kind, &exit_kind,
@ -241,29 +253,32 @@ where
B: Feedback<I, S>, B: Feedback<I, S>,
I: Input, I: Input,
{ {
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
input: &I, input: &I,
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
let a = self let a = self
.first .first
.is_interesting(state, input, observers, exit_kind)?; .is_interesting(state, manager, input, observers, exit_kind)?;
let b = self let b = self
.second .second
.is_interesting(state, input, observers, exit_kind)?; .is_interesting(state, manager, input, observers, exit_kind)?;
Ok(a || b) Ok(a || b)
} }
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
fn is_interesting_with_perf<OT>( fn is_interesting_with_perf<EM, OT>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
input: &I, input: &I,
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
@ -271,11 +286,13 @@ where
feedback_index: usize, feedback_index: usize,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
// Execute this feedback // Execute this feedback
let a = self.first.is_interesting_with_perf( let a = self.first.is_interesting_with_perf(
state, state,
manager,
input, input,
observers, observers,
&exit_kind, &exit_kind,
@ -284,6 +301,7 @@ where
)?; )?;
let b = self.second.is_interesting_with_perf( let b = self.second.is_interesting_with_perf(
state, state,
manager,
input, input,
observers, observers,
&exit_kind, &exit_kind,
@ -354,19 +372,21 @@ where
A: Feedback<I, S>, A: Feedback<I, S>,
I: Input, I: Input,
{ {
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
input: &I, input: &I,
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
Ok(!self Ok(!self
.first .first
.is_interesting(state, input, observers, exit_kind)?) .is_interesting(state, manager, input, observers, exit_kind)?)
} }
#[inline] #[inline]
@ -442,14 +462,16 @@ impl<I, S> Feedback<I, S> for ()
where where
I: Input, I: Input,
{ {
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
_state: &mut S, _state: &mut S,
_manager: &mut EM,
_input: &I, _input: &I,
_observers: &OT, _observers: &OT,
_exit_kind: &ExitKind, _exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
Ok(false) Ok(false)
@ -471,14 +493,16 @@ impl<I, S> Feedback<I, S> for CrashFeedback
where where
I: Input, I: Input,
{ {
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
_state: &mut S, _state: &mut S,
_manager: &mut EM,
_input: &I, _input: &I,
_observers: &OT, _observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
if let ExitKind::Crash = exit_kind { if let ExitKind::Crash = exit_kind {
@ -518,14 +542,16 @@ impl<I, S> Feedback<I, S> for TimeoutFeedback
where where
I: Input, I: Input,
{ {
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
_state: &mut S, _state: &mut S,
_manager: &mut EM,
_input: &I, _input: &I,
_observers: &OT, _observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
if let ExitKind::Timeout = exit_kind { if let ExitKind::Timeout = exit_kind {
@ -570,14 +596,16 @@ impl<I, S> Feedback<I, S> for TimeFeedback
where where
I: Input, I: Input,
{ {
fn is_interesting<OT>( fn is_interesting<EM, OT>(
&mut self, &mut self,
_state: &mut S, _state: &mut S,
_manager: &mut EM,
_input: &I, _input: &I,
observers: &OT, observers: &OT,
_exit_kind: &ExitKind, _exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
EM: EventFirer<I, S>,
OT: ObserversTuple, OT: ObserversTuple,
{ {
// TODO Replace with match_name_type when stable // TODO Replace with match_name_type when stable

View File

@ -3,7 +3,7 @@
use crate::{ use crate::{
bolts::current_time, bolts::current_time,
corpus::{Corpus, CorpusScheduler, Testcase}, corpus::{Corpus, CorpusScheduler, Testcase},
events::{Event, EventManager}, events::{Event, EventFirer, EventManager},
executors::{ executors::{
Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks, Executor, ExitKind, HasExecHooks, HasExecHooksTuple, HasObservers, HasObserversHooks,
}, },
@ -69,15 +69,19 @@ where
pub trait IsInteresting<I, OT, S> pub trait IsInteresting<I, OT, S>
where where
OT: ObserversTuple, OT: ObserversTuple,
I: Input,
{ {
/// Evaluate if a set of observation channels has an interesting state /// Evaluate if a set of observation channels has an interesting state
fn is_interesting( fn is_interesting<EM>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
input: &I, input: &I,
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<bool, Error>; ) -> Result<bool, Error>
where
EM: EventFirer<I, S>;
} }
/// Add to the state if interesting /// Add to the state if interesting
@ -259,18 +263,19 @@ where
S: HasCorpus<C, I>, S: HasCorpus<C, I>,
{ {
/// Evaluate if a set of observation channels has an interesting state /// Evaluate if a set of observation channels has an interesting state
fn is_interesting( fn is_interesting<EM>(
&mut self, &mut self,
state: &mut S, state: &mut S,
manager: &mut EM,
input: &I, input: &I,
observers: &OT, observers: &OT,
exit_kind: &ExitKind, exit_kind: &ExitKind,
) -> Result<bool, Error> ) -> Result<bool, Error>
where where
OT: ObserversTuple, EM: EventFirer<I, S>,
{ {
self.feedback_mut() self.feedback_mut()
.is_interesting(state, input, observers, exit_kind) .is_interesting(state, manager, input, observers, exit_kind)
} }
} }
@ -521,7 +526,7 @@ where
#[cfg(not(feature = "introspection"))] #[cfg(not(feature = "introspection"))]
let is_interesting = self let is_interesting = self
.feedback_mut() .feedback_mut()
.is_interesting(state, &input, observers, &exit_kind)?; .is_interesting(state, event_mgr, &input, observers, &exit_kind)?;
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
let is_interesting = { let is_interesting = {
@ -533,6 +538,7 @@ where
let feedback_index = 0; let feedback_index = 0;
let is_interesting = self.feedback_mut().is_interesting_with_perf( let is_interesting = self.feedback_mut().is_interesting_with_perf(
state, state,
event_mgr,
&input, &input,
observers, observers,
&exit_kind, &exit_kind,
@ -552,7 +558,7 @@ where
start_timer!(state); start_timer!(state);
let is_solution = self let is_solution = self
.objective_mut() .objective_mut()
.is_interesting(state, &input, observers, &exit_kind)?; .is_interesting(state, event_mgr, &input, observers, &exit_kind)?;
mark_feature_time!(state, PerfFeature::GetObjectivesInterestingAll); mark_feature_time!(state, PerfFeature::GetObjectivesInterestingAll);

View File

@ -1,9 +1,14 @@
//! Keep stats, and dispaly them to the user. Usually used in a broker, or main node, of some sort. //! Keep stats, and dispaly them to the user. Usually used in a broker, or main node, of some sort.
pub mod multi;
pub use multi::MultiStats;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
use core::{time, time::Duration}; use core::{fmt, time, time::Duration};
use hashbrown::HashMap;
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
use alloc::string::ToString; use alloc::string::ToString;
@ -14,6 +19,30 @@ use crate::bolts::current_time;
const CLIENT_STATS_TIME_WINDOW_SECS: u64 = 5; // 5 seconds const CLIENT_STATS_TIME_WINDOW_SECS: u64 = 5; // 5 seconds
/// User-defined stats types
#[derive(Serialize, Deserialize, Debug, Clone)]
pub enum UserStats {
Number(u64),
String(String),
Ratio(u64, u64),
}
impl fmt::Display for UserStats {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UserStats::Number(n) => write!(f, "{}", n),
UserStats::String(s) => write!(f, "{}", s),
UserStats::Ratio(a, b) => {
if *b == 0 {
write!(f, "{}/{}", a, b)
} else {
write!(f, "{}/{} ({}%)", a, b, a * 100 / b)
}
}
}
}
}
/// A simple struct to keep track of client stats /// A simple struct to keep track of client stats
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ClientStats { pub struct ClientStats {
@ -30,6 +59,8 @@ pub struct ClientStats {
pub last_window_time: time::Duration, pub last_window_time: time::Duration,
/// The last executions per sec /// The last executions per sec
pub last_execs_per_sec: f32, pub last_execs_per_sec: f32,
/// User-defined stats
pub user_stats: HashMap<String, UserStats>,
/// Client performance statistics /// Client performance statistics
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
@ -90,6 +121,16 @@ impl ClientStats {
self.last_execs_per_sec as u64 self.last_execs_per_sec as u64
} }
/// Update the user-defined stat with name and value
pub fn update_user_stats(&mut self, name: String, value: UserStats) {
self.user_stats.insert(name, value);
}
/// Get a user-defined stat using the name
pub fn get_user_stats(&mut self, name: &str) -> Option<&UserStats> {
self.user_stats.get(name)
}
/// Update the current [`ClientPerfStats`] with the given [`ClientPerfStats`] /// Update the current [`ClientPerfStats`] with the given [`ClientPerfStats`]
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]
pub fn update_introspection_stats(&mut self, introspection_stats: ClientPerfStats) { pub fn update_introspection_stats(&mut self, introspection_stats: ClientPerfStats) {
@ -109,7 +150,7 @@ pub trait Stats {
fn start_time(&mut self) -> time::Duration; fn start_time(&mut self) -> time::Duration;
/// show the stats to the user /// show the stats to the user
fn display(&mut self, event_msg: String); fn display(&mut self, event_msg: String, sender_id: u32);
/// Amount of elements in the corpus (combined for all children) /// Amount of elements in the corpus (combined for all children)
fn corpus_size(&self) -> u64 { fn corpus_size(&self) -> u64 {
@ -186,10 +227,11 @@ where
self.start_time self.start_time
} }
fn display(&mut self, event_msg: String) { fn display(&mut self, event_msg: String, sender_id: u32) {
let fmt = format!( let fmt = format!(
"[{}] clients: {}, corpus: {}, objectives: {}, executions: {}, exec/sec: {}", "[{} #{}] clients: {}, corpus: {}, objectives: {}, executions: {}, exec/sec: {}",
event_msg, event_msg,
sender_id,
self.client_stats().len(), self.client_stats().len(),
self.corpus_size(), self.corpus_size(),
self.objective_size(), self.objective_size(),

115
libafl/src/stats/multi.rs Normal file
View File

@ -0,0 +1,115 @@
//! Stats to disply both cumulative and per-client stats
use alloc::{string::String, vec::Vec};
use core::{time, time::Duration};
#[cfg(feature = "introspection")]
use alloc::string::ToString;
use crate::{
bolts::current_time,
stats::{ClientStats, Stats},
};
/// Tracking stats during fuzzing and display both per-client and cumulative info.
#[derive(Clone, Debug)]
pub struct MultiStats<F>
where
F: FnMut(String),
{
print_fn: F,
start_time: Duration,
corpus_size: usize,
client_stats: Vec<ClientStats>,
}
impl<F> Stats for MultiStats<F>
where
F: FnMut(String),
{
/// the client stats, mutable
fn client_stats_mut(&mut self) -> &mut Vec<ClientStats> {
&mut self.client_stats
}
/// the client stats
fn client_stats(&self) -> &[ClientStats] {
&self.client_stats
}
/// Time this fuzzing run stated
fn start_time(&mut self) -> time::Duration {
self.start_time
}
fn display(&mut self, event_msg: String, sender_id: u32) {
let pad = if event_msg.len() < 9 {
" ".repeat(9 - event_msg.len())
} else {
String::new()
};
let head = format!("{}{} #{}", event_msg, pad, sender_id);
let global_fmt = format!(
"[{}] (GLOBAL) clients: {}, corpus: {}, objectives: {}, executions: {}, exec/sec: {}",
head,
self.client_stats().len(),
self.corpus_size(),
self.objective_size(),
self.total_execs(),
self.execs_per_sec()
);
(self.print_fn)(global_fmt);
let client = self.client_stats_mut_for(sender_id);
let cur_time = current_time();
let exec_sec = client.execs_per_sec(cur_time);
let pad = " ".repeat(head.len());
let mut fmt = format!(
" {} (CLIENT) corpus: {}, objectives: {}, executions: {}, exec/sec: {}",
pad, client.corpus_size, client.objective_size, client.executions, exec_sec
);
for (key, val) in &client.user_stats {
fmt += &format!(", {}: {}", key, val);
}
(self.print_fn)(fmt);
// Only print perf stats if the feature is enabled
#[cfg(feature = "introspection")]
{
// Print the client performance stats. Skip the Client 0 which is the broker
for (i, client) in self.client_stats.iter().skip(1).enumerate() {
let fmt = format!("Client {:03}: {}", i + 1, client.introspection_stats);
(self.print_fn)(fmt);
}
// Separate the spacing just a bit
(self.print_fn)("\n".to_string());
}
}
}
impl<F> MultiStats<F>
where
F: FnMut(String),
{
/// Creates the stats, using the `current_time` as `start_time`.
pub fn new(print_fn: F) -> Self {
Self {
print_fn,
start_time: current_time(),
corpus_size: 0,
client_stats: vec![],
}
}
/// Creates the stats with a given `start_time`.
pub fn with_time(print_fn: F, start_time: time::Duration) -> Self {
Self {
print_fn,
start_time,
corpus_size: 0,
client_stats: vec![],
}
}
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libafl_cc" name = "libafl_cc"
version = "0.3.1" version = "0.3.2"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
description = "Commodity library to wrap compilers and link LibAFL" description = "Commodity library to wrap compilers and link LibAFL"
documentation = "https://docs.rs/libafl_cc" documentation = "https://docs.rs/libafl_cc"

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libafl_derive" name = "libafl_derive"
version = "0.3.1" version = "0.3.2"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
description = "Derive proc-macro crate for LibAFL" description = "Derive proc-macro crate for LibAFL"
documentation = "https://docs.rs/libafl_derive" documentation = "https://docs.rs/libafl_derive"

View File

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

View File

@ -14,6 +14,7 @@ use libafl::{
tuples::Named, tuples::Named,
}, },
corpus::Testcase, corpus::Testcase,
events::EventFirer,
executors::{CustomExitKind, ExitKind, HasExecHooks}, executors::{CustomExitKind, ExitKind, HasExecHooks},
feedbacks::Feedback, feedbacks::Feedback,
inputs::{HasTargetBytes, Input}, inputs::{HasTargetBytes, Input},
@ -1777,13 +1778,18 @@ impl<I, S> Feedback<I, S> for AsanErrorsFeedback
where where
I: Input + HasTargetBytes, I: Input + HasTargetBytes,
{ {
fn is_interesting<OT: ObserversTuple>( fn is_interesting<EM, OT>(
&mut self, &mut self,
_state: &mut S, _state: &mut S,
_manager: &mut EM,
_input: &I, _input: &I,
observers: &OT, observers: &OT,
_exit_kind: &ExitKind, _exit_kind: &ExitKind,
) -> Result<bool, Error> { ) -> Result<bool, Error>
where
EM: EventFirer<I, S>,
OT: ObserversTuple,
{
let observer = observers let observer = observers
.match_name::<AsanErrorsObserver>("AsanErrors") .match_name::<AsanErrorsObserver>("AsanErrors")
.expect("An AsanErrorsFeedback needs an AsanErrorsObserver"); .expect("An AsanErrorsFeedback needs an AsanErrorsObserver");

View File

@ -1,6 +1,6 @@
[package] [package]
name = "libafl_targets" name = "libafl_targets"
version = "0.3.1" version = "0.3.2"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>"]
description = "Common code for target instrumentation that can be used combined with LibAFL" description = "Common code for target instrumentation that can be used combined with LibAFL"
documentation = "https://docs.rs/libafl_targets" documentation = "https://docs.rs/libafl_targets"
@ -25,6 +25,6 @@ cc = { version = "1.0", features = ["parallel"] }
[dependencies] [dependencies]
rangemap = "0.1.10" rangemap = "0.1.10"
libafl = { path = "../libafl", version = "0.3.1", features = [] } libafl = { path = "../libafl", version = "0.3.2", features = [] }
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
serde-big-array = "0.3.2" serde-big-array = "0.3.2"