generate initial inputs

This commit is contained in:
Andrea Fioraldi 2020-11-23 11:00:58 +01:00
parent a3501cfb43
commit 0a22db2db9
7 changed files with 110 additions and 151 deletions

View File

@ -24,119 +24,6 @@ pub trait TestcaseMetadata {
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
} }
pub trait TestcaseTraitTODO<I, T>
where
I: Input,
T: TestcaseMetadata,
{
/// The input associated with this testcase
fn input(&self) -> &Option<I>;
/// The input associated with this testcase (mutable)
fn input_mut(&mut self) -> &mut Option<I>;
/// Filename, if this testcase is backed by a file in the filesystem
fn filename(&self) -> &Option<String>;
/// Map of metadatas associated with this testcase
fn metadatas(&self) -> &HashMap<&'static str, Box<dyn TestcaseMetadata>>;
/// Map of metadatas associated with this testcase
fn metadatas_mut(&mut self) -> &mut HashMap<&'static str, Box<dyn TestcaseMetadata>>;
}
#[cfg(feature = "std")]
pub enum FileBackedTestcase<I, P> {
/// A testcase on disk, not yet loaded
Stored { filename: P },
/// A testcase that has been loaded, and not yet dirtied.
/// The input should be equal to the on-disk state.
Loaded {
input: I,
filename: P,
//metadatas: HashMap<&'static str, Box<dyn TestcaseMetadata>>,
},
/// A testcase that has been mutated, but not yet written to disk
Dirty {
input: I,
filename: P,
//metadatas: HashMap<&'static str, Box<dyn TestcaseMetadata>>,
},
}
#[cfg(feature = "std")]
impl<I, P> FileBackedTestcase<I, P>
where
I: Input,
P: AsRef<Path>,
{
/// Load a testcase from disk if it is not already loaded.
///
/// # Errors
/// Errors if the testcase is [Dirty](FileBackedTestcase::Dirty)
pub fn load(self) -> Result<Self, AflError> {
match self {
Self::Stored { filename } => {
let input = I::from_file(&filename)?;
Ok(Self::Loaded { filename, input })
}
Self::Loaded {
input: _,
filename: _,
} => Ok(self),
_ => Err(AflError::IllegalState(
"Attempted load on dirty testcase".into(),
)),
}
}
/// Make sure that the in-memory state is syncd to disk, and load it from disk if
/// Nece
pub fn refresh(self) -> Result<Self, AflError> {
match self {
Self::Dirty {
input: _,
filename: _,
} => self.save(),
other => other.load(),
}
}
/// Writes changes to disk
pub fn save(self) -> Result<Self, AflError> {
match self {
Self::Loaded {
input: _,
filename: _,
} => Ok(self),
Self::Dirty { input, filename } => {
let mut file = File::create(&filename)?;
file.write_all(input.serialize()?)?;
Ok(Self::Loaded { input, filename })
}
Self::Stored { filename } => Err(AflError::IllegalState(format!(
"Tried to store to {:?} without input (in stored state)",
filename.as_ref()
))),
}
}
// Removes contents of this testcase from memory
pub fn unload(self) -> Result<Self, AflError> {
match self {
Self::Loaded { input: _, filename } => Ok(Self::Stored { filename }),
Self::Stored { filename: _ } => Ok(self),
Self::Dirty {
filename: _,
input: _,
} => self.save(),
}
}
}
/// An entry in the Testcase Corpus /// An entry in the Testcase Corpus
#[derive(Default)] #[derive(Default)]
pub struct Testcase<I> pub struct Testcase<I>
@ -147,6 +34,8 @@ where
input: Option<I>, input: Option<I>,
/// Filename, if this testcase is backed by a file in the filesystem /// Filename, if this testcase is backed by a file in the filesystem
filename: Option<String>, filename: Option<String>,
/// Accumulated fitness from all the feedbacks
fitness: u32,
/// Map of metadatas associated with this testcase /// Map of metadatas associated with this testcase
metadatas: HashMap<&'static str, Box<dyn TestcaseMetadata>>, metadatas: HashMap<&'static str, Box<dyn TestcaseMetadata>>,
} }
@ -186,22 +75,41 @@ where
pub fn input_mut(&mut self) -> &mut Option<I> { pub fn input_mut(&mut self) -> &mut Option<I> {
&mut self.input &mut self.input
} }
/// Set the input
pub fn set_input(&mut self, input: I) {
self.input = Some(input);
}
/// Get the filename, if any /// Get the filename, if any
pub fn filename(&self) -> &Option<String> { pub fn filename(&self) -> &Option<String> {
&self.filename &self.filename
} }
/// Get the filename, if any (mutable) /// Get the filename, if any (mutable)
pub fn filename_mut(&mut self) -> &mut Option<String> { pub fn filename_mut(&mut self) -> &mut Option<String> {
&mut self.filename &mut self.filename
} }
/// Set the filename
pub fn set_filename(&mut self, filename: String) {
self.filename = Some(filename);
}
/// Get the fitness
pub fn fitness(&self) -> u32 {
self.fitness
}
/// Get the fitness (mutable)
pub fn fitness_mut(&mut self) -> &mut u32 {
&mut self.fitness
}
/// Set the fitness
pub fn set_fitness(&mut self, fitness: u32) {
self.fitness = fitness;
}
/// Get all the metadatas into an HashMap (mutable) /// Get all the metadatas into an HashMap (mutable)
pub fn metadatas(&mut self) -> &mut HashMap<&'static str, Box<dyn TestcaseMetadata>> { pub fn metadatas(&mut self) -> &mut HashMap<&'static str, Box<dyn TestcaseMetadata>> {
&mut self.metadatas &mut self.metadatas
} }
/// Add a metadata /// Add a metadata
pub fn add_metadata(&mut self, meta: Box<dyn TestcaseMetadata>) { pub fn add_metadata(&mut self, meta: Box<dyn TestcaseMetadata>) {
self.metadatas.insert(meta.name(), meta); self.metadatas.insert(meta.name(), meta);
@ -215,6 +123,7 @@ where
Testcase { Testcase {
input: Some(input.into()), input: Some(input.into()),
filename: None, filename: None,
fitness: 0,
metadatas: HashMap::default(), metadatas: HashMap::default(),
} }
} }
@ -224,6 +133,7 @@ where
Testcase { Testcase {
input: Some(input), input: Some(input),
filename: Some(filename), filename: Some(filename),
fitness: 0,
metadatas: HashMap::default(), metadatas: HashMap::default(),
} }
} }
@ -232,6 +142,7 @@ where
Testcase { Testcase {
input: None, input: None,
filename: None, filename: None,
fitness: 0,
metadatas: HashMap::default(), metadatas: HashMap::default(),
} }
} }

