Grimoire fixes (#993)

* fixup grimoire/generalisation, remove GeneralizedInput in favour of metadata

* additional cleanup

* transformable inputs to solve the grimoire problem

* explicit use of 'transforming' to keep typing compatible with normal usage

* clippy fix

* fixes for nautilus, python

* explicit inlining for reflexive impl

* fix for tutorial

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
This commit is contained in:
Addison Crump 2023-01-13 01:07:21 +01:00 committed by GitHub
parent ec84c71eae
commit 28786c943a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 608 additions and 687 deletions

View File

@ -9,7 +9,7 @@ use libafl::{
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
inputs::{GeneralizedInput, HasTargetBytes},
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
mutators::{
havoc_mutations, scheduled::StdScheduledMutator, GrimoireExtensionMutator,
@ -59,13 +59,13 @@ pub fn main() {
let mut file = fs::File::open(path).expect("no file found");
let mut buffer = vec![];
file.read_to_end(&mut buffer).expect("buffer overflow");
let input = GeneralizedInput::new(buffer);
let input = BytesInput::new(buffer);
initial_inputs.push(input);
}
}
// The closure that we want to fuzz
let mut harness = |input: &GeneralizedInput| {
let mut harness = |input: &BytesInput| {
let target_bytes = input.target_bytes();
let bytes = target_bytes.as_slice();
@ -77,12 +77,6 @@ pub fn main() {
signals_set(3);
}
unsafe {
if input.grimoire_mutated {
// println!(">>> {:?}", input.generalized());
println!(">>> {:?}", std::str::from_utf8_unchecked(bytes));
}
}
signals_set(1);
ExitKind::Ok
};
@ -158,7 +152,7 @@ pub fn main() {
let mut stages = tuple_list!(
generalization,
StdMutationalStage::new(mutator),
StdMutationalStage::new(grimoire_mutator)
StdMutationalStage::transforming(grimoire_mutator)
);
for input in initial_inputs {

View File

@ -31,7 +31,7 @@ use libafl::{
feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, GeneralizedInput, HasTargetBytes},
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
mutators::{
grimoire::{
@ -47,8 +47,8 @@ use libafl::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
},
stages::{
calibrate::CalibrationStage, dump::DumpToDiskStage, power::StdPowerMutationalStage,
GeneralizationStage, StdMutationalStage, TracingStage,
calibrate::CalibrationStage, power::StdPowerMutationalStage, GeneralizationStage,
StdMutationalStage, TracingStage,
},
state::{HasCorpus, HasMetadata, StdState},
Error,
@ -148,11 +148,8 @@ pub fn libafl_main() {
}
}
let mut crashes = out_dir.clone();
let mut report = out_dir.clone();
crashes.push("crashes");
report.push("report");
out_dir.push("queue");
drop(fs::create_dir(&report));
let in_dir = PathBuf::from(
res.get_one::<String>("in")
@ -177,9 +174,7 @@ pub fn libafl_main() {
);
if check_if_textual(&in_dir, &tokens) {
fuzz_text(
out_dir, crashes, &report, &in_dir, tokens, &logfile, timeout,
)
fuzz_text(out_dir, crashes, &in_dir, tokens, &logfile, timeout)
.expect("An error occurred while fuzzing");
} else {
fuzz_binary(out_dir, crashes, &in_dir, tokens, &logfile, timeout)
@ -466,7 +461,6 @@ fn fuzz_binary(
fn fuzz_text(
corpus_dir: PathBuf,
objective_dir: PathBuf,
report_dir: &Path,
seed_dir: &PathBuf,
tokenfile: Option<PathBuf>,
logfile: &PathBuf,
@ -586,7 +580,7 @@ fn fuzz_text(
),
3,
);
let grimoire = StdMutationalStage::new(grimoire_mutator);
let grimoire = StdMutationalStage::transforming(grimoire_mutator);
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(StdWeightedScheduler::with_schedule(
@ -597,7 +591,7 @@ fn fuzz_text(
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |input: &GeneralizedInput| {
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
libfuzzer_test_one_input(buf);
@ -633,23 +627,8 @@ fn fuzz_text(
timeout * 10,
));
let fuzzbench = DumpToDiskStage::new(
|input: &GeneralizedInput| input.target_bytes().into(),
&report_dir.join("queue"),
&report_dir.join("crashes"),
)
.unwrap();
// The order of the stages matter!
let mut stages = tuple_list!(
fuzzbench,
generalization,
calibration,
tracing,
i2s,
power,
grimoire
);
let mut stages = tuple_list!(generalization, calibration, tracing, i2s, power, grimoire);
// Read tokens
if state.metadata().get::<Tokens>().is_none() {

View File

@ -4,7 +4,6 @@ use libafl::{
rands::{Rand, StdRand},
tuples::Named,
},
inputs::UsesInput,
mutators::{MutationResult, Mutator},
state::HasRand,
Error,
@ -16,9 +15,9 @@ pub struct LainMutator {
inner: lain::mutator::Mutator<StdRand>,
}
impl<S> Mutator<S> for LainMutator
impl<S> Mutator<PacketData, S> for LainMutator
where
S: UsesInput<Input = PacketData> + HasRand,
S: HasRand,
{
fn mutate(
&mut self,

View File

@ -5,7 +5,7 @@ use core::{cmp::min, marker::PhantomData};
use crate::{
bolts::rands::Rand,
inputs::{bytes::BytesInput, GeneralizedInput, Input},
inputs::{bytes::BytesInput, Input},
state::HasRand,
Error,
};
@ -33,51 +33,6 @@ where
fn generate_dummy(&self, state: &mut S) -> I;
}
/// A Generator that produces [`GeneralizedInput`]s from a wrapped [`BytesInput`] generator
#[derive(Clone, Debug)]
pub struct GeneralizedInputBytesGenerator<G, S> {
bytes_generator: G,
phantom: PhantomData<S>,
}
impl<G, S> GeneralizedInputBytesGenerator<G, S>
where
S: HasRand,
G: Generator<BytesInput, S>,
{
/// Creates a new [`GeneralizedInputBytesGenerator`] by wrapping a bytes generator.
pub fn new(bytes_generator: G) -> Self {
Self {
bytes_generator,
phantom: PhantomData,
}
}
}
impl<G, S> From<G> for GeneralizedInputBytesGenerator<G, S>
where
S: HasRand,
G: Generator<BytesInput, S>,
{
fn from(bytes_generator: G) -> Self {
Self::new(bytes_generator)
}
}
impl<G, S> Generator<GeneralizedInput, S> for GeneralizedInputBytesGenerator<G, S>
where
S: HasRand,
G: Generator<BytesInput, S>,
{
fn generate(&mut self, state: &mut S) -> Result<GeneralizedInput, Error> {
Ok(self.bytes_generator.generate(state)?.into())
}
fn generate_dummy(&self, state: &mut S) -> GeneralizedInput {
self.bytes_generator.generate_dummy(state).into()
}
}
#[derive(Clone, Debug)]
/// Generates random bytes
pub struct RandBytesGenerator<S>

View File

@ -1,18 +1,16 @@
//! The `GeneralizedInput` is an input that ca be generalized to represent a rule, used by Grimoire
use alloc::{borrow::ToOwned, rc::Rc, string::String, vec::Vec};
use core::{cell::RefCell, convert::From, hash::Hasher};
#[cfg(feature = "std")]
use std::{fs::File, io::Read, path::Path};
use alloc::vec::Vec;
use ahash::AHasher;
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use crate::Error;
use crate::{
bolts::{ownedref::OwnedSlice, HasLen},
inputs::{BytesInput, HasBytesVec, HasTargetBytes, Input},
corpus::{Corpus, CorpusId, Testcase},
impl_serdeany,
inputs::BytesInput,
stages::mutational::{MutatedTransform, MutatedTransformPost},
state::{HasCorpus, HasMetadata},
Error,
};
/// An item of the generalized input
@ -24,142 +22,31 @@ pub enum GeneralizedItem {
Gap,
}
/// A bytes input with a generalized version mainly used for Grimoire
/// Metadata regarding the generalised content of an input
#[derive(Serialize, Deserialize, Clone, Debug, Default, PartialEq, Eq, Hash)]
pub struct GeneralizedInput {
/// The raw input bytes
bytes: Vec<u8>,
generalized: Option<Vec<GeneralizedItem>>,
/// If was mutated or not by Grimoire
pub grimoire_mutated: bool,
pub struct GeneralizedInputMetadata {
generalized: Vec<GeneralizedItem>,
}
impl Input for GeneralizedInput {
/// Generate a name for this input
fn generate_name(&self, _idx: usize) -> String {
let mut hasher = AHasher::new_with_keys(0, 0);
// TODO add generalized
hasher.write(self.bytes());
format!("{:016x}", hasher.finish())
}
/// Load from a plain file of bytes
#[cfg(feature = "std")]
fn from_file<P>(path: P) -> Result<Self, Error>
where
P: AsRef<Path>,
{
let mut file = File::open(path)?;
let mut bytes: Vec<u8> = vec![];
file.read_to_end(&mut bytes)?;
Ok(Self {
bytes,
generalized: None,
grimoire_mutated: false,
})
}
/// An hook executed before being added to the corpus
fn wrapped_as_testcase(&mut self) {
// remove generalized for inputs generated with bit-level mutations
// and fix bytes for the ones generated by grimoire
if self.grimoire_mutated {
self.bytes = self.generalized_to_bytes();
} else {
self.generalized = None;
}
// restore to allow bit-level mutations
self.grimoire_mutated = false;
}
}
/// Rc Ref-cell from Input
impl From<GeneralizedInput> for Rc<RefCell<GeneralizedInput>> {
fn from(input: GeneralizedInput) -> Self {
Rc::new(RefCell::new(input))
}
}
impl HasBytesVec for GeneralizedInput {
#[inline]
fn bytes(&self) -> &[u8] {
&self.bytes
}
#[inline]
fn bytes_mut(&mut self) -> &mut Vec<u8> {
&mut self.bytes
}
}
impl HasTargetBytes for GeneralizedInput {
#[inline]
fn target_bytes(&self) -> OwnedSlice<u8> {
if self.grimoire_mutated {
OwnedSlice::from(self.generalized_to_bytes())
} else {
OwnedSlice::from(&self.bytes)
}
}
}
impl HasLen for GeneralizedInput {
#[inline]
fn len(&self) -> usize {
self.bytes.len()
}
}
impl From<Vec<u8>> for GeneralizedInput {
fn from(bytes: Vec<u8>) -> Self {
Self::new(bytes)
}
}
impl From<&[u8]> for GeneralizedInput {
fn from(bytes: &[u8]) -> Self {
Self::new(bytes.to_owned())
}
}
impl From<BytesInput> for GeneralizedInput {
fn from(bytes_input: BytesInput) -> Self {
Self::new(bytes_input.bytes)
}
}
impl From<&BytesInput> for GeneralizedInput {
fn from(bytes_input: &BytesInput) -> Self {
bytes_input.bytes().into()
}
}
impl GeneralizedInput {
/// Creates a new bytes input using the given bytes
#[must_use]
pub fn new(bytes: Vec<u8>) -> Self {
Self {
bytes,
generalized: None,
grimoire_mutated: false,
}
}
impl_serdeany!(GeneralizedInputMetadata);
impl GeneralizedInputMetadata {
/// Fill the generalized vector from a slice of option (None -> Gap)
pub fn generalized_from_options(&mut self, v: &[Option<u8>]) {
let mut res = vec![];
#[must_use]
pub fn generalized_from_options(v: &[Option<u8>]) -> Self {
let mut generalized = vec![];
let mut bytes = vec![];
if v.first() != Some(&None) {
res.push(GeneralizedItem::Gap);
generalized.push(GeneralizedItem::Gap);
}
for e in v {
match e {
None => {
if !bytes.is_empty() {
res.push(GeneralizedItem::Bytes(bytes.clone()));
generalized.push(GeneralizedItem::Bytes(bytes.clone()));
bytes.clear();
}
res.push(GeneralizedItem::Gap);
generalized.push(GeneralizedItem::Gap);
}
Some(b) => {
bytes.push(*b);
@ -167,36 +54,19 @@ impl GeneralizedInput {
}
}
if !bytes.is_empty() {
res.push(GeneralizedItem::Bytes(bytes));
generalized.push(GeneralizedItem::Bytes(bytes));
}
if res.last() != Some(&GeneralizedItem::Gap) {
res.push(GeneralizedItem::Gap);
}
self.generalized = Some(res);
}
/// Extend the generalized input
pub fn generalized_extend(&mut self, other: &[GeneralizedItem]) {
let gen = self.generalized.get_or_insert_with(Vec::new);
if gen.last().is_some()
&& other.first().is_some()
&& *gen.last().unwrap() == GeneralizedItem::Gap
&& *other.first().unwrap() == GeneralizedItem::Gap
{
gen.extend_from_slice(&other[1..]);
} else {
gen.extend_from_slice(other);
if generalized.last() != Some(&GeneralizedItem::Gap) {
generalized.push(GeneralizedItem::Gap);
}
Self { generalized }
}
/// Get the size of the generalized
#[must_use]
pub fn generalized_len(&self) -> usize {
match &self.generalized {
None => 0,
Some(gen) => {
let mut size = 0;
for item in gen {
for item in &self.generalized {
match item {
GeneralizedItem::Bytes(b) => size += b.len(),
GeneralizedItem::Gap => size += 1,
@ -204,34 +74,73 @@ impl GeneralizedInput {
}
size
}
}
}
/// Convert generalized to bytes
#[must_use]
pub fn generalized_to_bytes(&self) -> Vec<u8> {
match &self.generalized {
None => vec![],
Some(gen) => {
let mut bytes = vec![];
for item in gen {
if let GeneralizedItem::Bytes(b) = item {
bytes.extend_from_slice(b);
}
}
bytes
}
}
self.generalized
.iter()
.filter_map(|item| match item {
GeneralizedItem::Bytes(bytes) => Some(bytes),
GeneralizedItem::Gap => None,
})
.flatten()
.copied()
.collect()
}
/// Get the generalized input
#[must_use]
pub fn generalized(&self) -> Option<&[GeneralizedItem]> {
self.generalized.as_deref()
pub fn generalized(&self) -> &[GeneralizedItem] {
&self.generalized
}
/// Get the generalized input (mutable)
pub fn generalized_mut(&mut self) -> &mut Option<Vec<GeneralizedItem>> {
pub fn generalized_mut(&mut self) -> &mut Vec<GeneralizedItem> {
&mut self.generalized
}
}
impl<S> MutatedTransform<BytesInput, S> for GeneralizedInputMetadata
where
S: HasCorpus,
{
type Post = Self;
fn try_transform_from(
base: &Testcase<BytesInput>,
_state: &S,
corpus_idx: CorpusId,
) -> Result<Self, Error> {
base.metadata()
.get::<GeneralizedInputMetadata>()
.ok_or_else(|| {
Error::key_not_found(format!(
"Couldn't find the GeneralizedInputMetadata for corpus entry {corpus_idx}",
))
})
.cloned()
}
fn try_transform_into(self, _state: &S) -> Result<(BytesInput, Self::Post), Error> {
Ok((BytesInput::from(self.generalized_to_bytes()), self))
}
}
impl<S> MutatedTransformPost<S> for GeneralizedInputMetadata
where
S: HasCorpus,
{
fn post_exec(
self,
state: &mut S,
_stage_idx: i32,
corpus_idx: Option<CorpusId>,
) -> Result<(), Error> {
if let Some(corpus_idx) = corpus_idx {
let mut testcase = state.corpus().get(corpus_idx)?.borrow_mut();
testcase.metadata_mut().insert(self);
}
Ok(())
}
}

View File

@ -23,7 +23,7 @@ use crate::{
#[derive(Debug, Default)]
pub struct EncodedRandMutator;
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedRandMutator {
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedRandMutator {
fn mutate(
&mut self,
state: &mut S,
@ -58,7 +58,7 @@ impl EncodedRandMutator {
#[derive(Debug, Default)]
pub struct EncodedIncMutator;
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedIncMutator {
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedIncMutator {
fn mutate(
&mut self,
state: &mut S,
@ -93,7 +93,7 @@ impl EncodedIncMutator {
#[derive(Debug, Default)]
pub struct EncodedDecMutator;
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedDecMutator {
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedDecMutator {
fn mutate(
&mut self,
state: &mut S,
@ -128,7 +128,7 @@ impl EncodedDecMutator {
#[derive(Debug, Default)]
pub struct EncodedAddMutator;
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedAddMutator {
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedAddMutator {
fn mutate(
&mut self,
state: &mut S,
@ -167,7 +167,7 @@ impl EncodedAddMutator {
#[derive(Debug, Default)]
pub struct EncodedDeleteMutator;
impl<S: HasRand + UsesInput<Input = EncodedInput>> Mutator<S> for EncodedDeleteMutator {
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedDeleteMutator {
fn mutate(
&mut self,
state: &mut S,
@ -207,9 +207,9 @@ pub struct EncodedInsertCopyMutator {
tmp_buf: Vec<u32>,
}
impl<S> Mutator<S> for EncodedInsertCopyMutator
impl<S> Mutator<EncodedInput, S> for EncodedInsertCopyMutator
where
S: UsesInput<Input = EncodedInput> + HasRand + HasMaxSize,
S: HasRand + HasMaxSize,
{
fn mutate(
&mut self,
@ -268,7 +268,7 @@ impl EncodedInsertCopyMutator {
#[derive(Debug, Default)]
pub struct EncodedCopyMutator;
impl<S: UsesInput<Input = EncodedInput> + HasRand> Mutator<S> for EncodedCopyMutator {
impl<S: HasRand> Mutator<EncodedInput, S> for EncodedCopyMutator {
fn mutate(
&mut self,
state: &mut S,
@ -308,7 +308,7 @@ impl EncodedCopyMutator {
#[derive(Debug, Default)]
pub struct EncodedCrossoverInsertMutator;
impl<S> Mutator<S> for EncodedCrossoverInsertMutator
impl<S> Mutator<S::Input, S> for EncodedCrossoverInsertMutator
where
S: UsesInput<Input = EncodedInput> + HasRand + HasCorpus + HasMaxSize,
{
@ -381,7 +381,7 @@ impl EncodedCrossoverInsertMutator {
#[derive(Debug, Default)]
pub struct EncodedCrossoverReplaceMutator;
impl<S> Mutator<S> for EncodedCrossoverReplaceMutator
impl<S> Mutator<S::Input, S> for EncodedCrossoverReplaceMutator
where
S: UsesInput<Input = EncodedInput> + HasRand + HasCorpus,
{

View File

@ -10,7 +10,7 @@ use crate::{
bolts::{rands::Rand, tuples::Named},
corpus::Corpus,
generators::GramatronGenerator,
inputs::{GramatronInput, Terminal, UsesInput},
inputs::{GramatronInput, Terminal},
mutators::{MutationResult, Mutator},
random_corpus_id,
state::{HasCorpus, HasMetadata, HasRand},
@ -28,9 +28,9 @@ where
generator: &'a GramatronGenerator<'a, S>,
}
impl<'a, S> Mutator<S> for GramatronRandomMutator<'a, S>
impl<'a, S> Mutator<GramatronInput, S> for GramatronRandomMutator<'a, S>
where
S: UsesInput<Input = GramatronInput> + HasRand + HasMetadata,
S: HasRand + HasMetadata,
{
fn mutate(
&mut self,
@ -97,9 +97,9 @@ impl GramatronIdxMapMetadata {
#[derive(Default, Debug)]
pub struct GramatronSpliceMutator;
impl<S> Mutator<S> for GramatronSpliceMutator
impl<S> Mutator<S::Input, S> for GramatronSpliceMutator
where
S: UsesInput<Input = GramatronInput> + HasRand + HasCorpus + HasMetadata,
S: HasRand + HasCorpus<Input = GramatronInput> + HasMetadata,
{
fn mutate(
&mut self,
@ -169,9 +169,9 @@ pub struct GramatronRecursionMutator {
feature: Vec<Terminal>,
}
impl<S> Mutator<S> for GramatronRecursionMutator
impl<S> Mutator<GramatronInput, S> for GramatronRecursionMutator
where
S: UsesInput<Input = GramatronInput> + HasRand + HasMetadata,
S: HasRand + HasMetadata,
{
fn mutate(
&mut self,

View File

@ -7,7 +7,7 @@ use core::cmp::{max, min};
use crate::{
bolts::{rands::Rand, tuples::Named},
corpus::Corpus,
inputs::{GeneralizedInput, GeneralizedItem, UsesInput},
inputs::{GeneralizedInputMetadata, GeneralizedItem},
mutators::{token_mutations::Tokens, MutationResult, Mutator},
stages::generalization::GeneralizedIndexesMetadata,
state::{HasCorpus, HasMetadata, HasRand},
@ -24,7 +24,7 @@ fn extend_with_random_generalized<S>(
gap_indices: &mut Vec<usize>,
) -> Result<(), Error>
where
S: HasMetadata + HasRand + HasCorpus<Input = GeneralizedInput>,
S: HasMetadata + HasRand + HasCorpus,
{
let rand_idx = state.rand_mut().next() as usize;
@ -40,27 +40,18 @@ where
.unwrap()
};
/*if state
.corpus()
.get(idx)?
.borrow_mut()
.load_input()?
.generalized()
.is_none()
{
return Ok(true);
}*/
if state.rand_mut().below(100) > CHOOSE_SUBINPUT_PROB {
if state.rand_mut().below(100) < 50 {
let rand1 = state.rand_mut().next() as usize;
let rand2 = state.rand_mut().next() as usize;
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
if let Some(other) = other_testcase
.metadata_mut()
.get::<GeneralizedInputMetadata>()
{
if other.generalized_len() > 0 {
let gen = other.generalized().unwrap();
let gen = other.generalized();
for (i, _) in gen
.iter()
@ -86,6 +77,7 @@ where
return Ok(());
}
}
}
let rand1 = state.rand_mut().next() as usize;
@ -107,8 +99,11 @@ where
}
let mut other_testcase = state.corpus().get(idx)?.borrow_mut();
let other = other_testcase.load_input()?;
let gen = other.generalized().unwrap();
let other = other_testcase
.metadata_mut()
.get::<GeneralizedInputMetadata>()
.unwrap();
let gen = other.generalized();
if items.last() == Some(&GeneralizedItem::Gap) && gen.first() == Some(&GeneralizedItem::Gap) {
items.extend_from_slice(&gen[1..]);
@ -128,27 +123,22 @@ pub struct GrimoireExtensionMutator {
gap_indices: Vec<usize>,
}
impl<S> Mutator<S> for GrimoireExtensionMutator
impl<S> Mutator<GeneralizedInputMetadata, S> for GrimoireExtensionMutator
where
S: UsesInput<Input = GeneralizedInput> + HasMetadata + HasRand + HasCorpus,
S: HasMetadata + HasRand + HasCorpus,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut GeneralizedInput,
generalised_meta: &mut GeneralizedInputMetadata,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.generalized().is_none() {
return Ok(MutationResult::Skipped);
}
extend_with_random_generalized(
state,
input.generalized_mut().as_mut().unwrap(),
generalised_meta.generalized_mut(),
&mut self.gap_indices,
)?;
input.grimoire_mutated = true;
Ok(MutationResult::Mutated)
}
}
@ -176,29 +166,25 @@ pub struct GrimoireRecursiveReplacementMutator {
gap_indices: Vec<usize>,
}
impl<S> Mutator<S> for GrimoireRecursiveReplacementMutator
impl<S> Mutator<GeneralizedInputMetadata, S> for GrimoireRecursiveReplacementMutator
where
S: UsesInput<Input = GeneralizedInput> + HasMetadata + HasRand + HasCorpus,
S: HasMetadata + HasRand + HasCorpus,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut GeneralizedInput,
generalised_meta: &mut GeneralizedInputMetadata,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.generalized().is_none() {
return Ok(MutationResult::Skipped);
}
let mut mutated = MutationResult::Skipped;
let depth = *state.rand_mut().choose(&RECURSIVE_REPLACEMENT_DEPTH);
for _ in 0..depth {
if input.generalized_len() >= MAX_RECURSIVE_REPLACEMENT_LEN {
if generalised_meta.generalized_len() >= MAX_RECURSIVE_REPLACEMENT_LEN {
break;
}
let gen = input.generalized_mut().as_mut().unwrap();
let gen = generalised_meta.generalized_mut();
for (i, _) in gen
.iter()
@ -207,6 +193,9 @@ where
{
self.gap_indices.push(i);
}
if self.gap_indices.is_empty() {
break;
}
let selected = *state.rand_mut().choose(&self.gap_indices);
self.gap_indices.clear();
@ -219,7 +208,6 @@ where
self.scratch.clear();
mutated = MutationResult::Mutated;
input.grimoire_mutated = true;
}
Ok(mutated)
@ -247,30 +235,28 @@ impl GrimoireRecursiveReplacementMutator {
#[derive(Debug, Default)]
pub struct GrimoireStringReplacementMutator {}
impl<S> Mutator<S> for GrimoireStringReplacementMutator
impl<S> Mutator<GeneralizedInputMetadata, S> for GrimoireStringReplacementMutator
where
S: UsesInput<Input = GeneralizedInput> + HasMetadata + HasRand,
S: HasMetadata + HasRand + HasCorpus,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut GeneralizedInput,
generalised_meta: &mut GeneralizedInputMetadata,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.generalized().is_none() {
return Ok(MutationResult::Skipped);
}
let tokens_len = {
let meta = state.metadata().get::<Tokens>();
if meta.is_none() {
if let Some(tokens) = meta {
if tokens.is_empty() {
return Ok(MutationResult::Skipped);
}
if meta.unwrap().tokens().is_empty() {
tokens.tokens().len()
} else {
return Ok(MutationResult::Skipped);
}
meta.unwrap().tokens().len()
};
let token_find = state.rand_mut().below(tokens_len as u64) as usize;
let mut token_replace = state.rand_mut().below(tokens_len as u64) as usize;
if token_find == token_replace {
@ -286,7 +272,7 @@ where
let mut mutated = MutationResult::Skipped;
let gen = input.generalized_mut().as_mut().unwrap();
let gen = generalised_meta.generalized_mut();
rand_idx %= gen.len();
'first: for item in &mut gen[..rand_idx] {
@ -330,7 +316,6 @@ where
}
}
input.grimoire_mutated = true;
Ok(mutated)
}
}
@ -355,22 +340,17 @@ pub struct GrimoireRandomDeleteMutator {
gap_indices: Vec<usize>,
}
impl<S> Mutator<S> for GrimoireRandomDeleteMutator
impl<S> Mutator<GeneralizedInputMetadata, S> for GrimoireRandomDeleteMutator
where
S: UsesInput<Input = GeneralizedInput> + HasMetadata + HasRand + HasCorpus,
S: HasMetadata + HasRand + HasCorpus,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut GeneralizedInput,
generalised_meta: &mut GeneralizedInputMetadata,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.generalized().is_none() {
return Ok(MutationResult::Skipped);
}
input.grimoire_mutated = true;
let gen = input.generalized_mut().as_mut().unwrap();
let gen = generalised_meta.generalized_mut();
for (i, _) in gen
.iter()
@ -387,12 +367,14 @@ where
self.gap_indices.clear();
if min_idx == max_idx {
Ok(MutationResult::Skipped)
let result = if min_idx == max_idx {
MutationResult::Skipped
} else {
gen.drain(min_idx..max_idx);
Ok(MutationResult::Mutated)
}
MutationResult::Mutated
};
Ok(result)
}
}

View File

@ -25,7 +25,6 @@ pub use nautilus::*;
use crate::{
bolts::tuples::{HasConstLen, Named},
corpus::CorpusId,
inputs::UsesInput,
Error,
};
@ -45,15 +44,12 @@ pub enum MutationResult {
/// A mutator takes input, and mutates it.
/// Simple as that.
pub trait Mutator<S>
where
S: UsesInput,
{
pub trait Mutator<I, S> {
/// Mutate a given input
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error>;
@ -69,15 +65,12 @@ where
}
/// A `Tuple` of `Mutators` that can execute multiple `Mutators` in a row.
pub trait MutatorsTuple<S>: HasConstLen
where
S: UsesInput,
{
pub trait MutatorsTuple<I, S>: HasConstLen {
/// Runs the `mutate` function on all `Mutators` in this `Tuple`.
fn mutate_all(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error>;
@ -94,7 +87,7 @@ where
&mut self,
index: usize,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error>;
@ -108,14 +101,11 @@ where
) -> Result<(), Error>;
}
impl<S> MutatorsTuple<S> for ()
where
S: UsesInput,
{
impl<I, S> MutatorsTuple<I, S> for () {
fn mutate_all(
&mut self,
_state: &mut S,
_input: &mut S::Input,
_input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
Ok(MutationResult::Skipped)
@ -134,7 +124,7 @@ where
&mut self,
_index: usize,
_state: &mut S,
_input: &mut S::Input,
_input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
Ok(MutationResult::Skipped)
@ -151,16 +141,15 @@ where
}
}
impl<Head, Tail, S> MutatorsTuple<S> for (Head, Tail)
impl<Head, Tail, I, S> MutatorsTuple<I, S> for (Head, Tail)
where
Head: Mutator<S> + Named,
Tail: MutatorsTuple<S>,
S: UsesInput,
Head: Mutator<I, S> + Named,
Tail: MutatorsTuple<I, S>,
{
fn mutate_all(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let r = self.0.mutate(state, input, stage_idx)?;
@ -185,7 +174,7 @@ where
&mut self,
index: usize,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
if index == 0 {
@ -238,7 +227,7 @@ pub mod pybind {
}
}
impl Mutator<PythonStdState> for PyObjectMutator {
impl Mutator<BytesInput, PythonStdState> for PyObjectMutator {
fn mutate(
&mut self,
state: &mut PythonStdState,
@ -337,7 +326,7 @@ pub mod pybind {
}
}
impl Mutator<PythonStdState> for PythonMutator {
impl Mutator<BytesInput, PythonStdState> for PythonMutator {
fn mutate(
&mut self,
state: &mut PythonStdState,

View File

@ -359,21 +359,21 @@ pub enum MOptMode {
/// This is the main struct of `MOpt`, an `AFL` mutator.
/// See the original `MOpt` implementation in <https://github.com/puppet-meteor/MOpt-AFL>
pub struct StdMOptMutator<MT, S>
pub struct StdMOptMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
mode: MOptMode,
finds_before: usize,
mutations: MT,
max_stack_pow: u64,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl<MT, S> Debug for StdMOptMutator<MT, S>
impl<I, MT, S> Debug for StdMOptMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
@ -381,21 +381,21 @@ where
f,
"StdMOptMutator with {} mutations for Input type {}",
self.mutations.len(),
core::any::type_name::<S::Input>()
core::any::type_name::<I>()
)
}
}
impl<MT, S> Mutator<S> for StdMOptMutator<MT, S>
impl<I, MT, S> Mutator<I, S> for StdMOptMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
#[inline]
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
self.finds_before = state.corpus().count() + state.solutions().count();
@ -521,9 +521,9 @@ where
}
}
impl<MT, S> StdMOptMutator<MT, S>
impl<I, MT, S> StdMOptMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
/// Create a new [`StdMOptMutator`].
@ -548,7 +548,7 @@ where
fn core_mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
@ -578,7 +578,7 @@ where
fn pilot_mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
@ -613,9 +613,9 @@ where
}
}
impl<MT, S> ComposedByMutations<MT, S> for StdMOptMutator<MT, S>
impl<I, MT, S> ComposedByMutations<I, MT, S> for StdMOptMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
/// Get the mutations
@ -631,19 +631,19 @@ where
}
}
impl<MT, S> ScheduledMutator<MT, S> for StdMOptMutator<MT, S>
impl<I, MT, S> ScheduledMutator<I, MT, S> for StdMOptMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata + HasCorpus + HasSolutions,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &S::Input) -> u64 {
fn iterations(&self, state: &mut S, _: &I) -> u64 {
1 << (1 + state.rand_mut().below(self.max_stack_pow))
}
/// Get the next mutation to apply
#[inline]
fn schedule(&self, state: &mut S, _: &S::Input) -> usize {
fn schedule(&self, state: &mut S, _: &I) -> usize {
state
.metadata_mut()
.get_mut::<MOpt>()
@ -655,7 +655,7 @@ where
fn scheduled_mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mode = self.mode;

View File

@ -9,7 +9,7 @@ use core::{
use crate::{
bolts::{rands::Rand, tuples::Named},
corpus::Corpus,
inputs::{HasBytesVec, UsesInput},
inputs::HasBytesVec,
mutators::{MutationResult, Mutator},
random_corpus_id,
state::{HasCorpus, HasMaxSize, HasRand},
@ -101,15 +101,15 @@ pub const INTERESTING_32: [i32; 27] = [
#[derive(Default, Debug)]
pub struct BitFlipMutator;
impl<S> Mutator<S> for BitFlipMutator
impl<I, S> Mutator<I, S> for BitFlipMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut <S as UsesInput>::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -141,15 +141,15 @@ impl BitFlipMutator {
#[derive(Default, Debug)]
pub struct ByteFlipMutator;
impl<S> Mutator<S> for ByteFlipMutator
impl<I, S> Mutator<I, S> for ByteFlipMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -179,15 +179,15 @@ impl ByteFlipMutator {
#[derive(Default, Debug)]
pub struct ByteIncMutator;
impl<S> Mutator<S> for ByteIncMutator
impl<I, S> Mutator<I, S> for ByteIncMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -218,15 +218,15 @@ impl ByteIncMutator {
#[derive(Default, Debug)]
pub struct ByteDecMutator;
impl<S> Mutator<S> for ByteDecMutator
impl<I, S> Mutator<I, S> for ByteDecMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -257,15 +257,15 @@ impl ByteDecMutator {
#[derive(Default, Debug)]
pub struct ByteNegMutator;
impl<S> Mutator<S> for ByteNegMutator
impl<I, S> Mutator<I, S> for ByteNegMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -296,15 +296,15 @@ impl ByteNegMutator {
#[derive(Default, Debug)]
pub struct ByteRandMutator;
impl<S> Mutator<S> for ByteRandMutator
impl<I, S> Mutator<I, S> for ByteRandMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().is_empty() {
@ -340,15 +340,15 @@ macro_rules! add_mutator_impl {
pub struct $name;
#[allow(trivial_numeric_casts)]
impl<S> Mutator<S> for $name
impl<I, S> Mutator<I, S> for $name
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().len() < size_of::<$size>() {
@ -406,16 +406,16 @@ macro_rules! interesting_mutator_impl {
#[derive(Default, Debug)]
pub struct $name;
impl<S> Mutator<S> for $name
impl<I, S> Mutator<I, S> for $name
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
#[allow(clippy::cast_sign_loss)]
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
if input.bytes().len() < size_of::<$size>() {
@ -459,15 +459,15 @@ interesting_mutator_impl!(DwordInterestingMutator, u32, INTERESTING_32);
#[derive(Default, Debug)]
pub struct BytesDeleteMutator;
impl<S> Mutator<S> for BytesDeleteMutator
impl<I, S> Mutator<I, S> for BytesDeleteMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -501,15 +501,15 @@ impl BytesDeleteMutator {
#[derive(Default, Debug)]
pub struct BytesExpandMutator;
impl<S> Mutator<S> for BytesExpandMutator
impl<I, S> Mutator<I, S> for BytesExpandMutator
where
S: UsesInput + HasRand + HasMaxSize,
S::Input: HasBytesVec,
S: HasRand + HasMaxSize,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -550,15 +550,15 @@ impl BytesExpandMutator {
#[derive(Default, Debug)]
pub struct BytesInsertMutator;
impl<S> Mutator<S> for BytesInsertMutator
impl<I, S> Mutator<I, S> for BytesInsertMutator
where
S: UsesInput + HasRand + HasMaxSize,
S::Input: HasBytesVec,
S: HasRand + HasMaxSize,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -605,15 +605,15 @@ impl BytesInsertMutator {
#[derive(Default, Debug)]
pub struct BytesRandInsertMutator;
impl<S> Mutator<S> for BytesRandInsertMutator
impl<I, S> Mutator<I, S> for BytesRandInsertMutator
where
S: UsesInput + HasRand + HasMaxSize,
S::Input: HasBytesVec,
S: HasRand + HasMaxSize,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -657,15 +657,15 @@ impl BytesRandInsertMutator {
#[derive(Default, Debug)]
pub struct BytesSetMutator;
impl<S> Mutator<S> for BytesSetMutator
impl<I, S> Mutator<I, S> for BytesSetMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -701,15 +701,15 @@ impl BytesSetMutator {
#[derive(Default, Debug)]
pub struct BytesRandSetMutator;
impl<S> Mutator<S> for BytesRandSetMutator
impl<I, S> Mutator<I, S> for BytesRandSetMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -745,15 +745,15 @@ impl BytesRandSetMutator {
#[derive(Default, Debug)]
pub struct BytesCopyMutator;
impl<S> Mutator<S> for BytesCopyMutator
impl<I, S> Mutator<I, S> for BytesCopyMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -791,15 +791,15 @@ pub struct BytesInsertCopyMutator {
tmp_buf: Vec<u8>,
}
impl<S> Mutator<S> for BytesInsertCopyMutator
impl<I, S> Mutator<I, S> for BytesInsertCopyMutator
where
S: UsesInput + HasRand + HasMaxSize,
S::Input: HasBytesVec,
S: HasRand + HasMaxSize,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -853,15 +853,15 @@ impl BytesInsertCopyMutator {
#[derive(Debug, Default)]
pub struct BytesSwapMutator;
impl<S> Mutator<S> for BytesSwapMutator
impl<I, S> Mutator<I, S> for BytesSwapMutator
where
S: UsesInput + HasRand,
S::Input: HasBytesVec,
S: HasRand,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -899,7 +899,7 @@ impl BytesSwapMutator {
#[derive(Debug, Default)]
pub struct CrossoverInsertMutator;
impl<S> Mutator<S> for CrossoverInsertMutator
impl<S> Mutator<S::Input, S> for CrossoverInsertMutator
where
S: HasCorpus + HasRand + HasMaxSize,
S::Input: HasBytesVec,
@ -974,7 +974,7 @@ impl CrossoverInsertMutator {
#[derive(Debug, Default)]
pub struct CrossoverReplaceMutator;
impl<S> Mutator<S> for CrossoverReplaceMutator
impl<S> Mutator<S::Input, S> for CrossoverReplaceMutator
where
S: HasCorpus + HasRand,
S::Input: HasBytesVec,
@ -1056,7 +1056,7 @@ fn locate_diffs(this: &[u8], other: &[u8]) -> (i64, i64) {
#[derive(Debug, Default)]
pub struct SpliceMutator;
impl<S> Mutator<S> for SpliceMutator
impl<S> Mutator<S::Input, S> for SpliceMutator
where
S: HasCorpus + HasRand,
S::Input: HasBytesVec,
@ -1178,10 +1178,10 @@ mod tests {
state::{HasMetadata, StdState},
};
fn test_mutations<S>() -> impl MutatorsTuple<S>
fn test_mutations<I, S>() -> impl MutatorsTuple<I, S>
where
S: HasRand + HasCorpus + HasMetadata + HasMaxSize,
S::Input: HasBytesVec,
S: HasRand + HasMetadata + HasMaxSize,
I: HasBytesVec,
{
tuple_list!(
BitFlipMutator::new(),

View File

@ -14,7 +14,6 @@ use crate::{
generators::nautilus::NautilusContext,
inputs::nautilus::NautilusInput,
mutators::{MutationResult, Mutator},
prelude::UsesInput,
state::{HasCorpus, HasMetadata},
Error,
};
@ -31,10 +30,7 @@ impl Debug for NautilusRandomMutator<'_> {
}
}
impl<S> Mutator<S> for NautilusRandomMutator<'_>
where
S: UsesInput<Input = NautilusInput>,
{
impl<S> Mutator<NautilusInput, S> for NautilusRandomMutator<'_> {
fn mutate(
&mut self,
_state: &mut S,
@ -95,10 +91,7 @@ impl Debug for NautilusRecursionMutator<'_> {
}
}
impl<S> Mutator<S> for NautilusRecursionMutator<'_>
where
S: UsesInput<Input = NautilusInput>,
{
impl<S> Mutator<NautilusInput, S> for NautilusRecursionMutator<'_> {
fn mutate(
&mut self,
_state: &mut S,
@ -161,9 +154,9 @@ impl Debug for NautilusSpliceMutator<'_> {
}
}
impl<S> Mutator<S> for NautilusSpliceMutator<'_>
impl<S> Mutator<NautilusInput, S> for NautilusSpliceMutator<'_>
where
S: HasCorpus + HasMetadata + UsesInput<Input = NautilusInput>,
S: HasCorpus<Input = NautilusInput> + HasMetadata,
{
fn mutate(
&mut self,

View File

@ -16,9 +16,8 @@ use crate::{
AsMutSlice, AsSlice,
},
corpus::{Corpus, CorpusId},
inputs::UsesInput,
mutators::{MutationResult, Mutator, MutatorsTuple},
state::{HasCorpus, HasMetadata, HasRand, State},
state::{HasCorpus, HasMetadata, HasRand},
Error,
};
@ -55,10 +54,9 @@ impl LogMutationMetadata {
}
/// A [`Mutator`] that composes multiple mutations into one.
pub trait ComposedByMutations<MT, S>
pub trait ComposedByMutations<I, MT, S>
where
MT: MutatorsTuple<S>,
S: UsesInput,
MT: MutatorsTuple<I, S>,
{
/// Get the mutations
fn mutations(&self) -> &MT;
@ -68,23 +66,22 @@ where
}
/// A [`Mutator`] scheduling multiple [`Mutator`]s for an input.
pub trait ScheduledMutator<MT, S>: ComposedByMutations<MT, S> + Mutator<S>
pub trait ScheduledMutator<I, MT, S>: ComposedByMutations<I, MT, S> + Mutator<I, S>
where
MT: MutatorsTuple<S>,
S: UsesInput,
MT: MutatorsTuple<I, S>,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, input: &S::Input) -> u64;
fn iterations(&self, state: &mut S, input: &I) -> u64;
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, input: &S::Input) -> usize;
fn schedule(&self, state: &mut S, input: &I) -> usize;
/// New default implementation for mutate.
/// Implementations must forward mutate() to this method
fn scheduled_mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
@ -103,51 +100,51 @@ where
}
/// A [`Mutator`] that schedules one of the embedded mutations on each call.
pub struct StdScheduledMutator<MT, S>
pub struct StdScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
MT: MutatorsTuple<I, S>,
S: HasRand,
{
mutations: MT,
max_stack_pow: u64,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl<MT, S> Debug for StdScheduledMutator<MT, S>
impl<I, MT, S> Debug for StdScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
MT: MutatorsTuple<I, S>,
S: HasRand,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"StdScheduledMutator with {} mutations for Input type {}",
self.mutations.len(),
core::any::type_name::<S::Input>()
core::any::type_name::<I>()
)
}
}
impl<MT, S> Mutator<S> for StdScheduledMutator<MT, S>
impl<I, MT, S> Mutator<I, S> for StdScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
MT: MutatorsTuple<I, S>,
S: HasRand,
{
#[inline]
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
self.scheduled_mutate(state, input, stage_idx)
}
}
impl<MT, S> ComposedByMutations<MT, S> for StdScheduledMutator<MT, S>
impl<I, MT, S> ComposedByMutations<I, MT, S> for StdScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
MT: MutatorsTuple<I, S>,
S: HasRand,
{
/// Get the mutations
#[inline]
@ -162,27 +159,27 @@ where
}
}
impl<MT, S> ScheduledMutator<MT, S> for StdScheduledMutator<MT, S>
impl<I, MT, S> ScheduledMutator<I, MT, S> for StdScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
MT: MutatorsTuple<I, S>,
S: HasRand,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &S::Input) -> u64 {
fn iterations(&self, state: &mut S, _: &I) -> u64 {
1 << (1 + state.rand_mut().below(self.max_stack_pow))
}
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &S::Input) -> usize {
fn schedule(&self, state: &mut S, _: &I) -> usize {
debug_assert!(!self.mutations().is_empty());
state.rand_mut().below(self.mutations().len() as u64) as usize
}
}
impl<MT, S> StdScheduledMutator<MT, S>
impl<I, MT, S> StdScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
MT: MutatorsTuple<I, S>,
S: HasRand,
{
/// Create a new [`StdScheduledMutator`] instance specifying mutations
pub fn new(mutations: MT) -> Self {
@ -275,43 +272,43 @@ pub fn tokens_mutations() -> tuple_list_type!(TokenInsert, TokenReplace) {
}
/// A logging [`Mutator`] that wraps around a [`StdScheduledMutator`].
pub struct LoggerScheduledMutator<MT, S, SM>
pub struct LoggerScheduledMutator<I, MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: UsesInput + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus,
SM: ScheduledMutator<I, MT, S>,
{
scheduled: SM,
mutation_log: Vec<usize>,
phantom: PhantomData<(MT, S)>,
phantom: PhantomData<(I, MT, S)>,
}
impl<MT, S, SM> Debug for LoggerScheduledMutator<MT, S, SM>
impl<I, MT, S, SM> Debug for LoggerScheduledMutator<I, MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: UsesInput + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus,
SM: ScheduledMutator<I, MT, S>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"LoggerScheduledMutator with {} mutations for Input type {}",
self.scheduled.mutations().len(),
core::any::type_name::<<S as UsesInput>::Input>()
core::any::type_name::<I>()
)
}
}
impl<MT, S, SM> Mutator<S> for LoggerScheduledMutator<MT, S, SM>
impl<I, MT, S, SM> Mutator<I, S> for LoggerScheduledMutator<I, MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus,
SM: ScheduledMutator<I, MT, S>,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut <S as UsesInput>::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
self.scheduled_mutate(state, input, stage_idx)
@ -339,11 +336,11 @@ where
}
}
impl<MT, S, SM> ComposedByMutations<MT, S> for LoggerScheduledMutator<MT, S, SM>
impl<I, MT, S, SM> ComposedByMutations<I, MT, S> for LoggerScheduledMutator<I, MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus,
SM: ScheduledMutator<I, MT, S>,
{
#[inline]
fn mutations(&self) -> &MT {
@ -356,19 +353,19 @@ where
}
}
impl<MT, S, SM> ScheduledMutator<MT, S> for LoggerScheduledMutator<MT, S, SM>
impl<I, MT, S, SM> ScheduledMutator<I, MT, S> for LoggerScheduledMutator<I, MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus,
SM: ScheduledMutator<I, MT, S>,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &<S as UsesInput>::Input) -> u64 {
fn iterations(&self, state: &mut S, _: &I) -> u64 {
1 << (1 + state.rand_mut().below(6))
}
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &<S as UsesInput>::Input) -> usize {
fn schedule(&self, state: &mut S, _: &I) -> usize {
debug_assert!(!self.scheduled.mutations().is_empty());
state
.rand_mut()
@ -378,7 +375,7 @@ where
fn scheduled_mutate(
&mut self,
state: &mut S,
input: &mut <S as UsesInput>::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
let mut r = MutationResult::Skipped;
@ -398,11 +395,11 @@ where
}
}
impl<MT, S, SM> LoggerScheduledMutator<MT, S, SM>
impl<I, MT, S, SM> LoggerScheduledMutator<I, MT, S, SM>
where
MT: MutatorsTuple<S> + NamedTuple,
S: State + HasRand + HasCorpus,
SM: ScheduledMutator<MT, S>,
MT: MutatorsTuple<I, S> + NamedTuple,
S: HasRand + HasCorpus,
SM: ScheduledMutator<I, MT, S>,
{
/// Create a new [`StdScheduledMutator`] instance without mutations and corpus
pub fn new(scheduled: SM) -> Self {
@ -528,14 +525,16 @@ pub mod pybind {
use pyo3::prelude::*;
use super::{havoc_mutations, Debug, HavocMutationsType, StdScheduledMutator};
use crate::{mutators::pybind::PythonMutator, state::pybind::PythonStdState};
use crate::{
inputs::BytesInput, mutators::pybind::PythonMutator, state::pybind::PythonStdState,
};
#[pyclass(unsendable, name = "StdHavocMutator")]
#[derive(Debug)]
/// Python class for StdHavocMutator
pub struct PythonStdHavocMutator {
/// Rust wrapped StdHavocMutator object
pub inner: StdScheduledMutator<HavocMutationsType, PythonStdState>,
pub inner: StdScheduledMutator<BytesInput, HavocMutationsType, PythonStdState>,
}
#[pymethods]

View File

@ -291,15 +291,15 @@ impl<'it> IntoIterator for &'it Tokens {
#[derive(Debug, Default)]
pub struct TokenInsert;
impl<S> Mutator<S> for TokenInsert
impl<I, S> Mutator<I, S> for TokenInsert
where
S: UsesInput + HasMetadata + HasRand + HasMaxSize,
S::Input: HasBytesVec,
S: HasMetadata + HasRand + HasMaxSize,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let max_size = state.max_size();
@ -357,15 +357,15 @@ impl TokenInsert {
#[derive(Debug, Default)]
pub struct TokenReplace;
impl<S> Mutator<S> for TokenReplace
impl<I, S> Mutator<I, S> for TokenReplace
where
S: UsesInput + HasMetadata + HasRand + HasMaxSize,
S::Input: HasBytesVec,
I: HasBytesVec,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();
@ -419,16 +419,16 @@ impl TokenReplace {
#[derive(Debug, Default)]
pub struct I2SRandReplace;
impl<S> Mutator<S> for I2SRandReplace
impl<I, S> Mutator<I, S> for I2SRandReplace
where
S: UsesInput + HasMetadata + HasRand + HasMaxSize,
S::Input: HasBytesVec,
I: HasBytesVec,
{
#[allow(clippy::too_many_lines)]
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
_stage_idx: i32,
) -> Result<MutationResult, Error> {
let size = input.bytes().len();

View File

@ -15,7 +15,7 @@ use crate::{
bolts::rands::Rand,
impl_serdeany,
mutators::{ComposedByMutations, MutationResult, Mutator, MutatorsTuple, ScheduledMutator},
state::{HasMetadata, HasRand, State},
state::{HasMetadata, HasRand},
Error,
};
@ -54,51 +54,51 @@ impl_serdeany!(TuneableScheduledMutatorMetadata);
/// A [`Mutator`] that schedules one of the embedded mutations on each call.
/// The index of the next mutation can be set.
pub struct TuneableScheduledMutator<MT, S>
pub struct TuneableScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
MT: MutatorsTuple<I, S>,
S: HasRand,
{
mutations: MT,
max_stack_pow: u64,
phantom: PhantomData<S>,
phantom: PhantomData<(I, S)>,
}
impl<MT, S> Debug for TuneableScheduledMutator<MT, S>
impl<I, MT, S> Debug for TuneableScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
MT: MutatorsTuple<I, S>,
S: HasRand,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"TuneableScheduledMutator with {} mutations for Input type {}",
self.mutations.len(),
core::any::type_name::<S::Input>()
core::any::type_name::<I>()
)
}
}
impl<MT, S> Mutator<S> for TuneableScheduledMutator<MT, S>
impl<I, MT, S> Mutator<I, S> for TuneableScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand + HasMetadata,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata,
{
#[inline]
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
input: &mut I,
stage_idx: i32,
) -> Result<MutationResult, Error> {
self.scheduled_mutate(state, input, stage_idx)
}
}
impl<MT, S> ComposedByMutations<MT, S> for TuneableScheduledMutator<MT, S>
impl<I, MT, S> ComposedByMutations<I, MT, S> for TuneableScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand,
MT: MutatorsTuple<I, S>,
S: HasRand,
{
/// Get the mutations
#[inline]
@ -113,13 +113,13 @@ where
}
}
impl<MT, S> ScheduledMutator<MT, S> for TuneableScheduledMutator<MT, S>
impl<I, MT, S> ScheduledMutator<I, MT, S> for TuneableScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand + HasMetadata,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata,
{
/// Compute the number of iterations used to apply stacked mutations
fn iterations(&self, state: &mut S, _: &S::Input) -> u64 {
fn iterations(&self, state: &mut S, _: &I) -> u64 {
if let Some(iters) = Self::get_iters(state) {
iters
} else {
@ -129,7 +129,7 @@ where
}
/// Get the next mutation to apply
fn schedule(&self, state: &mut S, _: &S::Input) -> usize {
fn schedule(&self, state: &mut S, _: &I) -> usize {
debug_assert!(!self.mutations().is_empty());
// Assumption: we can not reach this code path without previously adding this metadatum.
let metadata = TuneableScheduledMutatorMetadata::get_mut(state).unwrap();
@ -152,10 +152,10 @@ where
}
}
impl<MT, S> TuneableScheduledMutator<MT, S>
impl<I, MT, S> TuneableScheduledMutator<I, MT, S>
where
MT: MutatorsTuple<S>,
S: State + HasRand + HasMetadata,
MT: MutatorsTuple<I, S>,
S: HasRand + HasMetadata,
{
/// Create a new [`TuneableScheduledMutator`] instance specifying mutations
pub fn new(state: &mut S, mutations: MT) -> Self {
@ -222,7 +222,7 @@ mod test {
#[test]
fn test_tuning() {
let mut state = NopState::new();
let mut state: NopState<BytesInput> = NopState::new();
let mutators = tuple_list!(
BitFlipMutator::new(),
ByteDecMutator::new(),

View File

@ -16,7 +16,7 @@ use crate::{
corpus::{Corpus, CorpusId},
executors::{Executor, HasObservers},
feedbacks::map::MapNoveltiesMetadata,
inputs::{GeneralizedInput, GeneralizedItem, HasBytesVec, UsesInput},
inputs::{BytesInput, GeneralizedInputMetadata, GeneralizedItem, HasBytesVec, UsesInput},
mark_feature_time,
observers::{MapObserver, ObserversTuple},
stages::Stage,
@ -69,7 +69,7 @@ pub struct GeneralizationStage<EM, O, OT, Z> {
impl<EM, O, OT, Z> UsesState for GeneralizationStage<EM, O, OT, Z>
where
EM: UsesState,
EM::State: UsesInput<Input = GeneralizedInput>,
EM::State: UsesInput<Input = BytesInput>,
{
type State = EM::State;
}
@ -79,7 +79,7 @@ where
O: MapObserver,
E: Executor<EM, Z> + HasObservers,
E::Observers: ObserversTuple<E::State>,
E::State: UsesInput<Input = GeneralizedInput>
E::State: UsesInput<Input = BytesInput>
+ HasClientPerfMonitor
+ HasExecutions
+ HasMetadata
@ -110,9 +110,8 @@ where
state.corpus().get(corpus_idx)?.borrow_mut().load_input()?;
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
let mut entry = state.corpus().get(corpus_idx)?.borrow_mut();
let input = entry.input_mut().as_mut().unwrap();
if input.generalized().is_some() {
if entry.metadata().contains::<GeneralizedInputMetadata>() {
drop(entry);
state
.metadata_mut()
@ -123,6 +122,8 @@ where
return Ok(());
}
let input = entry.input_mut().as_mut().unwrap();
let payload: Vec<_> = input.bytes().iter().map(|&x| Some(x)).collect();
let original = input.clone();
let meta = entry.metadata().get::<MapNoveltiesMetadata>().ok_or_else(|| {
@ -324,23 +325,13 @@ where
if payload.len() <= MAX_GENERALIZED_LEN {
// Save the modified input in the corpus
{
let mut entry = state.corpus().get(corpus_idx)?.borrow_mut();
entry.load_input()?;
entry
.input_mut()
.as_mut()
.unwrap()
.generalized_from_options(&payload);
entry.store_input()?;
let meta = GeneralizedInputMetadata::generalized_from_options(&payload);
debug_assert!(
entry.load_input()?.generalized().unwrap().first()
== Some(&GeneralizedItem::Gap)
);
debug_assert!(
entry.load_input()?.generalized().unwrap().last()
== Some(&GeneralizedItem::Gap)
);
debug_assert!(meta.generalized().first() == Some(&GeneralizedItem::Gap));
debug_assert!(meta.generalized().last() == Some(&GeneralizedItem::Gap));
let mut entry = state.corpus().get(corpus_idx)?.borrow_mut();
entry.metadata_mut().insert(meta);
}
state
@ -360,7 +351,7 @@ where
EM: UsesState,
O: MapObserver,
OT: ObserversTuple<EM::State>,
EM::State: UsesInput<Input = GeneralizedInput>
EM::State: UsesInput<Input = BytesInput>
+ HasClientPerfMonitor
+ HasExecutions
+ HasMetadata
@ -391,7 +382,7 @@ where
state: &mut EM::State,
manager: &mut EM,
novelties: &[usize],
input: &GeneralizedInput,
input: &BytesInput,
) -> Result<bool, Error>
where
E: Executor<EM, Z> + HasObservers<Observers = OT, State = EM::State>,
@ -449,7 +440,7 @@ where
if end > payload.len() {
end = payload.len();
}
let mut candidate = GeneralizedInput::new(vec![]);
let mut candidate = BytesInput::new(vec![]);
candidate
.bytes_mut()
.extend(payload[..start].iter().flatten());
@ -502,7 +493,7 @@ where
while end > start {
if payload[end] == Some(closing_char) {
endings += 1;
let mut candidate = GeneralizedInput::new(vec![]);
let mut candidate = BytesInput::new(vec![]);
candidate
.bytes_mut()
.extend(payload[..start].iter().flatten());

View File

@ -7,8 +7,9 @@ use core::marker::PhantomData;
use crate::monitors::PerfFeature;
use crate::{
bolts::rands::Rand,
corpus::{Corpus, CorpusId},
corpus::{Corpus, CorpusId, Testcase},
fuzzer::Evaluator,
inputs::Input,
mark_feature_time,
mutators::Mutator,
stages::Stage,
@ -19,16 +20,79 @@ use crate::{
// TODO multi mutators stage
/// Action performed after the un-transformed input is executed (e.g., updating metadata)
#[allow(unused_variables)]
pub trait MutatedTransformPost<S>: Sized {
/// Perform any post-execution steps necessary for the transformed input (e.g., updating metadata)
#[inline]
fn post_exec(
self,
state: &mut S,
stage_idx: i32,
corpus_idx: Option<CorpusId>,
) -> Result<(), Error> {
Ok(())
}
}
impl<S> MutatedTransformPost<S> for () {}
/// A type which may both be transformed from and into a given input type, used to perform
/// mutations over inputs which are not necessarily performable on the underlying type
///
/// This trait is implemented such that all testcases inherently transform to their inputs, should
/// the input be cloneable.
pub trait MutatedTransform<I, S>: Sized
where
I: Input,
{
/// Type indicating actions to be taken after the post-transformation input is executed
type Post: MutatedTransformPost<S>;
/// Transform the provided testcase into this type
fn try_transform_from(
base: &Testcase<I>,
state: &S,
corpus_idx: CorpusId,
) -> Result<Self, Error>;
/// Transform this instance back into the original input type
fn try_transform_into(self, state: &S) -> Result<(I, Self::Post), Error>;
}
// reflexive definition
impl<I, S> MutatedTransform<I, S> for I
where
I: Input + Clone,
{
type Post = ();
#[inline]
fn try_transform_from(
base: &Testcase<I>,
_state: &S,
_corpus_idx: CorpusId,
) -> Result<Self, Error> {
Ok(base.input().as_ref().unwrap().clone())
}
#[inline]
fn try_transform_into(self, _state: &S) -> Result<(I, Self::Post), Error> {
Ok((self, ()))
}
}
/// A Mutational stage is the stage in a fuzzing run that mutates inputs.
/// Mutational stages will usually have a range of mutations that are
/// being applied to the input one by one, between executions.
pub trait MutationalStage<E, EM, M, Z>: Stage<E, EM, Z>
pub trait MutationalStage<E, EM, I, M, Z>: Stage<E, EM, Z>
where
E: UsesState<State = Self::State>,
M: Mutator<Self::State>,
M: Mutator<I, Self::State>,
EM: UsesState<State = Self::State>,
Z: Evaluator<E, EM, State = Self::State>,
Self::State: HasClientPerfMonitor + HasCorpus,
I: MutatedTransform<Self::Input, Self::State> + Clone,
{
/// The mutator registered for this stage
fn mutator(&self) -> &M;
@ -51,25 +115,26 @@ where
) -> Result<(), Error> {
let num = self.iterations(state, corpus_idx)?;
for i in 0..num {
start_timer!(state);
let mut input = state
.corpus()
.get(corpus_idx)?
.borrow_mut()
.load_input()?
.clone();
let testcase = state.corpus().get(corpus_idx)?.borrow();
let input = I::try_transform_from(&testcase, state, corpus_idx)?;
drop(testcase);
mark_feature_time!(state, PerfFeature::GetInputFromCorpus);
for i in 0..num {
let mut input = input.clone();
start_timer!(state);
self.mutator_mut().mutate(state, &mut input, i as i32)?;
mark_feature_time!(state, PerfFeature::Mutate);
// Time is measured directly the `evaluate_input` function
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input)?;
let (untransformed, post) = input.try_transform_into(state)?;
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, untransformed)?;
start_timer!(state);
self.mutator_mut().post_exec(state, i as i32, corpus_idx)?;
post.post_exec(state, i as i32, corpus_idx)?;
mark_feature_time!(state, PerfFeature::MutatePostExec);
}
Ok(())
@ -82,19 +147,20 @@ pub static DEFAULT_MUTATIONAL_MAX_ITERATIONS: u64 = 128;
/// The default mutational stage
#[derive(Clone, Debug)]
pub struct StdMutationalStage<E, EM, M, Z> {
pub struct StdMutationalStage<E, EM, I, M, Z> {
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, Z)>,
phantom: PhantomData<(E, EM, I, Z)>,
}
impl<E, EM, M, Z> MutationalStage<E, EM, M, Z> for StdMutationalStage<E, EM, M, Z>
impl<E, EM, I, M, Z> MutationalStage<E, EM, I, M, Z> for StdMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
M: Mutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
I: MutatedTransform<Self::Input, Self::State> + Clone,
{
/// The mutator, added to this stage
#[inline]
@ -114,24 +180,25 @@ where
}
}
impl<E, EM, M, Z> UsesState for StdMutationalStage<E, EM, M, Z>
impl<E, EM, I, M, Z> UsesState for StdMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
M: Mutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
type State = Z::State;
}
impl<E, EM, M, Z> Stage<E, EM, Z> for StdMutationalStage<E, EM, M, Z>
impl<E, EM, I, M, Z> Stage<E, EM, Z> for StdMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
M: Mutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
I: MutatedTransform<Self::Input, Self::State> + Clone,
{
#[inline]
#[allow(clippy::let_and_return)]
@ -152,16 +219,30 @@ where
}
}
impl<E, EM, M, Z> StdMutationalStage<E, EM, M, Z>
impl<E, EM, M, Z> StdMutationalStage<E, EM, Z::Input, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
M: Mutator<Z::Input, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
/// Creates a new default mutational stage
pub fn new(mutator: M) -> Self {
Self::transforming(mutator)
}
}
impl<E, EM, I, M, Z> StdMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
/// Creates a new transforming mutational stage
pub fn transforming(mutator: M) -> Self {
Self {
mutator,
phantom: PhantomData,
@ -179,6 +260,7 @@ pub mod pybind {
events::pybind::PythonEventManager,
executors::pybind::PythonExecutor,
fuzzer::pybind::PythonStdFuzzer,
inputs::BytesInput,
mutators::pybind::PythonMutator,
stages::{pybind::PythonStage, StdMutationalStage},
};
@ -188,8 +270,13 @@ pub mod pybind {
/// Python class for StdMutationalStage
pub struct PythonStdMutationalStage {
/// Rust wrapped StdMutationalStage object
pub inner:
StdMutationalStage<PythonExecutor, PythonEventManager, PythonMutator, PythonStdFuzzer>,
pub inner: StdMutationalStage<
PythonExecutor,
PythonEventManager,
BytesInput,
PythonMutator,
PythonStdFuzzer,
>,
}
#[pymethods]

View File

@ -13,36 +13,41 @@ use crate::{
schedulers::{
powersched::SchedulerMetadata, testcase_score::CorpusPowerTestcaseScore, TestcaseScore,
},
stages::{MutationalStage, Stage},
stages::{
mutational::{MutatedTransform, MutatedTransformPost},
MutationalStage, Stage,
},
state::{HasClientPerfMonitor, HasCorpus, HasMetadata, HasRand, UsesState},
Error,
};
/// The mutational stage using power schedules
#[derive(Clone, Debug)]
pub struct PowerMutationalStage<E, F, EM, M, O, Z> {
pub struct PowerMutationalStage<E, F, EM, I, M, O, Z> {
map_observer_name: String,
mutator: M,
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, F, EM, O, Z)>,
phantom: PhantomData<(E, F, EM, I, O, Z)>,
}
impl<E, F, EM, M, O, Z> UsesState for PowerMutationalStage<E, F, EM, M, O, Z>
impl<E, F, EM, I, M, O, Z> UsesState for PowerMutationalStage<E, F, EM, I, M, O, Z>
where
E: UsesState,
{
type State = E::State;
}
impl<E, F, EM, M, O, Z> MutationalStage<E, EM, M, Z> for PowerMutationalStage<E, F, EM, M, O, Z>
impl<E, F, EM, I, M, O, Z> MutationalStage<E, EM, I, M, Z>
for PowerMutationalStage<E, F, EM, I, M, O, Z>
where
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = E::State>,
F: TestcaseScore<E::State>,
M: Mutator<E::State>,
M: Mutator<I, E::State>,
O: MapObserver,
E::State: HasClientPerfMonitor + HasCorpus + HasMetadata + HasRand,
Z: Evaluator<E, EM, State = E::State>,
I: MutatedTransform<E::Input, E::State> + Clone,
{
/// The mutator, added to this stage
#[inline]
@ -77,17 +82,17 @@ where
) -> Result<(), Error> {
let num = self.iterations(state, corpus_idx)?;
let testcase = state.corpus().get(corpus_idx)?.borrow();
let input = I::try_transform_from(&testcase, state, corpus_idx)?;
drop(testcase);
for i in 0..num {
let mut input = state
.corpus()
.get(corpus_idx)?
.borrow_mut()
.load_input()?
.clone();
let mut input = input.clone();
self.mutator_mut().mutate(state, &mut input, i as i32)?;
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, input)?;
let (untransformed, post) = input.try_transform_into(state)?;
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, untransformed)?;
let observer = executor
.observers()
@ -119,21 +124,23 @@ where
}
self.mutator_mut().post_exec(state, i as i32, corpus_idx)?;
post.post_exec(state, i as i32, corpus_idx)?;
}
Ok(())
}
}
impl<E, F, EM, M, O, Z> Stage<E, EM, Z> for PowerMutationalStage<E, F, EM, M, O, Z>
impl<E, F, EM, I, M, O, Z> Stage<E, EM, Z> for PowerMutationalStage<E, F, EM, I, M, O, Z>
where
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = E::State>,
F: TestcaseScore<E::State>,
M: Mutator<E::State>,
M: Mutator<I, E::State>,
O: MapObserver,
E::State: HasClientPerfMonitor + HasCorpus + HasMetadata + HasRand,
Z: Evaluator<E, EM, State = E::State>,
I: MutatedTransform<E::Input, E::State> + Clone,
{
#[inline]
#[allow(clippy::let_and_return)]
@ -150,18 +157,34 @@ where
}
}
impl<E, F, EM, M, O, Z> PowerMutationalStage<E, F, EM, M, O, Z>
impl<E, F, EM, M, O, Z> PowerMutationalStage<E, F, EM, E::Input, M, O, Z>
where
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = E::State>,
F: TestcaseScore<E::State>,
M: Mutator<E::State>,
M: Mutator<E::Input, E::State>,
O: MapObserver,
E::State: HasClientPerfMonitor + HasCorpus + HasMetadata + HasRand,
Z: Evaluator<E, EM, State = E::State>,
{
/// Creates a new [`PowerMutationalStage`]
pub fn new(mutator: M, map_observer_name: &O) -> Self {
Self::transforming(mutator, map_observer_name)
}
}
impl<E, F, EM, I, M, O, Z> PowerMutationalStage<E, F, EM, I, M, O, Z>
where
E: Executor<EM, Z> + HasObservers,
EM: UsesState<State = E::State>,
F: TestcaseScore<E::State>,
M: Mutator<I, E::State>,
O: MapObserver,
E::State: HasClientPerfMonitor + HasCorpus + HasMetadata + HasRand,
Z: Evaluator<E, EM, State = E::State>,
{
/// Creates a new transforming [`PowerMutationalStage`]
pub fn transforming(mutator: M, map_observer_name: &O) -> Self {
Self {
map_observer_name: map_observer_name.name().to_string(),
mutator,
@ -171,5 +194,5 @@ where
}
/// The standard powerscheduling stage
pub type StdPowerMutationalStage<E, EM, M, O, Z> =
PowerMutationalStage<E, CorpusPowerTestcaseScore<<E as UsesState>::State>, EM, M, O, Z>;
pub type StdPowerMutationalStage<E, EM, I, M, O, Z> =
PowerMutationalStage<E, CorpusPowerTestcaseScore<<E as UsesState>::State>, EM, I, M, O, Z>;

View File

@ -41,7 +41,7 @@ pub struct StdMutationalPushStage<CS, EM, M, OT, Z>
where
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
M: Mutator<CS::State>,
M: Mutator<CS::Input, CS::State>,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasRand + Clone + Debug,
Z: ExecutionProcessor<OT, State = CS::State>
@ -63,7 +63,7 @@ impl<CS, EM, M, OT, Z> StdMutationalPushStage<CS, EM, M, OT, Z>
where
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
M: Mutator<CS::State>,
M: Mutator<CS::Input, CS::State>,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasCorpus + HasRand + Clone + Debug,
Z: ExecutionProcessor<OT, State = CS::State>
@ -86,7 +86,7 @@ impl<CS, EM, M, OT, Z> PushStage<CS, EM, OT, Z> for StdMutationalPushStage<CS, E
where
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId + ProgressReporter,
M: Mutator<CS::State>,
M: Mutator<CS::Input, CS::State>,
OT: ObserversTuple<CS::State>,
CS::State:
HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata + Clone + Debug,
@ -199,7 +199,7 @@ impl<CS, EM, M, OT, Z> Iterator for StdMutationalPushStage<CS, EM, M, OT, Z>
where
CS: Scheduler,
EM: EventFirer + EventRestarter + HasEventManagerId + ProgressReporter<State = CS::State>,
M: Mutator<CS::State>,
M: Mutator<CS::Input, CS::State>,
OT: ObserversTuple<CS::State>,
CS::State:
HasClientPerfMonitor + HasCorpus + HasRand + HasExecutions + HasMetadata + Clone + Debug,
@ -218,7 +218,7 @@ impl<CS, EM, M, OT, Z> StdMutationalPushStage<CS, EM, M, OT, Z>
where
CS: Scheduler,
EM: EventFirer<State = CS::State> + EventRestarter + HasEventManagerId,
M: Mutator<CS::State>,
M: Mutator<CS::Input, CS::State>,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasCorpus + HasRand + Clone + Debug,
Z: ExecutionProcessor<OT, State = CS::State>

View File

@ -41,7 +41,7 @@ where
EM: EventFirer<State = Self::State>,
F1: Feedback<Self::State>,
F2: Feedback<Self::State>,
M: Mutator<Self::State>,
M: Mutator<Self::Input, Self::State>,
OT: ObserversTuple<CS::State>,
Z: ExecutionProcessor<OT, State = Self::State>
+ ExecutesInput<E, EM>
@ -177,7 +177,7 @@ impl<CS, E, EM, F1, F2, FF, M, OT, Z> UsesState
for StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
CS: Scheduler,
M: Mutator<CS::State>,
M: Mutator<CS::Input, CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>,
{
type State = CS::State;
@ -194,7 +194,7 @@ where
F1: Feedback<CS::State>,
F2: Feedback<CS::State>,
FF: FeedbackFactory<F2, CS::State, OT>,
M: Mutator<CS::State>,
M: Mutator<CS::Input, CS::State>,
OT: ObserversTuple<CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>
+ ExecutesInput<E, EM>
@ -241,7 +241,7 @@ where
F2: Feedback<CS::State>,
FF: FeedbackFactory<F2, CS::State, OT>,
<CS::State as UsesInput>::Input: HasLen + Hash,
M: Mutator<CS::State>,
M: Mutator<CS::Input, CS::State>,
OT: ObserversTuple<CS::State>,
CS::State: HasClientPerfMonitor + HasCorpus + HasExecutions + HasMaxSize,
Z: ExecutionProcessor<OT, State = CS::State>
@ -270,7 +270,7 @@ where
impl<CS, E, EM, F1, F2, FF, M, OT, Z> StdTMinMutationalStage<CS, E, EM, F1, F2, FF, M, OT, Z>
where
CS: Scheduler,
M: Mutator<CS::State>,
M: Mutator<CS::Input, CS::State>,
Z: ExecutionProcessor<OT, State = CS::State>,
{
/// Creates a new minimising mutational stage that will minimize provided corpus entries

View File

@ -9,7 +9,10 @@ use crate::{
corpus::CorpusId,
impl_serdeany,
mutators::Mutator,
stages::{mutational::DEFAULT_MUTATIONAL_MAX_ITERATIONS, MutationalStage, Stage},
stages::{
mutational::{MutatedTransform, DEFAULT_MUTATIONAL_MAX_ITERATIONS},
MutationalStage, Stage,
},
state::{HasClientPerfMonitor, HasCorpus, HasMetadata, HasRand, UsesState},
Error, Evaluator,
};
@ -52,18 +55,19 @@ pub fn reset<S: HasMetadata>(state: &mut S) -> Result<(), Error> {
/// A [`crate::stages::MutationalStage`] where the mutator iteration can be tuned at runtime
#[derive(Clone, Debug)]
pub struct TuneableMutationalStage<E, EM, M, Z> {
pub struct TuneableMutationalStage<E, EM, I, M, Z> {
mutator: M,
phantom: PhantomData<(E, EM, Z)>,
phantom: PhantomData<(E, EM, I, Z)>,
}
impl<E, EM, M, Z> MutationalStage<E, EM, M, Z> for TuneableMutationalStage<E, EM, M, Z>
impl<E, EM, I, M, Z> MutationalStage<E, EM, I, M, Z> for TuneableMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
M: Mutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
I: MutatedTransform<Z::Input, Z::State> + Clone,
{
/// The mutator, added to this stage
#[inline]
@ -89,24 +93,26 @@ where
}
}
impl<E, EM, M, Z> UsesState for TuneableMutationalStage<E, EM, M, Z>
impl<E, EM, I, M, Z> UsesState for TuneableMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
M: Mutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
I: MutatedTransform<Z::Input, Z::State> + Clone,
{
type State = Z::State;
}
impl<E, EM, M, Z> Stage<E, EM, Z> for TuneableMutationalStage<E, EM, M, Z>
impl<E, EM, I, M, Z> Stage<E, EM, Z> for TuneableMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
M: Mutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
I: MutatedTransform<Z::Input, Z::State> + Clone,
{
#[inline]
#[allow(clippy::let_and_return)]
@ -127,17 +133,32 @@ where
}
}
impl<E, EM, M, Z> TuneableMutationalStage<E, EM, M, Z>
impl<E, EM, M, Z> TuneableMutationalStage<E, EM, Z::Input, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<Z::State>,
M: Mutator<Z::Input, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
{
/// Creates a new default mutational stage
#[must_use]
pub fn new(state: &mut Z::State, mutator: M) -> Self {
Self::transforming(state, mutator)
}
}
impl<E, EM, I, M, Z> TuneableMutationalStage<E, EM, I, M, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
M: Mutator<I, Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
{
/// Creates a new tranforming mutational stage
#[must_use]
pub fn transforming(state: &mut Z::State, mutator: M) -> Self {
if !state.has_metadata::<TuneableMutationalStageMetadata>() {
state.add_metadata(TuneableMutationalStageMetadata::default());
}