use std::any::Any;
use std::sync::atomic::Ordering;
use libafl::bolts::rands::Rand;
use libafl::state::HasRand;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use super::afl::{
BitFlipMutator, ByteAddMutator, ByteDecMutator, ByteFlipMutator, ByteIncMutator,
ByteInterestingMutator, ByteNegMutator, ByteRandMutator, BytesCopyMutator, BytesDeleteMutator,
BytesExpandMutator, BytesInsertCopyMutator, BytesInsertMutator, BytesRandInsertMutator,
BytesRandSetMutator, BytesSetMutator, BytesSwapMutator, CrossoverInsertMutator,
CrossoverReplaceMutator, DwordAddMutator, DwordInterestingMutator, LibAflMutator,
QwordAddMutator, WordAddMutator, WordInterestingMutator,
};
use super::Mutator;
use crate::input::Data;
use crate::metadata::AsAny;
use crate::state::SharedState;
use crate::std_ext::tuple::Named;
use crate::{atomic_load, error::FeroxFuzzError};
use tracing::error;
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct HavocMutator {
corpus_name: String,
max_power_of_two: u64,
mutators: Vec<LibAflMutator>,
}
impl HavocMutator {
#[must_use]
pub fn new(corpus_name: &str) -> Self {
let mutators = vec![
LibAflMutator::BitFlipMutator(BitFlipMutator::new()),
LibAflMutator::ByteFlipMutator(ByteFlipMutator::new()),
LibAflMutator::ByteIncMutator(ByteIncMutator::new()),
LibAflMutator::ByteDecMutator(ByteDecMutator::new()),
LibAflMutator::ByteNegMutator(ByteNegMutator::new()),
LibAflMutator::ByteRandMutator(ByteRandMutator::new()),
LibAflMutator::ByteAddMutator(ByteAddMutator::new()),
LibAflMutator::WordAddMutator(WordAddMutator::new()),
LibAflMutator::DwordAddMutator(DwordAddMutator::new()),
LibAflMutator::QwordAddMutator(QwordAddMutator::new()),
LibAflMutator::ByteInterestingMutator(ByteInterestingMutator::new()),
LibAflMutator::WordInterestingMutator(WordInterestingMutator::new()),
LibAflMutator::DwordInterestingMutator(DwordInterestingMutator::new()),
LibAflMutator::BytesDeleteMutator(BytesDeleteMutator::new()),
LibAflMutator::BytesDeleteMutator(BytesDeleteMutator::new()),
LibAflMutator::BytesDeleteMutator(BytesDeleteMutator::new()),
LibAflMutator::BytesDeleteMutator(BytesDeleteMutator::new()),
LibAflMutator::BytesExpandMutator(BytesExpandMutator::new()),
LibAflMutator::BytesInsertMutator(BytesInsertMutator::new()),
LibAflMutator::BytesRandInsertMutator(BytesRandInsertMutator::new()),
LibAflMutator::BytesSetMutator(BytesSetMutator::new()),
LibAflMutator::BytesRandSetMutator(BytesRandSetMutator::new()),
LibAflMutator::BytesCopyMutator(BytesCopyMutator::new()),
LibAflMutator::BytesInsertCopyMutator(BytesInsertCopyMutator::new()),
LibAflMutator::BytesSwapMutator(BytesSwapMutator::new()),
LibAflMutator::CrossoverInsertMutator(CrossoverInsertMutator::new(corpus_name)),
LibAflMutator::CrossoverReplaceMutator(CrossoverReplaceMutator::new(corpus_name)),
];
Self {
corpus_name: corpus_name.to_string(),
max_power_of_two: 6,
mutators,
}
}
fn iterations(&self, state: &mut SharedState) -> u64 {
1 << (1 + state.rand_mut().below(self.max_power_of_two))
}
}
impl Mutator for HavocMutator {
fn mutate(&mut self, input: &mut Data, state: &mut SharedState) -> Result<(), FeroxFuzzError> {
let corpus = state.corpus_by_name(&self.corpus_name)?;
let scheduled_idx = state.corpus_index_by_name(&self.corpus_name)?;
let index = atomic_load!(scheduled_idx);
if let Ok(guard) = corpus.read() {
let result = guard.get(index);
if result.is_none() {
error!(name=guard.name(), %index, "corpus entry not found");
return Err(FeroxFuzzError::CorpusEntryNotFound {
name: guard.name().to_string(),
index,
});
}
*input = result.unwrap().clone();
if !input.is_fuzzable() {
input.toggle_type();
}
}
for _ in 0..self.iterations(state) {
let upper_bound = self.mutators.len() as u64 + 1;
#[allow(clippy::cast_possible_truncation)]
let mutator_idx = state.rand_mut().below(upper_bound) as usize;
for mutator in self.mutators[..mutator_idx].iter_mut().rev() {
mutator.mutate(input, state)?;
}
}
Ok(())
}
}
impl Named for HavocMutator {
fn name(&self) -> &str {
"HavocMutator"
}
}
impl AsAny for HavocMutator {
fn as_any(&self) -> &dyn Any {
self
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::corpora::RangeCorpus;
use std::collections::HashMap;
#[test]
fn test_havoc_mutator_new() {
let mutator = HavocMutator::new("corpus");
assert_eq!(mutator.mutators.len(), 27);
}
#[test]
fn test_havoc_mutator_max_power_of_two() {
let mutator = HavocMutator::new("corpus");
assert_eq!(mutator.max_power_of_two, 6);
}
#[test]
fn test_smoke_test_for_ser_de() {
let mutator = HavocMutator::new("corpus");
let serialized = serde_json::to_string(&mutator).unwrap();
let _deserialized: HavocMutator = serde_json::from_str(&serialized).unwrap();
}
#[test]
fn test_havoc_mutator_iterations() {
let mutator = HavocMutator::new("corpus");
let corpus = RangeCorpus::with_stop(10).name("corpus").build().unwrap();
let mut state = SharedState::with_corpus(corpus);
let mut counter = HashMap::new();
(0..200).for_each(|_| {
let iterations = mutator.iterations(&mut state);
*counter.entry(iterations).or_insert(0) += 1;
});
let expected = [2, 4, 8, 16, 32, 64];
(0..129).for_each(|i| {
if expected.contains(&i) {
assert!(counter.get(&i).is_some());
} else {
assert!(counter.get(&i).is_none());
}
});
}
}