LIBAFL_DEBUG_OUTPUT in Launcher and OnDiskTOMLMonitor to create fuzzer_stats (#666)

* LIBAFL_DEBUG_OUTPUT in launcher on unix

* OnDiskTOMLMonitor

* fix

* clp

* clippy

* fix

* fix

* allow all

Co-authored-by: tokatoka <tokazerkje@outlook.com>
This commit is contained in:
Andrea Fioraldi 2022-06-08 17:32:58 +02:00 committed by GitHub
parent 2e746bf439
commit 323b8e23ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 178 additions and 13 deletions

View File

@ -1 +1,2 @@
libpng-* fuzzer_stats.toml
libpng-*

View File

@ -27,7 +27,7 @@ use libafl::{
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback, TimeoutFeedback},
fuzzer::{Fuzzer, StdFuzzer}, fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes}, inputs::{BytesInput, HasTargetBytes},
monitors::tui::TuiMonitor, monitors::{MultiMonitor, OnDiskTOMLMonitor},
mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator}, mutators::scheduled::{havoc_mutations, tokens_mutations, StdScheduledMutator},
mutators::token_mutations::Tokens, mutators::token_mutations::Tokens,
observers::{HitcountsMapObserver, StdMapObserver, TimeObserver}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
@ -139,7 +139,10 @@ pub fn libafl_main() {
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory"); let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
let monitor = TuiMonitor::new("Test fuzzer on libpng".into(), true); let monitor = OnDiskTOMLMonitor::new(
"./fuzzer_stats.toml",
MultiMonitor::new(|s| println!("{}", s)),
);
let mut run_client = |state: Option<_>, mut restarting_mgr, _core_id| { let mut run_client = |state: Option<_>, mut restarting_mgr, _core_id| {
// Create an observation channel using the coverage map // Create an observation channel using the coverage map

View File

@ -142,6 +142,9 @@ where
.stdout_file .stdout_file
.map(|filename| File::create(filename).unwrap()); .map(|filename| File::create(filename).unwrap());
#[cfg(feature = "std")]
let debug_output = std::env::var("LIBAFL_DEBUG_OUTPUT").is_ok();
// Spawn clients // Spawn clients
let mut index = 0_u64; let mut index = 0_u64;
for (id, bind_to) in core_ids.iter().enumerate().take(num_cores) { for (id, bind_to) in core_ids.iter().enumerate().take(num_cores) {
@ -163,10 +166,13 @@ where
std::thread::sleep(std::time::Duration::from_millis(index * 100)); std::thread::sleep(std::time::Duration::from_millis(index * 100));
#[cfg(feature = "std")] #[cfg(feature = "std")]
if let Some(file) = stdout_file { if !debug_output {
dup2(file.as_raw_fd(), libc::STDOUT_FILENO)?; if let Some(file) = stdout_file {
dup2(file.as_raw_fd(), libc::STDERR_FILENO)?; dup2(file.as_raw_fd(), libc::STDOUT_FILENO)?;
dup2(file.as_raw_fd(), libc::STDERR_FILENO)?;
}
} }
// Fuzzer client. keeps retrying the connection to broker till the broker starts // Fuzzer client. keeps retrying the connection to broker till the broker starts
let (state, mgr) = RestartingMgr::<I, MT, OT, S, SP>::builder() let (state, mgr) = RestartingMgr::<I, MT, OT, S, SP>::builder()
.shmem_provider(self.shmem_provider.clone()) .shmem_provider(self.shmem_provider.clone())

View File

@ -205,7 +205,7 @@ where
fn shmem_from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result<Self::ShMem, Error> { fn shmem_from_id_and_size(&mut self, id: ShMemId, size: usize) -> Result<Self::ShMem, Error> {
let parts = id.as_str().split(':').collect::<Vec<&str>>(); let parts = id.as_str().split(':').collect::<Vec<&str>>();
let server_id_str = parts.get(0).unwrap(); let server_id_str = parts.first().unwrap();
let (server_fd, client_fd) = self.send_receive(ServedShMemRequest::ExistingMap( let (server_fd, client_fd) = self.send_receive(ServedShMemRequest::ExistingMap(
ShMemDescription::from_string_and_size(server_id_str, size), ShMemDescription::from_string_and_size(server_id_str, size),
))?; ))?;
@ -431,7 +431,8 @@ where
// It's either running at this point, or we won't be able to spawn it anyway. // It's either running at this point, or we won't be able to spawn it anyway.
env::set_var(AFL_SHMEM_SERVICE_STARTED, "true"); env::set_var(AFL_SHMEM_SERVICE_STARTED, "true");
match *success { let status = *success;
match status {
ShMemServiceStatus::Starting => panic!("Unreachable"), ShMemServiceStatus::Starting => panic!("Unreachable"),
ShMemServiceStatus::Started => { ShMemServiceStatus::Started => {
println!("Started ShMem Service"); println!("Started ShMem Service");

View File

@ -332,6 +332,8 @@ where
name: String, name: String,
/// Name identifier of the observer /// Name identifier of the observer
observer_name: String, observer_name: String,
/// Name of the feedback as shown in the `UserStats`
stats_name: String,
/// Phantom Data of Reducer /// Phantom Data of Reducer
phantom: PhantomData<(I, N, S, R, O, T)>, phantom: PhantomData<(I, N, S, R, O, T)>,
} }
@ -416,7 +418,7 @@ where
manager.fire( manager.fire(
state, state,
Event::UpdateUserStats { Event::UpdateUserStats {
name: self.name.to_string(), name: self.stats_name.to_string(),
value: UserStats::Ratio(filled, size as u64), value: UserStats::Ratio(filled, size as u64),
phantom: PhantomData, phantom: PhantomData,
}, },
@ -480,6 +482,10 @@ where
} }
} }
fn create_stats_name(name: &str) -> String {
name.to_lowercase()
}
impl<I, N, O, R, S, T> MapFeedback<I, N, O, R, S, T> impl<I, N, O, R, S, T> MapFeedback<I, N, O, R, S, T>
where where
T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug, T: PartialEq + Default + Copy + 'static + Serialize + DeserializeOwned + Debug,
@ -497,6 +503,7 @@ where
novelties: None, novelties: None,
name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(), name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(),
observer_name: map_observer.name().to_string(), observer_name: map_observer.name().to_string(),
stats_name: create_stats_name(map_observer.name()),
phantom: PhantomData, phantom: PhantomData,
} }
} }
@ -509,6 +516,7 @@ where
novelties: if track_novelties { Some(vec![]) } else { None }, novelties: if track_novelties { Some(vec![]) } else { None },
name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(), name: MAPFEEDBACK_PREFIX.to_string() + map_observer.name(),
observer_name: map_observer.name().to_string(), observer_name: map_observer.name().to_string(),
stats_name: create_stats_name(map_observer.name()),
phantom: PhantomData, phantom: PhantomData,
} }
} }
@ -521,6 +529,7 @@ where
novelties: None, novelties: None,
name: name.to_string(), name: name.to_string(),
observer_name: observer_name.to_string(), observer_name: observer_name.to_string(),
stats_name: create_stats_name(name),
phantom: PhantomData, phantom: PhantomData,
} }
} }
@ -537,6 +546,7 @@ where
indexes: if track_indexes { Some(vec![]) } else { None }, indexes: if track_indexes { Some(vec![]) } else { None },
novelties: if track_novelties { Some(vec![]) } else { None }, novelties: if track_novelties { Some(vec![]) } else { None },
observer_name: observer_name.to_string(), observer_name: observer_name.to_string(),
stats_name: create_stats_name(name),
name: name.to_string(), name: name.to_string(),
phantom: PhantomData, phantom: PhantomData,
} }

