Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions fuzzers/baby/baby_fuzzer_custom_executor/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use libafl::{
mutators::{havoc_mutations::havoc_mutations, scheduled::StdScheduledMutator},
observers::StdMapObserver,
schedulers::QueueScheduler,
stages::mutational::StdMutationalStage,
stages::{mutational::StdMutationalStage, AflStatsStage, CalibrationStage},
state::{HasCorpus, HasExecutions, StdState, UsesState},
};
use libafl_bolts::{current_nanos, nonzero, rands::StdRand, tuples::tuple_list, AsSlice};
Expand Down Expand Up @@ -86,6 +86,12 @@ pub fn main() {
// Feedback to rate the interestingness of an input
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
let mut objective = feedback_and_fast!(
// Look for crashes.
Expand Down Expand Up @@ -151,7 +157,11 @@ pub fn main() {

// Setup a mutational stage with a basic bytes mutator
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
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
Expand Down
6 changes: 6 additions & 0 deletions libafl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ default = [
"regex",
"serdeany_autoreg",
"libafl_bolts/xxh3",
"tui_monitor",
]
document-features = ["dep:document-features"]

Expand Down Expand Up @@ -200,6 +201,11 @@ nautilus = [
"regex",
]

[[example]]
name = "tui_mock"
path = "./examples/tui_mock/main.rs"
required-features = ["std", "tui_monitor"]

[build-dependencies]
rustversion = "1.0.17"

Expand Down
20 changes: 20 additions & 0 deletions libafl/examples/tui_mock/main.rs
Original file line number Diff line number Diff line change
@@ -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));
}
8 changes: 4 additions & 4 deletions libafl/src/monitors/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,15 +313,15 @@ impl fmt::Display for UserStatsValue {
/// Prettifies float values for human-readable output
fn prettify_float(value: f64) -> String {
let (value, suffix) = match value {
value if value >= 1000000.0 => (value / 1000000.0, "M"),
value if value >= 1000.0 => (value / 1000.0, "k"),
value if value >= 1_000_000.0 => (value / 1_000_000.0, "M"),
value if value >= 1_000.0 => (value / 1_000.0, "k"),
value => (value, ""),
};
match value {
value if value >= 1000000.0 => {
value if value >= 1_000_000.0 => {
format!("{value:.2}{suffix}")
}
value if value >= 1000.0 => {
value if value >= 1_000.0 => {
format!("{value:.1}{suffix}")
}
value if value >= 100.0 => {
Expand Down
77 changes: 53 additions & 24 deletions libafl/src/stages/afl_stats.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,10 @@ use serde::{Deserialize, Serialize};
use crate::feedbacks::{CRASH_FEEDBACK_NAME, TIMEOUT_FEEDBACK_NAME};
use crate::{
corpus::{Corpus, HasCurrentCorpusId, SchedulerTestcaseMetadata, Testcase},
events::EventFirer,
events::{Event, EventFirer},
executors::HasObservers,
inputs::UsesInput,
monitors::{AggregatorOps, UserStats, UserStatsValue},
mutators::Tokens,
observers::MapObserver,
schedulers::{minimizer::IsFavoredMetadata, HasQueueCycles},
Expand All @@ -35,6 +36,7 @@ use crate::{
std::string::ToString,
Error, HasMetadata, HasNamedMetadata, HasScheduler,
};

/// AFL++'s default stats update interval
pub const AFL_FUZZER_STATS_UPDATE_INTERVAL_SECS: u64 = 60;

Expand Down Expand Up @@ -76,7 +78,7 @@ libafl_bolts::impl_serdeany!(FuzzTime);
#[derive(Debug, Clone)]
pub struct AflStatsStage<C, E, EM, O, S, Z> {
map_observer_handle: Handle<C>,
stats_file_path: PathBuf,
stats_file_path: Option<PathBuf>,
plot_file_path: Option<PathBuf>,
start_time: u64,
// the number of testcases that have been fuzzed
Expand Down Expand Up @@ -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>
where
E: HasObservers,
EM: EventFirer,
EM: EventFirer<State = S>,
Z: HasScheduler<<S::Corpus as Corpus>::Input, S>,
S: HasImported
+ HasCorpus
Expand All @@ -260,7 +262,7 @@ where
fuzzer: &mut Z,
executor: &mut E,
state: &mut S,
_manager: &mut EM,
manager: &mut EM,
) -> Result<(), Error> {
let Some(corpus_idx) = state.current_corpus_id()? else {
return Err(Error::illegal_state(
Expand Down Expand Up @@ -365,6 +367,7 @@ where
execs_since_crash: total_executions - self.execs_at_last_objective,
exec_timeout: self.exec_timeout,
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)]
peak_rss_mb: peak_rss_mb_child_processes()?,
#[cfg(not(unix))]
Expand Down Expand Up @@ -398,10 +401,36 @@ where
saved_crashes: &stats.saved_crashes,
execs_done: &stats.execs_done,
};
self.write_fuzzer_stats(&stats)?;
self.maybe_write_fuzzer_stats(&stats)?;
if self.plot_file_path.is_some() {
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(())
}

Expand All @@ -428,15 +457,17 @@ where
AflStatsStageBuilder::new()
}

fn write_fuzzer_stats(&self, stats: &AFLFuzzerStats) -> Result<(), Error> {
let tmp_file = self
.stats_file_path
.parent()
.expect("fuzzer_stats file must have a parent!")
.join(".fuzzer_stats_tmp");
std::fs::write(&tmp_file, stats.to_string())?;
_ = std::fs::copy(&tmp_file, &self.stats_file_path)?;
std::fs::remove_file(tmp_file)?;
/// Writes a stats file, if a `stats_file_path` is set.
fn maybe_write_fuzzer_stats(&self, stats: &AFLFuzzerStats) -> Result<(), Error> {
if let Some(stats_file_path) = &self.stats_file_path {
let tmp_file = stats_file_path
.parent()
.expect("fuzzer_stats file must have a parent!")
.join(".fuzzer_stats_tmp");
std::fs::write(&tmp_file, stats.to_string())?;
_ = std::fs::copy(&tmp_file, stats_file_path)?;
std::fs::remove_file(tmp_file)?;
}
Ok(())
}

Expand Down Expand Up @@ -557,8 +588,8 @@ impl Display for AFLPlotData<'_> {
}
}
impl AFLPlotData<'_> {
fn get_header() -> 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".to_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"
}
}
impl Display for AFLFuzzerStats<'_> {
Expand Down Expand Up @@ -736,10 +767,10 @@ where
// check if it contains any data
let file = File::open(path)?;
if BufReader::new(file).lines().next().is_none() {
std::fs::write(path, AFLPlotData::get_header())?;
std::fs::write(path, AFLPlotData::header())?;
}
} else {
std::fs::write(path, AFLPlotData::get_header())?;
std::fs::write(path, AFLPlotData::header())?;
}
Ok(())
}
Expand All @@ -757,19 +788,17 @@ where
/// No `MapObserver` supplied to the builder
/// No `stats_file_path` provieded
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() {
return Err(Error::illegal_argument("Must set `map_observer`"));
}
if let Some(ref plot_file) = self.plot_file_path {
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 {
stats_file_path,
stats_file_path: self.stats_file_path,
plot_file_path: self.plot_file_path,
map_observer_handle: self.map_observer_handle.unwrap(),
start_time: current_time().as_secs(),
Expand Down
13 changes: 8 additions & 5 deletions libafl/src/stages/calibrate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ use crate::{
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
/// Formula is same as AFL++: number of unstable entries divided by the number of filled entries.
#[cfg_attr(
Expand Down Expand Up @@ -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.
#[derive(Clone, Debug)]
pub struct CalibrationStage<C, E, O, OT, S> {
Expand All @@ -83,9 +89,6 @@ pub struct CalibrationStage<C, 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>
where
E: Executor<EM, <S::Corpus as Corpus>::Input, S, Z> + HasObservers<Observers = OT>,
Expand Down
Loading