View File

@ -13,6 +13,7 @@ use crate::corpus::{Corpus, HasCorpus, Testcase};
use crate::events::EventManager; use crate::events::EventManager;
use crate::executors::Executor; use crate::executors::Executor;
use crate::feedbacks::Feedback; use crate::feedbacks::Feedback;
use crate::generators::Generator;
use crate::inputs::Input; use crate::inputs::Input;
use crate::observers::Observer; use crate::observers::Observer;
use crate::stages::Stage; use crate::stages::Stage;
@ -95,30 +96,68 @@ where
fn executor_mut(&mut self) -> &mut E; fn executor_mut(&mut self) -> &mut E;
/// Runs the input and triggers observers and feedback /// Runs the input and triggers observers and feedback
fn evaluate_input(&mut self, input: &I) -> Result<bool, AflError> { fn evaluate_input(
&mut self,
input: I,
) -> Result<(bool, Option<Rc<RefCell<Testcase<I>>>>), AflError> {
self.reset_observers()?; self.reset_observers()?;
self.executor_mut().run_target(input)?; self.executor_mut().run_target(&input)?;
self.set_executions(self.executions() + 1); self.set_executions(self.executions() + 1);
self.post_exec_observers()?; self.post_exec_observers()?;
let mut rate_acc = 0; let mut fitness = 0;
for feedback in self.feedbacks_mut() { for feedback in self.feedbacks_mut() {
rate_acc += feedback.is_interesting(input)?; fitness += feedback.is_interesting(&input)?;
} }
if rate_acc >= 25 { if fitness >= 25 {
let testcase = Rc::new(RefCell::new(Testcase::new(input.clone()))); let testcase: Rc<RefCell<_>> = Testcase::new(input).into();
for feedback in self.feedbacks_mut() { for feedback in self.feedbacks_mut() {
feedback.append_metadata(testcase.clone())?; feedback.append_metadata(testcase.clone())?;
} }
Ok(true) testcase.borrow_mut().set_fitness(fitness);
self.corpus_mut().add(testcase.clone());
Ok((true, Some(testcase)))
} else { } else {
for feedback in self.feedbacks_mut() { for feedback in self.feedbacks_mut() {
feedback.discard_metadata()?; feedback.discard_metadata()?;
} }
Ok(false) Ok((false, None))
} }
} }
fn load_initial_input(&mut self, input: I) -> Result<(), AflError> {
// Inefficent clone, but who cares this is done once at init
let (_, testcase) = self.evaluate_input(input.clone())?;
if testcase.is_none() {
let testcase = Testcase::new(input).into();
self.corpus_mut().add(testcase);
}
Ok(())
}
}
pub fn generate_initial_inputs<S, G, C, E, I, R, EM>(
rand: &mut R,
state: &mut S,
generator: &mut G,
_events: &mut EM,
num: usize,
) -> Result<(), AflError>
where
S: State<C, E, I, R>,
G: Generator<I, R>,
C: Corpus<I, R>,
E: Executor<I>,
I: Input,
R: Rand,
EM: EventManager,
{
for _ in 0..num {
let input = generator.generate(rand)?;
state.load_initial_input(input)?;
}
Ok(())
} }
pub struct StdState<C, E, I, R> pub struct StdState<C, E, I, R>
@ -247,12 +286,17 @@ where
events: &mut EM, events: &mut EM,
) -> Result<usize, AflError> { ) -> Result<usize, AflError> {
let (testcase, idx) = state.corpus_mut().next(rand)?; let (testcase, idx) = state.corpus_mut().next(rand)?;
println!("Cur entry: {}\tExecutions: {}", idx, state.executions());
for stage in self.stages_mut() { for stage in self.stages_mut() {
stage.perform(rand, state, events, testcase.clone())?; stage.perform(rand, state, events, testcase.clone())?;
} }
Ok(idx) Ok(idx)
} }
fn fuzz_loop(&mut self, rand: &mut R, state: &mut S, events: &mut EM) -> Result<(), AflError> {
loop {
self.fuzz_one(rand, state, events)?;
}
}
} }
pub struct StdEngine<S, EM, E, C, I, R> pub struct StdEngine<S, EM, E, C, I, R>