136
libafl/src/monitors/disk.rs Normal file
View File

@ -0,0 +1,136 @@
//! Monitors that wrap a base one and log on disk
use alloc::{string::String, vec::Vec};
use core::time::Duration;
use crate::{
bolts::{current_time, format_duration_hms},
monitors::{ClientStats, Monitor, NopMonitor},
};
use std::{fs::File, io::Write, path::PathBuf};
/// Wrap a monitor and log the current state of the monitor into a TOML file.
#[derive(Debug, Clone)]
pub struct OnDiskTOMLMonitor<M>
where
M: Monitor,
{
base: M,
filename: PathBuf,
last_update: Duration,
}
impl<M> Monitor for OnDiskTOMLMonitor<M>
where
M: Monitor,
{
/// The client monitor, mutable
fn client_stats_mut(&mut self) -> &mut Vec<ClientStats> {
self.base.client_stats_mut()
}
/// The client monitor
fn client_stats(&self) -> &[ClientStats] {
self.base.client_stats()
}
/// Time this fuzzing run stated
fn start_time(&mut self) -> Duration {
self.base.start_time()
}
fn display(&mut self, event_msg: String, sender_id: u32) {
let cur_time = current_time();
if (cur_time - self.last_update).as_secs() >= 60 {
self.last_update = cur_time;
let mut file = File::create(&self.filename).expect("Failed to open the TOML file");
write!(
&mut file,
"# This TOML is generated using the OnDiskMonitor component of LibAFL
[global]
run_time = \"{}\"
clients = {}
corpus = {}
objectives = {}
executions = {}
exec_sec = {}
",
format_duration_hms(&(cur_time - self.start_time())),
self.client_stats().len(),
self.corpus_size(),
self.objective_size(),
self.total_execs(),
self.execs_per_sec()
)
.expect("Failed to write to the TOML file");
for (i, client) in self.client_stats_mut().iter_mut().skip(1).enumerate() {
let exec_sec = client.execs_per_sec(cur_time);
write!(
&mut file,
"
[client_{}]
corpus = {}
objectives = {}
executions = {}
exec_sec = {}
",
i + 1,
client.corpus_size,
client.objective_size,
client.executions,
exec_sec
)
.expect("Failed to write to the TOML file");
for (key, val) in &client.user_monitor {
let k: String = key
.chars()
.map(|c| if c.is_whitespace() { '_' } else { c })
.filter(|c| c.is_alphanumeric() || *c == '_')
.collect();
writeln!(&mut file, "{} = \"{}\"", k, val)
.expect("Failed to write to the TOML file");
}
}
drop(file);
}
self.base.display(event_msg, sender_id);
}
}
impl<M> OnDiskTOMLMonitor<M>
where
M: Monitor,
{
/// Create new [`OnDiskTOMLMonitor`]
#[must_use]
pub fn new<P>(filename: P, base: M) -> Self
where
P: Into<PathBuf>,
{
Self {
base,
filename: filename.into(),
last_update: current_time(),
}
}
}
impl OnDiskTOMLMonitor<NopMonitor> {
/// Create new [`OnDiskTOMLMonitor`] without a base
#[must_use]
pub fn nop<P>(filename: P) -> Self
where
P: Into<PathBuf>,
{
Self::new(filename, NopMonitor::new())
}
}

