Make Stats stage send stats again (#2830)
* Make Stats stage send stats again * re-remove stats mod * clp, fmt * clip
This commit is contained in:
parent
fd06e5ced0
commit
02566b33cd
@ -18,7 +18,7 @@ use libafl::{
|
|||||||
mutators::{havoc_mutations::havoc_mutations, scheduled::StdScheduledMutator},
|
mutators::{havoc_mutations::havoc_mutations, scheduled::StdScheduledMutator},
|
||||||
observers::StdMapObserver,
|
observers::StdMapObserver,
|
||||||
schedulers::QueueScheduler,
|
schedulers::QueueScheduler,
|
||||||
stages::mutational::StdMutationalStage,
|
stages::{mutational::StdMutationalStage, AflStatsStage, CalibrationStage},
|
||||||
state::{HasCorpus, HasExecutions, StdState, UsesState},
|
state::{HasCorpus, HasExecutions, StdState, UsesState},
|
||||||
};
|
};
|
||||||
use libafl_bolts::{current_nanos, nonzero, rands::StdRand, tuples::tuple_list, AsSlice};
|
use libafl_bolts::{current_nanos, nonzero, rands::StdRand, tuples::tuple_list, AsSlice};
|
||||||
@ -86,6 +86,12 @@ pub fn main() {
|
|||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
let mut feedback = MaxMapFeedback::new(&observer);
|
let mut feedback = MaxMapFeedback::new(&observer);
|
||||||
|
|
||||||
|
let calibration_stage = CalibrationStage::new(&feedback);
|
||||||
|
let stats_stage = AflStatsStage::builder()
|
||||||
|
.map_observer(&observer)
|
||||||
|
.build()
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
// A feedback to choose if an input is a solution or not
|
// A feedback to choose if an input is a solution or not
|
||||||
let mut objective = feedback_and_fast!(
|
let mut objective = feedback_and_fast!(
|
||||||
// Look for crashes.
|
// Look for crashes.
|
||||||
@ -151,7 +157,11 @@ pub fn main() {
|
|||||||
|
|
||||||
// Setup a mutational stage with a basic bytes mutator
|
// Setup a mutational stage with a basic bytes mutator
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
let mut stages = tuple_list!(
|
||||||
|
calibration_stage,
|
||||||
|
StdMutationalStage::new(mutator),
|
||||||
|
stats_stage
|
||||||
|
);
|
||||||
|
|
||||||
fuzzer
|
fuzzer
|
||||||
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||||
|
@ -39,6 +39,7 @@ default = [
|
|||||||
"regex",
|
"regex",
|
||||||
"serdeany_autoreg",
|
"serdeany_autoreg",
|
||||||
"libafl_bolts/xxh3",
|
"libafl_bolts/xxh3",
|
||||||
|
"tui_monitor",
|
||||||
]
|
]
|
||||||
document-features = ["dep:document-features"]
|
document-features = ["dep:document-features"]
|
||||||
|
|
||||||
@ -200,6 +201,11 @@ nautilus = [
|
|||||||
"regex",
|
"regex",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "tui_mock"
|
||||||
|
path = "./examples/tui_mock/main.rs"
|
||||||
|
required-features = ["std", "tui_monitor"]
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
rustversion = "1.0.17"
|
rustversion = "1.0.17"
|
||||||
|
|
||||||
|
20
libafl/examples/tui_mock/main.rs
Normal file
20
libafl/examples/tui_mock/main.rs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
//! An example for TUI that uses the TUI without any real data.
|
||||||
|
//! This is mainly to fix the UI without having to run a real fuzzer.
|
||||||
|
|
||||||
|
use std::{thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
use libafl::monitors::{tui::TuiMonitor, ClientStats, Monitor};
|
||||||
|
use libafl_bolts::ClientId;
|
||||||
|
|
||||||
|
pub fn main() {
|
||||||
|
let mut monitor = TuiMonitor::builder().build();
|
||||||
|
|
||||||
|
let client_stats = ClientStats {
|
||||||
|
corpus_size: 1024,
|
||||||
|
executions: 512,
|
||||||
|
..ClientStats::default()
|
||||||
|
};
|
||||||
|
|
||||||
|
monitor.display("Test", ClientId(0));
|
||||||
|
sleep(Duration::from_secs(10));
|
||||||
|
}
|
@ -313,15 +313,15 @@ impl fmt::Display for UserStatsValue {
|
|||||||
/// Prettifies float values for human-readable output
|
/// Prettifies float values for human-readable output
|
||||||
fn prettify_float(value: f64) -> String {
|
fn prettify_float(value: f64) -> String {
|
||||||
let (value, suffix) = match value {
|
let (value, suffix) = match value {
|
||||||
value if value >= 1000000.0 => (value / 1000000.0, "M"),
|
value if value >= 1_000_000.0 => (value / 1_000_000.0, "M"),
|
||||||
value if value >= 1000.0 => (value / 1000.0, "k"),
|
value if value >= 1_000.0 => (value / 1_000.0, "k"),
|
||||||
value => (value, ""),
|
value => (value, ""),
|
||||||
};
|
};
|
||||||
match value {
|
match value {
|
||||||
value if value >= 1000000.0 => {
|
value if value >= 1_000_000.0 => {
|
||||||
format!("{value:.2}{suffix}")
|
format!("{value:.2}{suffix}")
|
||||||
}
|
}
|
||||||
value if value >= 1000.0 => {
|
value if value >= 1_000.0 => {
|
||||||
format!("{value:.1}{suffix}")
|
format!("{value:.1}{suffix}")
|
||||||
}
|
}
|
||||||
value if value >= 100.0 => {
|
value if value >= 100.0 => {
|
||||||
|
@ -24,9 +24,10 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME};
|
use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME};
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase},
|
corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase},
|
||||||
events::EventFirer,
|
events::{Event, EventFirer},
|
||||||
executors::HasObservers,
|
executors::HasObservers,
|
||||||
inputs::UsesInput,
|
inputs::UsesInput,
|
||||||
|
monitors::{AggregatorOps, UserStats, UserStatsValue},
|
||||||
mutators::Tokens,
|
mutators::Tokens,
|
||||||
observers::MapObserver,
|
observers::MapObserver,
|
||||||
schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles},
|
schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles},
|
||||||
@ -35,6 +36,7 @@ use crate::{
|
|||||||
std::string::ToString,
|
std::string::ToString,
|
||||||
Error, HasMetadata, HasNamedMetadata, HasScheduler,
|
Error, HasMetadata, HasNamedMetadata, HasScheduler,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// AFL++'s default stats update interval
|
/// AFL++'s default stats update interval
|
||||||
pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60;
|
pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60;
|
||||||
|
|
||||||
@ -76,7 +78,7 @@ libafl_bolts::impl_serdeany!(FuzzTime);
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct AflStatsStage<C, E, EM, O, S, Z> {
|
pub struct AflStatsStage<C, E, EM, O, S, Z> {
|
||||||
map_observer_handle: Handle<C>,
|
map_observer_handle: Handle<C>,
|
||||||
stats_file_path: PathBuf,
|
stats_file_path: Option<PathBuf>,
|
||||||
plot_file_path: Option<PathBuf>,
|
plot_file_path: Option<PathBuf>,
|
||||||
start_time: u64,
|
start_time: u64,
|
||||||
// the number of testcases that have been fuzzed
|
// the number of testcases that have been fuzzed
|
||||||
@ -238,7 +240,7 @@ pub struct AFLPlotData<'a> {
|
|||||||
impl<C, E, EM, O, S, Z> Stage<E, EM, S, Z> for AflStatsStage<C, E, EM, O, S, Z>
|
impl<C, E, EM, O, S, Z> Stage<E, EM, S, Z> for AflStatsStage<C, E, EM, O, S, Z>
|
||||||
where
|
where
|
||||||
E: HasObservers,
|
E: HasObservers,
|
||||||
EM: EventFirer,
|
EM: EventFirer<State = S>,
|
||||||
Z: HasScheduler<<S::Corpus as Corpus>::Input, S>,
|
Z: HasScheduler<<S::Corpus as Corpus>::Input, S>,
|
||||||
S: HasImported
|
S: HasImported
|
||||||
+ HasCorpus
|
+ HasCorpus
|
||||||
@ -260,7 +262,7 @@ where
|
|||||||
fuzzer: &mut Z,
|
fuzzer: &mut Z,
|
||||||
executor: &mut E,
|
executor: &mut E,
|
||||||
state: &mut S,
|
state: &mut S,
|
||||||
_manager: &mut EM,
|
manager: &mut EM,
|
||||||
) -> Result<(), Error> {
|
) -> Result<(), Error> {
|
||||||
let Some(corpus_idx) = state.current_corpus_id()? else {
|
let Some(corpus_idx) = state.current_corpus_id()? else {
|
||||||
return Err(Error::illegal_state(
|
return Err(Error::illegal_state(
|
||||||
@ -365,6 +367,7 @@ where
|
|||||||
execs_since_crash: total_executions - self.execs_at_last_objective,
|
execs_since_crash: total_executions - self.execs_at_last_objective,
|
||||||
exec_timeout: self.exec_timeout,
|
exec_timeout: self.exec_timeout,
|
||||||
slowest_exec_ms: self.slowest_exec.as_millis(),
|
slowest_exec_ms: self.slowest_exec.as_millis(),
|
||||||
|
// TODO: getting rss_mb may take some extra millis, so might make sense to make this optional
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
peak_rss_mb: peak_rss_mb_child_processes()?,
|
peak_rss_mb: peak_rss_mb_child_processes()?,
|
||||||
#[cfg(not(unix))]
|
#[cfg(not(unix))]
|
||||||
@ -398,10 +401,36 @@ where
|
|||||||
saved_crashes: &stats.saved_crashes,
|
saved_crashes: &stats.saved_crashes,
|
||||||
execs_done: &stats.execs_done,
|
execs_done: &stats.execs_done,
|
||||||
};
|
};
|
||||||
self.write_fuzzer_stats(&stats)?;
|
self.maybe_write_fuzzer_stats(&stats)?;
|
||||||
if self.plot_file_path.is_some() {
|
if self.plot_file_path.is_some() {
|
||||||
self.write_plot_data(&plot_data)?;
|
self.write_plot_data(&plot_data)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
drop(testcase);
|
||||||
|
|
||||||
|
// We construct this simple json by hand to squeeze out some extra speed.
|
||||||
|
let json = format!(
|
||||||
|
"{{\
|
||||||
|
\"pending\":{},\
|
||||||
|
\"pending_fav\":{},\
|
||||||
|
\"own_finds:\"{},\
|
||||||
|
\"imported\":{}\
|
||||||
|
}}",
|
||||||
|
stats.pending_total, stats.pending_favs, stats.corpus_found, stats.corpus_imported
|
||||||
|
);
|
||||||
|
|
||||||
|
manager.fire(
|
||||||
|
state,
|
||||||
|
Event::UpdateUserStats {
|
||||||
|
name: Cow::Borrowed("AflStats"),
|
||||||
|
value: UserStats::new(
|
||||||
|
UserStatsValue::String(Cow::Owned(json)),
|
||||||
|
AggregatorOps::None,
|
||||||
|
),
|
||||||
|
phantom: PhantomData,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,15 +457,17 @@ where
|
|||||||
AflStatsStageBuilder::new()
|
AflStatsStageBuilder::new()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_fuzzer_stats(&self, stats: &AFLFuzzerStats) -> Result<(), Error> {
|
/// Writes a stats file, if a `stats_file_path` is set.
|
||||||
let tmp_file = self
|
fn maybe_write_fuzzer_stats(&self, stats: &AFLFuzzerStats) -> Result<(), Error> {
|
||||||
.stats_file_path
|
if let Some(stats_file_path) = &self.stats_file_path {
|
||||||
|
let tmp_file = stats_file_path
|
||||||
.parent()
|
.parent()
|
||||||
.expect("fuzzer_stats file must have a parent!")
|
.expect("fuzzer_stats file must have a parent!")
|
||||||
.join(".fuzzer_stats_tmp");
|
.join(".fuzzer_stats_tmp");
|
||||||
std::fs::write(&tmp_file, stats.to_string())?;
|
std::fs::write(&tmp_file, stats.to_string())?;
|
||||||
_ = std::fs::copy(&tmp_file, &self.stats_file_path)?;
|
_ = std::fs::copy(&tmp_file, stats_file_path)?;
|
||||||
std::fs::remove_file(tmp_file)?;
|
std::fs::remove_file(tmp_file)?;
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -557,8 +588,8 @@ impl Display for AFLPlotData<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl AFLPlotData<'_> {
|
impl AFLPlotData<'_> {
|
||||||
fn get_header() -> String {
|
fn header() -> &'static str {
|
||||||
"# relative_time, cycles_done, cur_item, corpus_count, pending_total, pending_favs, total_edges, saved_crashes, saved_hangs, max_depth, execs_per_sec, execs_done, edges_found".to_string()
|
"# relative_time, cycles_done, cur_item, corpus_count, pending_total, pending_favs, total_edges, saved_crashes, saved_hangs, max_depth, execs_per_sec, execs_done, edges_found"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl Display for AFLFuzzerStats<'_> {
|
impl Display for AFLFuzzerStats<'_> {
|
||||||
@ -736,10 +767,10 @@ where
|
|||||||
// check if it contains any data
|
// check if it contains any data
|
||||||
let file = File::open(path)?;
|
let file = File::open(path)?;
|
||||||
if BufReader::new(file).lines().next().is_none() {
|
if BufReader::new(file).lines().next().is_none() {
|
||||||
std::fs::write(path, AFLPlotData::get_header())?;
|
std::fs::write(path, AFLPlotData::header())?;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
std::fs::write(path, AFLPlotData::get_header())?;
|
std::fs::write(path, AFLPlotData::header())?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@ -757,19 +788,17 @@ where
|
|||||||
/// No `MapObserver` supplied to the builder
|
/// No `MapObserver` supplied to the builder
|
||||||
/// No `stats_file_path` provieded
|
/// No `stats_file_path` provieded
|
||||||
pub fn build(self) -> Result<AflStatsStage<C, E, EM, O, S, Z>, Error> {
|
pub fn build(self) -> Result<AflStatsStage<C, E, EM, O, S, Z>, Error> {
|
||||||
if self.stats_file_path.is_none() {
|
|
||||||
return Err(Error::illegal_argument("Must set `stats_file_path`"));
|
|
||||||
}
|
|
||||||
let stats_file_path = self.stats_file_path.unwrap();
|
|
||||||
if self.map_observer_handle.is_none() {
|
if self.map_observer_handle.is_none() {
|
||||||
return Err(Error::illegal_argument("Must set `map_observer`"));
|
return Err(Error::illegal_argument("Must set `map_observer`"));
|
||||||
}
|
}
|
||||||
if let Some(ref plot_file) = self.plot_file_path {
|
if let Some(ref plot_file) = self.plot_file_path {
|
||||||
Self::create_plot_data_file(plot_file)?;
|
Self::create_plot_data_file(plot_file)?;
|
||||||
}
|
}
|
||||||
Self::create_fuzzer_stats_file(&stats_file_path)?;
|
if let Some(stats_file_path) = &self.stats_file_path {
|
||||||
|
Self::create_fuzzer_stats_file(stats_file_path)?;
|
||||||
|
}
|
||||||
Ok(AflStatsStage {
|
Ok(AflStatsStage {
|
||||||
stats_file_path,
|
stats_file_path: self.stats_file_path,
|
||||||
plot_file_path: self.plot_file_path,
|
plot_file_path: self.plot_file_path,
|
||||||
map_observer_handle: self.map_observer_handle.unwrap(),
|
map_observer_handle: self.map_observer_handle.unwrap(),
|
||||||
start_time: current_time().as_secs(),
|
start_time: current_time().as_secs(),
|
||||||
|
@ -27,6 +27,14 @@ use crate::{
|
|||||||
Error, HasMetadata, HasNamedMetadata,
|
Error, HasMetadata, HasNamedMetadata,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// AFL++'s `CAL_CYCLES_FAST` + 1
|
||||||
|
const CAL_STAGE_START: usize = 4;
|
||||||
|
/// AFL++'s `CAL_CYCLES` + 1
|
||||||
|
const CAL_STAGE_MAX: usize = 8;
|
||||||
|
|
||||||
|
/// Default name for `CalibrationStage`; derived from AFL++
|
||||||
|
pub const CALIBRATION_STAGE_NAME: &str = "calibration";
|
||||||
|
|
||||||
/// The metadata to keep unstable entries
|
/// The metadata to keep unstable entries
|
||||||
/// Formula is same as AFL++: number of unstable entries divided by the number of filled entries.
|
/// Formula is same as AFL++: number of unstable entries divided by the number of filled entries.
|
||||||
#[cfg_attr(
|
#[cfg_attr(
|
||||||
@ -69,8 +77,6 @@ impl Default for UnstableEntriesMetadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default name for `CalibrationStage`; derived from AFL++
|
|
||||||
pub const CALIBRATION_STAGE_NAME: &str = "calibration";
|
|
||||||
/// The calibration stage will measure the average exec time and the target's stability for this input.
|
/// The calibration stage will measure the average exec time and the target's stability for this input.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct CalibrationStage<C, E, O, OT, S> {
|
pub struct CalibrationStage<C, E, O, OT, S> {
|
||||||
@ -83,9 +89,6 @@ pub struct CalibrationStage<C, E, O, OT, S> {
|
|||||||
phantom: PhantomData<(E, O, OT, S)>,
|
phantom: PhantomData<(E, O, OT, S)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CAL_STAGE_START: usize = 4; // AFL++'s CAL_CYCLES_FAST + 1
|
|
||||||
const CAL_STAGE_MAX: usize = 8; // AFL++'s CAL_CYCLES + 1
|
|
||||||
|
|
||||||
impl<C, E, EM, O, OT, S, Z> Stage<E, EM, S, Z> for CalibrationStage<C, E, O, OT, S>
|
impl<C, E, EM, O, OT, S, Z> Stage<E, EM, S, Z> for CalibrationStage<C, E, O, OT, S>
|
||||||
where
|
where
|
||||||
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers<Observers = OT>,
|
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers<Observers = OT>,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user