View File

@ -5,10 +5,14 @@ pub use scheduled::ScheduledMutator;
pub use scheduled::StdScheduledMutator; pub use scheduled::StdScheduledMutator;
use crate::corpus::Corpus; use crate::corpus::Corpus;
use crate::corpus::Testcase;
use crate::inputs::Input; use crate::inputs::Input;
use crate::utils::Rand; use crate::utils::Rand;
use crate::AflError; use crate::AflError;
use alloc::rc::Rc;
use core::cell::RefCell;
pub trait Mutator<C, I, R> pub trait Mutator<C, I, R>
where where
C: Corpus<I, R>, C: Corpus<I, R>,
@ -25,7 +29,12 @@ where
) -> Result<(), AflError>; ) -> Result<(), AflError>;
/// Post-process given the outcome of the execution /// Post-process given the outcome of the execution
fn post_exec(&mut self, _is_interesting: bool, _stage_idx: i32) -> Result<(), AflError> { fn post_exec(
&mut self,
_is_interesting: bool,
_new_testcase: Option<Rc<RefCell<Testcase<I>>>>,
_stage_idx: i32,
) -> Result<(), AflError> {
Ok(()) Ok(())
} }
} }

View File

@ -156,7 +156,7 @@ where
/// Bitflip mutation for inputs with a bytes vector /// Bitflip mutation for inputs with a bytes vector
pub fn mutation_bitflip<M, C, I, R>( pub fn mutation_bitflip<M, C, I, R>(
mutator: &mut M, _mutator: &mut M,
rand: &mut R, rand: &mut R,
_corpus: &mut C, _corpus: &mut C,
input: &mut I, input: &mut I,
@ -190,7 +190,7 @@ fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) {
/// Splicing mutator /// Splicing mutator
pub fn mutation_splice<M, C, I, R>( pub fn mutation_splice<M, C, I, R>(
mutator: &mut M, _mutator: &mut M,
rand: &mut R, rand: &mut R,
corpus: &mut C, corpus: &mut C,
input: &mut I, input: &mut I,

View File

@ -46,20 +46,15 @@ where
testcase: Rc<RefCell<Testcase<I>>>, testcase: Rc<RefCell<Testcase<I>>>,
) -> Result<(), AflError> { ) -> Result<(), AflError> {
let num = self.iterations(rand); let num = self.iterations(rand);
let input = testcase.borrow_mut().load_input()?.clone();
for i in 0..num { for i in 0..num {
let mut input_tmp = input.clone(); let mut input = testcase.borrow_mut().load_input()?.clone();
self.mutator_mut() self.mutator_mut()
.mutate(rand, state.corpus_mut(), &mut input_tmp, i as i32)?; .mutate(rand, state.corpus_mut(), &mut input, i as i32)?;
let interesting = state.evaluate_input(&input_tmp)?; let (interesting, new_testcase) = state.evaluate_input(input)?;
self.mutator_mut().post_exec(interesting, i as i32)?; self.mutator_mut()
.post_exec(interesting, new_testcase, i as i32)?;
if interesting {
state.corpus_mut().add(Testcase::new(input_tmp).into());
}
} }
Ok(()) Ok(())
} }

View File

@ -8,11 +8,12 @@ use alloc::rc::Rc;
use core::cell::RefCell; use core::cell::RefCell;
use afl::corpus::{Corpus, InMemoryCorpus, Testcase}; use afl::corpus::{Corpus, InMemoryCorpus, Testcase};
use afl::engines::{Engine, State, StdEngine, StdState}; use afl::engines::{generate_initial_inputs, Engine, State, StdEngine, StdState};
use afl::events::LoggerEventManager; use afl::events::LoggerEventManager;
use afl::executors::inmemory::InMemoryExecutor; use afl::executors::inmemory::InMemoryExecutor;
use afl::executors::{Executor, ExitKind}; use afl::executors::{Executor, ExitKind};
use afl::feedbacks::{create_history_map, MaxMapFeedback}; use afl::feedbacks::{create_history_map, MaxMapFeedback};
use afl::generators::{Generator, RandPrintablesGenerator};
use afl::inputs::bytes::BytesInput; use afl::inputs::bytes::BytesInput;
use afl::mutators::scheduled::HavocBytesMutator; use afl::mutators::scheduled::HavocBytesMutator;
use afl::observers::StdMapObserver; use afl::observers::StdMapObserver;
@ -42,9 +43,9 @@ fn harness<I>(_executor: &dyn Executor<I>, buf: &[u8]) -> ExitKind {
pub extern "C" fn afl_libfuzzer_main() { pub extern "C" fn afl_libfuzzer_main() {
let mut rand = StdRand::new(0); let mut rand = StdRand::new(0);
let mut corpus = InMemoryCorpus::new(); let corpus = InMemoryCorpus::new();
let testcase = Testcase::new(vec![0; 4]).into(); let mut generator = RandPrintablesGenerator::new(4096);
corpus.add(testcase); let mut events = LoggerEventManager::new();
let edges_observer = Rc::new(RefCell::new(StdMapObserver::new_from_ptr( let edges_observer = Rc::new(RefCell::new(StdMapObserver::new_from_ptr(
unsafe { __lafl_edges_map }, unsafe { __lafl_edges_map },
@ -53,23 +54,22 @@ pub extern "C" fn afl_libfuzzer_main() {
let edges_history_map = create_history_map::<u8>(MAP_SIZE); let edges_history_map = create_history_map::<u8>(MAP_SIZE);
let edges_feedback = MaxMapFeedback::new(edges_observer.clone(), edges_history_map); let edges_feedback = MaxMapFeedback::new(edges_observer.clone(), edges_history_map);
let executor = InMemoryExecutor::<BytesInput>::new(harness); let executor = InMemoryExecutor::new(harness);
let mut state = StdState::new(corpus, executor); let mut state = StdState::new(corpus, executor);
state.add_observer(edges_observer); state.add_observer(edges_observer);
state.add_feedback(Box::new(edges_feedback)); state.add_feedback(Box::new(edges_feedback));
generate_initial_inputs(&mut rand, &mut state, &mut generator, &mut events, 4)
.expect("Failed to load initial inputs");
let mut engine = StdEngine::new(); let mut engine = StdEngine::new();
let mutator = HavocBytesMutator::new_default(); let mutator = HavocBytesMutator::new_default();
let stage = StdMutationalStage::new(mutator); let stage = StdMutationalStage::new(mutator);
engine.add_stage(Box::new(stage)); engine.add_stage(Box::new(stage));
let mut events = LoggerEventManager::new(); engine
.fuzz_loop(&mut rand, &mut state, &mut events)
for i in 0..1000 { .expect("Fuzzer fatal error");
engine
.fuzz_one(&mut rand, &mut state, &mut events)
.expect(&format!("Error in iter {}", i));
}
#[cfg(feature = "std")] #[cfg(feature = "std")]
println!("OK"); println!("OK");
} }

View File

@ -3,11 +3,11 @@
int target_func(const uint8_t *buf, size_t size) { int target_func(const uint8_t *buf, size_t size) {
printf("BUF (%ld): ", size); /*printf("BUF (%ld): ", size);
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
printf("%02X", buf[i]); printf("%02X", buf[i]);
} }
printf("\n"); printf("\n");*/
switch (buf[0]) { switch (buf[0]) {