View File

@ -7,6 +7,11 @@ pub use multi::MultiMonitor;
#[allow(missing_docs)] #[allow(missing_docs)]
pub mod tui; pub mod tui;
#[cfg(feature = "std")]
pub mod disk;
#[cfg(feature = "std")]
pub use disk::OnDiskTOMLMonitor;
use alloc::{string::String, vec::Vec}; use alloc::{string::String, vec::Vec};
#[cfg(feature = "introspection")] #[cfg(feature = "introspection")]

View File

@ -813,9 +813,10 @@ pub mod pybind {
} }
#[pyo3(name = "match_name")] #[pyo3(name = "match_name")]
#[allow(clippy::all)]
fn pymatch_name(&self, name: &str) -> Option<PythonObserver> { fn pymatch_name(&self, name: &str) -> Option<PythonObserver> {
for ob in &self.list { for ob in &self.list {
if ob.name() == name { if *ob.name() == *name {
return Some(ob.clone()); return Some(ob.clone());
} }
} }

View File

@ -155,8 +155,10 @@ where
} }
} }
impl<E, EM, I, S, Z> /// Function type when the callback in `SyncFromDiskStage` is not a lambda
SyncFromDiskStage<fn(&mut Z, &mut S, &Path) -> Result<I, Error>, E, EM, I, S, Z> pub type SyncFromDiskFunction<I, S, Z> = fn(&mut Z, &mut S, &Path) -> Result<I, Error>;
impl<E, EM, I, S, Z> SyncFromDiskStage<SyncFromDiskFunction<I, S, Z>, E, EM, I, S, Z>
where where
I: Input, I: Input,
S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasMetadata, S: HasClientPerfMonitor + HasCorpus<I> + HasRand + HasMetadata,

View File

@ -381,7 +381,7 @@ mod tests {
edge = cfg.get_edge((41864 >> 1) ^ 26911).unwrap(); edge = cfg.get_edge((41864 >> 1) ^ 26911).unwrap();
assert_eq!(edge.calling_func, "main"); assert_eq!(edge.calling_func, "main");
assert_eq!(edge.successor_edges.len(), 2); assert_eq!(edge.successor_edges.len(), 2);
assert_eq!(*edge.successor_edges.get(0).unwrap(), (26911 >> 1) ^ 52706); assert_eq!(*edge.successor_edges.first().unwrap(), (26911 >> 1) ^ 52706);
assert!(cfg.get_edge(26911).is_none()); assert!(cfg.get_edge(26911).is_none());
assert!(cfg.get_edge(41864).is_some()); assert!(cfg.get_edge(41864).is_some());