havoc test case, documentation

This commit is contained in:
Dominik Maier 2021-02-09 10:18:19 +01:00
parent c849b435b7
commit 4e4513b24c
12 changed files with 73 additions and 77 deletions

View File

@ -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: _ } => {

View File

@ -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<ClientStats>;
@ -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,

View File

@ -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)]

View File

@ -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) {

View File

@ -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,

View File

@ -1,3 +1,5 @@
//! Generators may generate bytes or, in general, data, for inputs.
use alloc::vec::Vec;
use core::{cmp::min, marker::PhantomData};

View File

@ -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<u8>,

View File

@ -1,3 +1,5 @@
//! Inputs are the actual contents sent to a target for each exeuction.
pub mod bytes;
pub use bytes::BytesInput;

View File

@ -1,3 +1,5 @@
//! Mutators mutate input during fuzzing.
pub mod scheduled;
pub use scheduled::*;
pub mod mutations;

View File

@ -502,66 +502,6 @@ where
Ok(MutationResult::Mutated)
}
/* TODO
/// Insert a dictionary token
pub fn mutation_tokeninsert<I, M, R, S>(
mutator: &mut M,
rand: &mut R,
state: &mut S,
input: &mut I,
) -> Result<MutationResult, AflError>
where
M: HasMaxSize,
I: Input + HasBytesVec,
R: Rand,
S: HasMetadata,
{
let tokens: Vec<Vec<u8>> = 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<I, M, R, S>(
mutator: &mut M,
rand: &mut R,
state: &S,
input: &mut I,
) -> Result<MutationResult, AflError>
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<I, M, R, S>(
mutator: &mut M,
rand: &mut R,

View File

@ -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<BytesInput, XKCDRand> = InMemoryCorpus::new();
let mut corpus: InMemoryCorpus<BytesInput, _> = 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<BytesInput, XKCDRand>,
_,
@ -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<BytesInput, StdRand> = 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);
}
}
}

View File

@ -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<Vec<u8>>,
}
impl AsAny for Tokens {
}
/// Insert a dictionary token