havoc test case, documentation
This commit is contained in:
parent
c849b435b7
commit
4e4513b24c
@ -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: _ } => {
|
||||
|
@ -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,
|
||||
|
@ -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)]
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Generators may generate bytes or, in general, data, for inputs.
|
||||
|
||||
use alloc::vec::Vec;
|
||||
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 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>,
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Inputs are the actual contents sent to a target for each exeuction.
|
||||
|
||||
pub mod bytes;
|
||||
pub use bytes::BytesInput;
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
//! Mutators mutate input during fuzzing.
|
||||
|
||||
pub mod scheduled;
|
||||
pub use scheduled::*;
|
||||
pub mod mutations;
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user