havoc test case, documentation
This commit is contained in:
parent
c849b435b7
commit
4e4513b24c
@ -315,7 +315,7 @@ where
|
|||||||
observers_buf: _,
|
observers_buf: _,
|
||||||
} => {
|
} => {
|
||||||
stats.client_stats_mut()[0].corpus_size = *corpus_size as u64;
|
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)
|
Ok(BrokerEventResult::Handled)
|
||||||
}
|
}
|
||||||
Event::UpdateStats {
|
Event::UpdateStats {
|
||||||
@ -325,7 +325,7 @@ where
|
|||||||
} => {
|
} => {
|
||||||
// TODO: The stats buffer should be added on client add.
|
// TODO: The stats buffer should be added on client add.
|
||||||
stats.client_stats_mut()[0].executions = *executions as u64;
|
stats.client_stats_mut()[0].executions = *executions as u64;
|
||||||
stats.show(event.name().to_string());
|
stats.display(event.name().to_string());
|
||||||
Ok(BrokerEventResult::Handled)
|
Ok(BrokerEventResult::Handled)
|
||||||
}
|
}
|
||||||
Event::Crash { input: _ } => {
|
Event::Crash { input: _ } => {
|
||||||
@ -540,7 +540,7 @@ where
|
|||||||
} => {
|
} => {
|
||||||
let client = stats.client_stats_mut_for(sender_id);
|
let client = stats.client_stats_mut_for(sender_id);
|
||||||
client.corpus_size = *corpus_size as u64;
|
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)
|
Ok(BrokerEventResult::Handled)
|
||||||
}
|
}
|
||||||
Event::UpdateStats {
|
Event::UpdateStats {
|
||||||
@ -551,7 +551,7 @@ where
|
|||||||
// TODO: The stats buffer should be added on client add.
|
// TODO: The stats buffer should be added on client add.
|
||||||
let client = stats.client_stats_mut_for(sender_id);
|
let client = stats.client_stats_mut_for(sender_id);
|
||||||
client.executions = *executions as u64;
|
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)
|
Ok(BrokerEventResult::Handled)
|
||||||
}
|
}
|
||||||
Event::Crash { input: _ } => {
|
Event::Crash { input: _ } => {
|
||||||
|
@ -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 alloc::{string::String, vec::Vec};
|
||||||
use core::{time, time::Duration};
|
use core::{time, time::Duration};
|
||||||
|
|
||||||
@ -22,6 +24,7 @@ pub struct ClientStats {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl 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) {
|
pub fn update_executions(&mut self, executions: u64, cur_time: time::Duration) {
|
||||||
self.executions = executions;
|
self.executions = executions;
|
||||||
if (cur_time - self.last_window_time).as_secs() > CLIENT_STATS_TIME_WINDOW_SECS {
|
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 {
|
pub fn execs_per_sec(&self, cur_time: time::Duration) -> u64 {
|
||||||
if self.executions == 0 {
|
if self.executions == 0 {
|
||||||
return 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 {
|
pub trait Stats {
|
||||||
/// the client stats (mut)
|
/// the client stats (mut)
|
||||||
fn client_stats_mut(&mut self) -> &mut Vec<ClientStats>;
|
fn client_stats_mut(&mut self) -> &mut Vec<ClientStats>;
|
||||||
@ -56,7 +61,7 @@ pub trait Stats {
|
|||||||
fn start_time(&mut self) -> time::Duration;
|
fn start_time(&mut self) -> time::Duration;
|
||||||
|
|
||||||
/// show the stats to the user
|
/// 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)
|
/// Amount of elements in the corpus (combined for all children)
|
||||||
fn corpus_size(&self) -> u64 {
|
fn corpus_size(&self) -> u64 {
|
||||||
@ -125,7 +130,7 @@ where
|
|||||||
self.start_time
|
self.start_time
|
||||||
}
|
}
|
||||||
|
|
||||||
fn show(&mut self, event_msg: String) {
|
fn display(&mut self, event_msg: String) {
|
||||||
let fmt = format!(
|
let fmt = format!(
|
||||||
"[{}] clients: {}, corpus: {}, executions: {}, exec/sec: {}",
|
"[{}] clients: {}, corpus: {}, executions: {}, exec/sec: {}",
|
||||||
event_msg,
|
event_msg,
|
||||||
|
@ -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;
|
use core::marker::PhantomData;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
|
//! A sancov runtime to update a simple u8 map with coverage-information during fuzzing
|
||||||
|
|
||||||
//#![feature(asm)]
|
//#![feature(asm)]
|
||||||
|
|
||||||
|
/// The map size used by this instance.
|
||||||
const MAP_SIZE: usize = 65536;
|
const MAP_SIZE: usize = 65536;
|
||||||
|
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
@ -11,6 +14,7 @@ pub static mut __lafl_cmp_map: *mut u8 = unsafe { __lafl_dummy_map.as_ptr() as *
|
|||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
pub static mut __lafl_max_edges_size: u32 = 0;
|
pub static mut __lafl_max_edges_size: u32 = 0;
|
||||||
|
|
||||||
|
/// Called for each branch the target program takes.
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard(guard: &u32) {
|
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);
|
//*trace_byte = (*trace_byte).wrapping_add(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Called when the targetprogram starts
|
||||||
#[no_mangle]
|
#[no_mangle]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) {
|
pub unsafe extern "C" fn __sanitizer_cov_trace_pc_guard_init(mut start: *mut u32, stop: *mut u32) {
|
||||||
|
@ -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::{
|
use alloc::{
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Generators may generate bytes or, in general, data, for inputs.
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use core::{cmp::min, marker::PhantomData};
|
use core::{cmp::min, marker::PhantomData};
|
||||||
|
|
||||||
|
@ -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 alloc::{borrow::ToOwned, rc::Rc, vec::Vec};
|
||||||
use core::{cell::RefCell, convert::From};
|
use core::{cell::RefCell, convert::From};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@ -5,7 +8,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use crate::inputs::{HasBytesVec, HasTargetBytes, Input, TargetBytes};
|
use crate::inputs::{HasBytesVec, HasTargetBytes, Input, TargetBytes};
|
||||||
|
|
||||||
/// A bytes input is the basic input
|
/// A bytes input is the basic input
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq)]
|
||||||
pub struct BytesInput {
|
pub struct BytesInput {
|
||||||
/// The raw input bytes
|
/// The raw input bytes
|
||||||
bytes: Vec<u8>,
|
bytes: Vec<u8>,
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Inputs are the actual contents sent to a target for each exeuction.
|
||||||
|
|
||||||
pub mod bytes;
|
pub mod bytes;
|
||||||
pub use bytes::BytesInput;
|
pub use bytes::BytesInput;
|
||||||
|
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Mutators mutate input during fuzzing.
|
||||||
|
|
||||||
pub mod scheduled;
|
pub mod scheduled;
|
||||||
pub use scheduled::*;
|
pub use scheduled::*;
|
||||||
pub mod mutations;
|
pub mod mutations;
|
||||||
|
@ -502,66 +502,6 @@ where
|
|||||||
Ok(MutationResult::Mutated)
|
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>(
|
pub fn mutation_bytesinsert<I, M, R, S>(
|
||||||
mutator: &mut M,
|
mutator: &mut M,
|
||||||
rand: &mut R,
|
rand: &mut R,
|
||||||
|
@ -208,7 +208,7 @@ where
|
|||||||
let idx = self.scheduled.schedule(14, rand, input);
|
let idx = self.scheduled.schedule(14, rand, input);
|
||||||
let mutation = match idx {
|
let mutation = match idx {
|
||||||
0 => mutation_bitflip,
|
0 => mutation_bitflip,
|
||||||
/*1 => mutation_byteflip,
|
1 => mutation_byteflip,
|
||||||
2 => mutation_byteinc,
|
2 => mutation_byteinc,
|
||||||
3 => mutation_bytedec,
|
3 => mutation_bytedec,
|
||||||
4 => mutation_byteneg,
|
4 => mutation_byteneg,
|
||||||
@ -219,7 +219,7 @@ where
|
|||||||
8 => mutation_dwordadd,
|
8 => mutation_dwordadd,
|
||||||
9 => mutation_byteinteresting,
|
9 => mutation_byteinteresting,
|
||||||
10 => mutation_wordinteresting,
|
10 => mutation_wordinteresting,
|
||||||
11 => mutation_dwordinteresting,*/
|
11 => mutation_dwordinteresting,
|
||||||
_ => mutation_splice,
|
_ => mutation_splice,
|
||||||
};
|
};
|
||||||
mutation(self, rand, state, input)?;
|
mutation(self, rand, state, input)?;
|
||||||
@ -323,18 +323,20 @@ where
|
|||||||
mod tests {
|
mod tests {
|
||||||
use crate::{
|
use crate::{
|
||||||
corpus::{Corpus, InMemoryCorpus, Testcase},
|
corpus::{Corpus, InMemoryCorpus, Testcase},
|
||||||
inputs::BytesInput,
|
inputs::{BytesInput, HasBytesVec},
|
||||||
inputs::HasBytesVec,
|
mutators::{
|
||||||
mutators::scheduled::{mutation_splice, StdScheduledMutator},
|
scheduled::{mutation_splice, HavocBytesMutator, StdScheduledMutator},
|
||||||
|
Mutator,
|
||||||
|
},
|
||||||
state::State,
|
state::State,
|
||||||
utils::{Rand, XKCDRand},
|
utils::{Rand, StdRand, XKCDRand},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_mut_splice() {
|
fn test_mut_scheduled() {
|
||||||
// With the current impl, seed of 1 will result in a split at pos 2.
|
// With the current impl, seed of 1 will result in a split at pos 2.
|
||||||
let mut rand = XKCDRand::new();
|
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!['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());
|
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, ());
|
let mut state = State::new(corpus, ());
|
||||||
|
|
||||||
rand.set_seed(5);
|
rand.set_seed(5);
|
||||||
|
|
||||||
let mut mutator = StdScheduledMutator::<
|
let mut mutator = StdScheduledMutator::<
|
||||||
InMemoryCorpus<BytesInput, XKCDRand>,
|
InMemoryCorpus<BytesInput, XKCDRand>,
|
||||||
_,
|
_,
|
||||||
@ -362,4 +365,30 @@ mod tests {
|
|||||||
// TODO: Maybe have a fixed rand for this purpose?
|
// TODO: Maybe have a fixed rand for this purpose?
|
||||||
assert_eq!(input.bytes(), &['a' as u8, 'b' as u8, 'f' as u8])
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 {
|
struct Tokens {
|
||||||
vec: Vec<Vec<u8>>,
|
vec: Vec<Vec<u8>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl AsAny for Tokens {
|
impl AsAny for Tokens {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Insert a dictionary token
|
/// Insert a dictionary token
|
||||||
|
Loading…
x
Reference in New Issue
Block a user