From 4e4513b24cdde6c184b29581c39e598149bb3e98 Mon Sep 17 00:00:00 2001 From: Dominik Maier Date: Tue, 9 Feb 2021 10:18:19 +0100 Subject: [PATCH] havoc test case, documentation --- afl/src/events/mod.rs | 8 ++-- afl/src/events/stats.rs | 9 ++++- afl/src/executors/inprocess.rs | 3 ++ afl/src/executors/runtime.rs | 5 +++ afl/src/feedbacks/mod.rs | 3 ++ afl/src/generators/mod.rs | 2 + afl/src/inputs/bytes.rs | 5 ++- afl/src/inputs/mod.rs | 2 + afl/src/mutators/mod.rs | 2 + afl/src/mutators/mutations.rs | 60 ----------------------------- afl/src/mutators/scheduled.rs | 45 ++++++++++++++++++---- afl/src/mutators/token_mutations.rs | 6 ++- 12 files changed, 73 insertions(+), 77 deletions(-) diff --git a/afl/src/events/mod.rs b/afl/src/events/mod.rs index 0958af333c..cd467eb98d 100644 --- a/afl/src/events/mod.rs +++ b/afl/src/events/mod.rs @@ -315,7 +315,7 @@ where observers_buf: _, } => { stats.client_stats_mut()[0].corpus_size = *corpus_size as u64; - stats.show(event.name().to_string()); + stats.display(event.name().to_string()); Ok(BrokerEventResult::Handled) } Event::UpdateStats { @@ -325,7 +325,7 @@ where } => { // TODO: The stats buffer should be added on client add. stats.client_stats_mut()[0].executions = *executions as u64; - stats.show(event.name().to_string()); + stats.display(event.name().to_string()); Ok(BrokerEventResult::Handled) } Event::Crash { input: _ } => { @@ -540,7 +540,7 @@ where } => { let client = stats.client_stats_mut_for(sender_id); client.corpus_size = *corpus_size as u64; - stats.show(event.name().to_string() + " #" + &sender_id.to_string()); + stats.display(event.name().to_string() + " #" + &sender_id.to_string()); Ok(BrokerEventResult::Handled) } Event::UpdateStats { @@ -551,7 +551,7 @@ where // TODO: The stats buffer should be added on client add. let client = stats.client_stats_mut_for(sender_id); client.executions = *executions as u64; - stats.show(event.name().to_string() + " #" + &sender_id.to_string()); + stats.display(event.name().to_string() + " #" + &sender_id.to_string()); Ok(BrokerEventResult::Handled) } Event::Crash { input: _ } => { diff --git a/afl/src/events/stats.rs b/afl/src/events/stats.rs index 693d929c00..27800bcd11 100644 --- a/afl/src/events/stats.rs +++ b/afl/src/events/stats.rs @@ -1,3 +1,5 @@ +//! Keep stats, and dispaly them to the user. Usually used in a broker, or main node, of some sort. + use alloc::{string::String, vec::Vec}; use core::{time, time::Duration}; @@ -22,6 +24,7 @@ pub struct ClientStats { } impl ClientStats { + /// We got a new information about executions for this client, insert them. pub fn update_executions(&mut self, executions: u64, cur_time: time::Duration) { self.executions = executions; if (cur_time - self.last_window_time).as_secs() > CLIENT_STATS_TIME_WINDOW_SECS { @@ -31,6 +34,7 @@ impl ClientStats { } } + /// Get the calculated executions per second for this client pub fn execs_per_sec(&self, cur_time: time::Duration) -> u64 { if self.executions == 0 { return 0; @@ -45,6 +49,7 @@ impl ClientStats { } } +/// The stats trait keeps track of all the client's stats, and offers methods to dispaly them. pub trait Stats { /// the client stats (mut) fn client_stats_mut(&mut self) -> &mut Vec; @@ -56,7 +61,7 @@ pub trait Stats { fn start_time(&mut self) -> time::Duration; /// show the stats to the user - fn show(&mut self, event_msg: String); + fn display(&mut self, event_msg: String); /// Amount of elements in the corpus (combined for all children) fn corpus_size(&self) -> u64 { @@ -125,7 +130,7 @@ where self.start_time } - fn show(&mut self, event_msg: String) { + fn display(&mut self, event_msg: String) { let fmt = format!( "[{}] clients: {}, corpus: {}, executions: {}, exec/sec: {}", event_msg, diff --git a/afl/src/executors/inprocess.rs b/afl/src/executors/inprocess.rs index 599cc8adae..81f00a7c59 100644 --- a/afl/src/executors/inprocess.rs +++ b/afl/src/executors/inprocess.rs @@ -1,3 +1,6 @@ +//! The InProcess Executor is a libfuzzer-like executor, that will simply call a function. +//! It should usually be paired with extra error-handling, such as a restarting event manager, to be effective. + use core::marker::PhantomData; #[cfg(feature = "std")] #[cfg(unix)] diff --git a/afl/src/executors/runtime.rs b/afl/src/executors/runtime.rs index 7fe4fda169..c1aa4af39f 100644 --- a/afl/src/executors/runtime.rs +++ b/afl/src/executors/runtime.rs @@ -1,5 +1,8 @@ +//! A sancov runtime to update a simple u8 map with coverage-information during fuzzing + //#![feature(asm)] +/// The map size used by this instance. const MAP_SIZE: usize = 65536; #[no_mangle] @@ -11,6 +14,7 @@ pub static mut __lafl_cmp_map: *mut u8 = unsafe { __lafl_dummy_map.as_ptr() as * #[no_mangle] pub static mut __lafl_max_edges_size: u32 = 0; +/// Called for each branch the target program takes. #[no_mangle] #[inline] pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: &u32) { @@ -34,6 +38,7 @@ pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: &u32) { //*trace_byte = (*trace_byte).wrapping_add(1); } +/// Called when the targetprogram starts #[no_mangle] #[inline] pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) { diff --git a/afl/src/feedbacks/mod.rs b/afl/src/feedbacks/mod.rs index 9279c8c7ed..2d8e5d4a8f 100644 --- a/afl/src/feedbacks/mod.rs +++ b/afl/src/feedbacks/mod.rs @@ -1,3 +1,6 @@ +//! The feedbacks reduce observer state after each run to a single `is_interesting`-value. +//! If a testcase is interesting, it may be added to a Corpus. + use alloc::{ string::{String, ToString}, vec::Vec, diff --git a/afl/src/generators/mod.rs b/afl/src/generators/mod.rs index b8d2e91587..f262645ee9 100644 --- a/afl/src/generators/mod.rs +++ b/afl/src/generators/mod.rs @@ -1,3 +1,5 @@ +//! Generators may generate bytes or, in general, data, for inputs. + use alloc::vec::Vec; use core::{cmp::min, marker::PhantomData}; diff --git a/afl/src/inputs/bytes.rs b/afl/src/inputs/bytes.rs index d81f588836..0726df7de6 100644 --- a/afl/src/inputs/bytes.rs +++ b/afl/src/inputs/bytes.rs @@ -1,3 +1,6 @@ +//! The BytesInput is the "normal" input, a map of bytes, that can be sent directly to the client +//! (As opposed to other, more abstract, imputs, like an Grammar-Based AST Input) + use alloc::{borrow::ToOwned, rc::Rc, vec::Vec}; use core::{cell::RefCell, convert::From}; use serde::{Deserialize, Serialize}; @@ -5,7 +8,7 @@ use serde::{Deserialize, Serialize}; use crate::inputs::{HasBytesVec, HasTargetBytes, Input, TargetBytes}; /// A bytes input is the basic input -#[derive(Serialize, Deserialize, Clone, Debug, Default)] +#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)] pub struct BytesInput { /// The raw input bytes bytes: Vec, diff --git a/afl/src/inputs/mod.rs b/afl/src/inputs/mod.rs index d422f299a5..258cf8e53f 100644 --- a/afl/src/inputs/mod.rs +++ b/afl/src/inputs/mod.rs @@ -1,3 +1,5 @@ +//! Inputs are the actual contents sent to a target for each exeuction. + pub mod bytes; pub use bytes::BytesInput; diff --git a/afl/src/mutators/mod.rs b/afl/src/mutators/mod.rs index a41aaf48bc..c575d3f9cc 100644 --- a/afl/src/mutators/mod.rs +++ b/afl/src/mutators/mod.rs @@ -1,3 +1,5 @@ +//! Mutators mutate input during fuzzing. + pub mod scheduled; pub use scheduled::*; pub mod mutations; diff --git a/afl/src/mutators/mutations.rs b/afl/src/mutators/mutations.rs index 9b019e0793..fda3a304c7 100644 --- a/afl/src/mutators/mutations.rs +++ b/afl/src/mutators/mutations.rs @@ -502,66 +502,6 @@ where Ok(MutationResult::Mutated) } -/* TODO -/// Insert a dictionary token -pub fn mutation_tokeninsert( - mutator: &mut M, - rand: &mut R, - state: &mut S, - input: &mut I, -) -> Result -where - M: HasMaxSize, - I: Input + HasBytesVec, - R: Rand, - S: HasMetadata, -{ - let tokens: Vec> = state.metadata().get().unwrap(); - if mutator.tokens.size() == 0 { - return Ok(MutationResult::Skipped); - } - let token = &mutator.tokens[rand.below(token.size())]; - let token_len = token.size(); - let size = input.bytes().len(); - let off = if size == 0 { - 0 - } else { - rand.below(core::cmp::min( - size, - (mutator.max_size() - token_len) as u64, - )) as usize - } as usize; - - input.bytes_mut().resize(size + token_len, 0); - mem_move(input.bytes_mut(), token, 0, off, len); - Ok(MutationResult::Mutated) -} - -/// Overwrite with a dictionary token -pub fn mutation_tokenreplace( - mutator: &mut M, - rand: &mut R, - state: &S, - input: &mut I, -) -> Result -where - M: HasMaxSize, - I: Input + HasBytesVec, - R: Rand, - S: HasMetadata, -{ - if mutator.tokens.size() > len || !len { - return Ok(MutationResult::Skipped); - } - let token = &mutator.tokens[rand.below(token.size())]; - let token_len = token.size(); - let size = input.bytes().len(); - let off = rand.below((mutator.max_size() - token_len) as u64) as usize; - mem_move(input.bytes_mut(), token, 0, off, len); - Ok(MutationResult::Mutated) -} -*/ - pub fn mutation_bytesinsert( mutator: &mut M, rand: &mut R, diff --git a/afl/src/mutators/scheduled.rs b/afl/src/mutators/scheduled.rs index 5ff8a29127..a2ededcad3 100644 --- a/afl/src/mutators/scheduled.rs +++ b/afl/src/mutators/scheduled.rs @@ -208,7 +208,7 @@ where let idx = self.scheduled.schedule(14, rand, input); let mutation = match idx { 0 => mutation_bitflip, - /*1 => mutation_byteflip, + 1 => mutation_byteflip, 2 => mutation_byteinc, 3 => mutation_bytedec, 4 => mutation_byteneg, @@ -219,7 +219,7 @@ where 8 => mutation_dwordadd, 9 => mutation_byteinteresting, 10 => mutation_wordinteresting, - 11 => mutation_dwordinteresting,*/ + 11 => mutation_dwordinteresting, _ => mutation_splice, }; mutation(self, rand, state, input)?; @@ -323,18 +323,20 @@ where mod tests { use crate::{ corpus::{Corpus, InMemoryCorpus, Testcase}, - inputs::BytesInput, - inputs::HasBytesVec, - mutators::scheduled::{mutation_splice, StdScheduledMutator}, + inputs::{BytesInput, HasBytesVec}, + mutators::{ + scheduled::{mutation_splice, HavocBytesMutator, StdScheduledMutator}, + Mutator, + }, state::State, - utils::{Rand, XKCDRand}, + utils::{Rand, StdRand, XKCDRand}, }; #[test] - fn test_mut_splice() { + fn test_mut_scheduled() { // With the current impl, seed of 1 will result in a split at pos 2. let mut rand = XKCDRand::new(); - let mut corpus: InMemoryCorpus = InMemoryCorpus::new(); + let mut corpus: InMemoryCorpus = InMemoryCorpus::new(); corpus.add(Testcase::new(vec!['a' as u8, 'b' as u8, 'c' as u8]).into()); corpus.add(Testcase::new(vec!['d' as u8, 'e' as u8, 'f' as u8]).into()); @@ -346,6 +348,7 @@ mod tests { let mut state = State::new(corpus, ()); rand.set_seed(5); + let mut mutator = StdScheduledMutator::< InMemoryCorpus, _, @@ -362,4 +365,30 @@ mod tests { // TODO: Maybe have a fixed rand for this purpose? assert_eq!(input.bytes(), &['a' as u8, 'b' as u8, 'f' as u8]) } + + #[test] + fn test_havoc() { + // With the current impl, seed of 1 will result in a split at pos 2. + let mut rand = StdRand::new(0x1337); + let mut corpus: InMemoryCorpus = InMemoryCorpus::new(); + corpus.add(Testcase::new(vec!['a' as u8, 'b' as u8, 'c' as u8]).into()); + corpus.add(Testcase::new(vec!['d' as u8, 'e' as u8, 'f' as u8]).into()); + + let (testcase, _) = corpus + .next(&mut rand) + .expect("Corpus did not contain entries"); + let mut input = testcase.borrow_mut().load_input().unwrap().clone(); + let input_prior = input.clone(); + + let mut state = State::new(corpus, ()); + + let mut havoc = HavocBytesMutator::new(StdScheduledMutator::new()); + + assert_eq!(input, input_prior); + + for i in 0..42 { + havoc.mutate(&mut rand, &mut state, &mut input, i).unwrap(); + assert_ne!(input, input_prior); + } + } } diff --git a/afl/src/mutators/token_mutations.rs b/afl/src/mutators/token_mutations.rs index 8622947f47..3e16b9749c 100644 --- a/afl/src/mutators/token_mutations.rs +++ b/afl/src/mutators/token_mutations.rs @@ -1,11 +1,13 @@ +//! Tokens are what afl calls extras or dictionaries. +//! They may be inserted as part of mutations during fuzzing. - +/// The tokens type, to be stored as metadata struct Tokens { vec: Vec>, } impl AsAny for Tokens { - + } /// Insert a dictionary token