Skip to content

Commit

Permalink
dump state
Browse files Browse the repository at this point in the history
  • Loading branch information
rmalmain committed Jun 24, 2024
1 parent c787687 commit c4d9510
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 22 deletions.
2 changes: 1 addition & 1 deletion libafl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ tcp_compression = ["tcp_manager", "libafl_bolts/gzip"]
multi_machine = ["tokio", "std", "enumflags2", "ahash/std"]

## Dump state of each client on exit
dump_state = ["std", "libafl_bolts/unsafe_stable_anymap"]
dump_state = ["std"]

## Enables the `NaiveTokenizer` and `StacktraceObserver`
regex = ["std", "dep:regex"]
Expand Down
19 changes: 10 additions & 9 deletions libafl/src/events/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,22 +98,23 @@ impl Handler for ShutdownSignalData {
unsafe {
// println!("Exiting from the handler....");

#[cfg(unix)]
#[cfg(feature = "dump_state")]
{
#[cfg(not(feature = "dump_state"))]
// fuzzer will exit at the end of fuzzing run.
INTERRUPT_FUZZER = true;
}

#[cfg(not(feature = "dump_state"))]
{
#[cfg(unix)]
{
libc::_exit(CTRL_C_EXIT);
}

#[cfg(feature = "dump_state")]
{
// fuzzer will exit at the end of fuzzing run.
INTERRUPT_FUZZER = true;
#[cfg(windows)]
windows::Win32::System::Threading::ExitProcess(100);
}
}

#[cfg(windows)]
windows::Win32::System::Threading::ExitProcess(100);
}
}

