Introduce Persistent Record for libafl-fuzz (#2411)
* libafl-fuzz: fix PERSISTENT_SIG and DEFERRED_SIG * libafl-fuzz: add AFL_PERSISTENT_RECORD * libafl-fuzz: update README
This commit is contained in:
parent
713652e5d8
commit
b9da7dd87f
@ -44,7 +44,7 @@ Rewrite of afl-fuzz in Rust.
|
||||
- [ ] AFL_FAST_CAL
|
||||
- [ ] AFL_NO_CRASH_README
|
||||
- [ ] AFL_KEEP_TIMEOUTS
|
||||
- [ ] AFL_PERSISTENT_RECORD
|
||||
- [x] AFL_PERSISTENT_RECORD
|
||||
- [ ] AFL_TESTCACHE_SIZE
|
||||
- [ ] AFL_NO_ARITH
|
||||
- [ ] AFL_DISABLE_TRIM
|
||||
|
@ -102,6 +102,9 @@ pub fn parse_envs(opt: &mut Opt) -> Result<(), Error> {
|
||||
if let Ok(res) = std::env::var("AFL_KILL_SIGNAL") {
|
||||
opt.kill_signal = Some(res.parse()?);
|
||||
}
|
||||
if let Ok(res) = std::env::var("AFL_PERSISTENT_RECORD") {
|
||||
opt.persistent_record = res.parse()?;
|
||||
}
|
||||
if let Ok(res) = std::env::var("AFL_SYNC_TIME") {
|
||||
opt.foreign_sync_interval = Duration::from_secs(res.parse::<u64>()? * 60);
|
||||
} else {
|
||||
|
@ -1,2 +1,3 @@
|
||||
pub mod filepath;
|
||||
pub mod persistent_record;
|
||||
pub mod seed;
|
||||
|
161
fuzzers/libafl-fuzz/src/feedback/persistent_record.rs
Normal file
161
fuzzers/libafl-fuzz/src/feedback/persistent_record.rs
Normal file
@ -0,0 +1,161 @@
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
collections::VecDeque,
|
||||
fmt::{Debug, Formatter},
|
||||
marker::PhantomData,
|
||||
};
|
||||
|
||||
use libafl::{
|
||||
corpus::{Corpus, Testcase},
|
||||
events::EventFirer,
|
||||
executors::ExitKind,
|
||||
feedbacks::{Feedback, FeedbackFactory},
|
||||
inputs::Input,
|
||||
observers::ObserversTuple,
|
||||
state::{HasCorpus, State},
|
||||
};
|
||||
use libafl_bolts::{Error, Named};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// A [`PersitentRecordFeedback`] tracks the last N inputs that the fuzzer has run.
|
||||
/// TODO: Kept in memory for now but should write to disk.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct PersitentRecordFeedback<I, S>
|
||||
where
|
||||
S: State<Input = I>,
|
||||
{
|
||||
/// Vec that tracks the last `record_size` [`Input`]
|
||||
record: VecDeque<I>,
|
||||
record_size: usize,
|
||||
phantomm: PhantomData<(I, S)>,
|
||||
}
|
||||
|
||||
impl<I, S> PersitentRecordFeedback<I, S>
|
||||
where
|
||||
I: Input,
|
||||
S: State<Input = I>,
|
||||
{
|
||||
/// Create a new [`PersitentRecordFeedback`].
|
||||
pub fn new(record_size: usize) -> Self {
|
||||
Self {
|
||||
record_size,
|
||||
record: VecDeque::default(),
|
||||
phantomm: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S, T> FeedbackFactory<PersitentRecordFeedback<I, S>, T> for PersitentRecordFeedback<I, S>
|
||||
where
|
||||
I: Input,
|
||||
S: State<Input = I>,
|
||||
{
|
||||
fn create_feedback(&self, _ctx: &T) -> PersitentRecordFeedback<I, S> {
|
||||
Self {
|
||||
record_size: self.record_size.clone(),
|
||||
record: self.record.clone(),
|
||||
phantomm: self.phantomm,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Named for PersitentRecordFeedback<I, S>
|
||||
where
|
||||
I: Input,
|
||||
S: State<Input = I>,
|
||||
{
|
||||
fn name(&self) -> &Cow<'static, str> {
|
||||
static NAME: Cow<'static, str> = Cow::Borrowed("PersitentRecordFeedback");
|
||||
&NAME
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Debug for PersitentRecordFeedback<I, S>
|
||||
where
|
||||
I: Input,
|
||||
S: State<Input = I>,
|
||||
{
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("PersitentRecordFeedback")
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Feedback<S> for PersitentRecordFeedback<I, S>
|
||||
where
|
||||
S: State<Input = I> + HasCorpus,
|
||||
I: Input,
|
||||
{
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
#[inline]
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
input: &I,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
{
|
||||
if self.should_run() {
|
||||
self.record.push_back(input.clone());
|
||||
if self.record.len() == self.record_size {
|
||||
self.record.pop_front();
|
||||
}
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
fn append_metadata<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_observers: &OT,
|
||||
testcase: &mut Testcase<<S>::Input>,
|
||||
) -> Result<(), Error>
|
||||
where
|
||||
OT: ObserversTuple<S>,
|
||||
EM: EventFirer<State = S>,
|
||||
{
|
||||
if self.should_run() {
|
||||
let file_path = testcase
|
||||
.file_path()
|
||||
.as_ref()
|
||||
.expect("file path for the testcase must be set!");
|
||||
let file_dir = file_path
|
||||
.parent()
|
||||
.expect("testcase must have a parent directory!");
|
||||
// fetch the ID for this testcase
|
||||
let id = state.corpus().peek_free_id().0;
|
||||
let record = format!("RECORD:{id:0>6}");
|
||||
// save all inputs in our persistent record
|
||||
for (i, input) in self.record.iter().enumerate() {
|
||||
let filename = file_dir.join(format!("{record},cnt{i:0>6}"));
|
||||
input.to_file(file_dir.join(filename))?;
|
||||
}
|
||||
// rewrite this current testcase's filepath
|
||||
let filename = format!("RECORD:{id:0>6},cnt:{0:0>6}", self.record.len());
|
||||
*testcase.file_path_mut() = Some(file_dir.join(&filename));
|
||||
*testcase.filename_mut() = Some(filename);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "track_hit_feedbacks")]
|
||||
#[inline]
|
||||
fn last_result(&self) -> Result<bool, Error> {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> PersitentRecordFeedback<I, S>
|
||||
where
|
||||
I: Input,
|
||||
S: State<Input = I>,
|
||||
{
|
||||
fn should_run(&self) -> bool {
|
||||
return self.record_size > 0;
|
||||
}
|
||||
}
|
@ -43,7 +43,10 @@ use crate::{
|
||||
afl_stats::AflStatsStage,
|
||||
corpus::{set_corpus_filepath, set_solution_filepath},
|
||||
env_parser::AFL_DEFAULT_MAP_SIZE,
|
||||
feedback::{filepath::CustomFilepathToTestcaseFeedback, seed::SeedFeedback},
|
||||
feedback::{
|
||||
filepath::CustomFilepathToTestcaseFeedback, persistent_record::PersitentRecordFeedback,
|
||||
seed::SeedFeedback,
|
||||
},
|
||||
mutational_stage::SupportedMutationalStages,
|
||||
scheduler::SupportedSchedulers,
|
||||
Opt, AFL_DEFAULT_INPUT_LEN_MAX, AFL_DEFAULT_INPUT_LEN_MIN, SHMEM_ENV_VAR,
|
||||
@ -119,6 +122,7 @@ where
|
||||
* We check if it's a crash or a timeout (if we are configured to consider timeouts)
|
||||
* The `CustomFilepathToTestcaseFeedback is used to adhere to AFL++'s corpus format.
|
||||
* The `MaxMapFeedback` saves objectives only if they hit new edges
|
||||
* Note: The order of the feedbacks matter!
|
||||
* */
|
||||
let mut objective = feedback_or!(
|
||||
feedback_and!(
|
||||
@ -131,7 +135,8 @@ where
|
||||
),
|
||||
MaxMapFeedback::with_name("edges_objective", &edges_observer)
|
||||
),
|
||||
CustomFilepathToTestcaseFeedback::new(set_solution_filepath, fuzzer_dir.clone())
|
||||
CustomFilepathToTestcaseFeedback::new(set_solution_filepath, fuzzer_dir.clone()),
|
||||
PersitentRecordFeedback::new(opt.persistent_record),
|
||||
);
|
||||
|
||||
// Initialize our State if necessary
|
||||
|
@ -37,8 +37,8 @@ const AFL_DEFAULT_INPUT_LEN_MAX: usize = 1_048_576;
|
||||
const AFL_DEFAULT_INPUT_LEN_MIN: usize = 1;
|
||||
const OUTPUT_GRACE: u64 = 25;
|
||||
pub const AFL_DEFAULT_BROKER_PORT: u16 = 1337;
|
||||
const PERSIST_SIG: &str = "##SIG_AFL_PERSISTENT##";
|
||||
const DEFER_SIG: &str = "##SIG_AFL_DEFER_FORKSRV##";
|
||||
const PERSIST_SIG: &str = "##SIG_AFL_PERSISTENT##\0";
|
||||
const DEFER_SIG: &str = "##SIG_AFL_DEFER_FORKSRV##\0";
|
||||
const SHMEM_ENV_VAR: &str = "__AFL_SHM_ID";
|
||||
static AFL_HARNESS_FILE_INPUT: &str = "@@";
|
||||
|
||||
@ -201,6 +201,8 @@ struct Opt {
|
||||
|
||||
#[clap(skip)]
|
||||
foreign_sync_interval: Duration,
|
||||
#[clap(skip)]
|
||||
persistent_record: usize,
|
||||
|
||||
// TODO:
|
||||
#[clap(skip)]
|
||||
|
@ -74,7 +74,8 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
impl<S, SM, P, E, EM, M, I, Z> Stage<E, EM, Z> for SupportedMutationalStages<S, SM, P, E, EM, M, I, Z>
|
||||
impl<S, SM, P, E, EM, M, I, Z> Stage<E, EM, Z>
|
||||
for SupportedMutationalStages<S, SM, P, E, EM, M, I, Z>
|
||||
where
|
||||
E: UsesState<State = S>,
|
||||
EM: UsesState<State = S>,
|
||||
|
Loading…
x
Reference in New Issue
Block a user