Expand Down
21 changes: 15 additions & 6 deletions libafl/src/fuzzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
use alloc::string::ToString;
use core::{fmt::Debug, marker::PhantomData, time::Duration};
use std::path::PathBuf;
#[cfg(all(feature = "std", feature = "dump_state"))]
use std::{
fs::{self, File},
Expand All @@ -17,7 +18,7 @@ use libafl_bolts::os::CTRL_C_EXIT;
use serde::{de::DeserializeOwned, Serialize};

#[cfg(all(feature = "std", feature = "dump_state"))]
use crate::state::MaybeHasDumpStateDir;
use crate::state::MaybeCanDumpState;
use crate::{
corpus::{Corpus, CorpusId, HasCurrentCorpusId, HasTestcase, Testcase},
events::{Event, EventConfig, EventFirer, EventProcessor, ProgressReporter},
Expand Down Expand Up @@ -238,25 +239,33 @@ where
if INTERRUPT_FUZZER {
log::info!("Interrupting fuzzer...");

let dump_path: Option<PathBuf> = state.dump_state_dir().cloned();

// Dump state if needed.
if let Some(dump_path) = state.dump_state_dir() {
if let Some(dump_path) = dump_path {
log::info!("Dumping state to disk...");
let serialized = postcard::to_allocvec(&state)?;
let state_dump = state.gen_dump_state()?;
let dump_serialized = postcard::to_allocvec(&state_dump)?;

// Taken from staterestorer
// generate a filename
let mut hasher = RandomState::with_seeds(0, 0, 0, 0).build_hasher();
// Using the last few k as randomness for a filename, hoping it's unique.
hasher.write(&serialized[serialized.len().saturating_sub(4096)..]);
hasher
.write(&dump_serialized[dump_serialized.len().saturating_sub(4096)..]);

let filename = format!("{:016x}.libafl_state", hasher.finish());
let dump_file = dump_path.join(&filename);
let dump_file = dump_path.join(filename);

fs::create_dir_all(dump_path.as_path())?;
File::create(dump_file)?.write_all(&serialized)?;
File::create(dump_file)?.write_all(&dump_serialized)?;
}

#[cfg(unix)]
libc::exit(CTRL_C_EXIT);

#[cfg(windows)]
windows::Win32::System::Threading::ExitProcess(100);
}
}

Expand Down
60 changes: 54 additions & 6 deletions libafl/src/state/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub trait State:
+ DeserializeOwned
+ MaybeHasClientPerfMonitor
+ MaybeHasScalabilityMonitor
+ MaybeHasDumpStateDir
+ MaybeCanDumpState
+ HasCurrentCorpusId
+ HasCurrentStage
{
Expand Down Expand Up @@ -169,17 +169,23 @@ impl<T> MaybeHasScalabilityMonitor for T where T: HasScalabilityMonitor {}

/// Trait for getting the optional dump directory for the state
#[cfg(all(feature = "std", feature = "dump_state"))]
pub trait MaybeHasDumpStateDir {
pub trait MaybeCanDumpState {
/// The dump state type
type StateDump: Serialize + for<'de> Deserialize<'de>;

/// Get the dump dir, if there is one.
fn dump_state_dir(&self) -> Option<&PathBuf>;

/// Generate the state dump
fn gen_dump_state(&mut self) -> Result<Self::StateDump, Error>;
}

/// Trait for getting the optional dump directory for the state
#[cfg(all(feature = "std", not(feature = "dump_state")))]
pub trait MaybeHasDumpStateDir {}

#[cfg(all(feature = "std", not(feature = "dump_state")))]
impl<T> MaybeHasDumpStateDir for T {}
impl<T> MaybeCanDumpState for T {}

/// Trait for offering a [`ScalabilityMonitor`]
#[cfg(feature = "scalability_introspection")]
Expand Down Expand Up @@ -299,6 +305,20 @@ pub struct StdState<I, C, R, SC> {
phantom: PhantomData<I>,
}

/// The standard state dump
#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(bound = "I: serde::de::DeserializeOwned")]
pub struct StdStateDump<I>
where
I: Input,
{
/// Fuzzer start time.
start_time: Duration,

/// Loaded inputs
testcases: Vec<Testcase<I>>,
}

impl<I, C, R, SC> UsesInput for StdState<I, C, R, SC>
where
I: Input,
Expand All @@ -307,18 +327,40 @@ where
}

#[cfg(all(feature = "std", feature = "dump_state"))]
impl<I, C, R, SC> MaybeHasDumpStateDir for StdState<I, C, R, SC> {
impl<I, C, R, SC> MaybeCanDumpState for StdState<I, C, R, SC>
where
C: Corpus<Input = I>,
I: Input,
{
type StateDump = StdStateDump<I>;

fn dump_state_dir(&self) -> Option<&PathBuf> {
self.dump_state_dir.as_ref()
}

fn gen_dump_state(&mut self) -> Result<Self::StateDump, Error> {
let mut tcs: Vec<Testcase<I>> = Vec::new();
for corpus_id in self.corpus.ids() {
let mut tc = self.corpus.get(corpus_id)?.clone();
let tc_ref = tc.get_mut();
tc_ref.load_input(&self.corpus)?;
tcs.push(tc_ref.clone());
}

Ok(StdStateDump {
start_time: self.start_time,
testcases: tcs,
})
}
}

impl<I, C, R, SC> State for StdState<I, C, R, SC>
where
C: Corpus<Input = Self::Input>,
I: Input,
R: Rand,
SC: Corpus<Input = Self::Input>,
Self: UsesInput,
Self: UsesInput<Input = I>,
{
}

Expand Down Expand Up @@ -1247,10 +1289,16 @@ impl<I> HasMaxSize for NopState<I> {
}

#[cfg(all(feature = "std", feature = "dump_state"))]
impl<I> MaybeHasDumpStateDir for NopState<I> {
impl<I> MaybeCanDumpState for NopState<I> {
type StateDump = ();

fn dump_state_dir(&self) -> Option<&PathBuf> {
None
}

fn gen_dump_state(&mut self) -> Result<Self::StateDump, Error> {
Ok(())
}
}

impl<I> UsesInput for NopState<I>
Expand Down

0 comments on commit c4d9510

Please sign in to comment.