From 4dabfc5f00cf456c0c3b54199b006d82e633ffce Mon Sep 17 00:00:00 2001 From: Alwin Berger Date: Tue, 7 Jun 2022 01:50:21 +0200 Subject: [PATCH] Add InterruptShifterMutator Also add features fuzz_interrupt and muta_interrupt, which dictate if interrupts are used during fuzzing Also allow makefile to skip finished iterations --- fuzzers/wcet_qemu_sys/Cargo.toml | 3 + fuzzers/wcet_qemu_sys/Makefile | 45 ++++--- fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs | 26 ++-- fuzzers/wcet_qemu_sys/src/bin/showmap.rs | 23 ++-- fuzzers/wcet_qemu_sys/src/sysstate/graph.rs | 12 +- fuzzers/wcet_qemu_sys/src/sysstate/mod.rs | 7 +- .../wcet_qemu_sys/src/sysstate/mutators.rs | 123 ++++++++++++++++++ .../wcet_qemu_sys/src/sysstate/observers.rs | 4 +- 8 files changed, 196 insertions(+), 47 deletions(-) create mode 100644 fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs diff --git a/fuzzers/wcet_qemu_sys/Cargo.toml b/fuzzers/wcet_qemu_sys/Cargo.toml index 6dd9dfd9ff..11875ac0da 100644 --- a/fuzzers/wcet_qemu_sys/Cargo.toml +++ b/fuzzers/wcet_qemu_sys/Cargo.toml @@ -9,6 +9,8 @@ default = ["std", "obj_collect"] std = [] multicore = [] +fuzz_interrupt = [] # use the first bytes of the input for a timed interrupts + # select which feedbacks to use. enable at least one. feed_known_edges = [] feed_afl = [] @@ -31,6 +33,7 @@ obj_ticks = [] muta_input = [ "sched_graph" ] muta_snip = [ "sched_graph" ] muta_suffix = [ "sched_graph" ] +muta_interrupt = [] benchmark = [] # don't save corpus to disk, easy parallelizable dump_infos = [] # dump select corpus items for analysis diff --git a/fuzzers/wcet_qemu_sys/Makefile b/fuzzers/wcet_qemu_sys/Makefile index d119027b56..dace03ae5d 100644 --- a/fuzzers/wcet_qemu_sys/Makefile +++ b/fuzzers/wcet_qemu_sys/Makefile @@ -2,46 +2,54 @@ BENCHDIR = target/bench TARGET = tmr.axf TARGET_TRACE = $(BENCHDIR)/traces/tmr_worst.ron TARGET_EDGES = $(BENCHDIR)/edges/tmr_worst.ron -RUNTIME = 1 +RUNTIME = 3600 +COMMON_FLAGS = benchmark,dump_infos,fuzz_interrupt $(BENCHDIR)/bin: mkdir -p $@ $(BENCHDIR)/target_known_edges: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_known_edges,sched_queue + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_known_edges,sched_queue $(BENCHDIR)/target_afl_queue: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_afl,sched_queue + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_afl,sched_queue $(BENCHDIR)/target_afl_mapmax: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_afl,sched_mapmax + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_afl,sched_mapmax $(BENCHDIR)/target_state: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_state,sched_state + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,sched_state $(BENCHDIR)/target_state_afl: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_state,sched_state + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,sched_state + +$(BENCHDIR)/target_state_afl_int: + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_state,sched_state,muta_interrupt $(BENCHDIR)/target_graph: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph $(BENCHDIR)/target_graph_muta: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph,muta_snip,muta_input,muta_suffix + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,muta_snip,muta_input,muta_suffix $(BENCHDIR)/target_graph_afl: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph,feed_afl + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,feed_afl + +$(BENCHDIR)/target_graph_muta_afl: + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix $(BENCHDIR)/target_graph_all: - cargo build --bin fuzzer --target-dir $@ --features benchmark,dump_infos,feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix + cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_graph,sched_graph,feed_afl,muta_snip,muta_input,muta_suffix,muta_interrupt -binaries: $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_afl_queue $(BENCHDIR)/target_afl_mapmax $(BENCHDIR)/target_state $(BENCHDIR)/target_state_afl \ - $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_muta $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_all +binaries: $(BENCHDIR)/target_known_edges $(BENCHDIR)/target_afl_queue $(BENCHDIR)/target_afl_mapmax $(BENCHDIR)/target_state $(BENCHDIR)/target_state_afl $(BENCHDIR)/target_state_afl_int \ + $(BENCHDIR)/target_graph $(BENCHDIR)/target_graph_muta $(BENCHDIR)/target_graph_afl $(BENCHDIR)/target_graph_muta_afl $(BENCHDIR)/target_graph_all # variants: known_edges, afl_queue, afl_mapmax, state, state_afl, graph, graph_muta, graph_afl, graph_all $(BENCHDIR)/bench_%.log: $(BENCHDIR)/target_% $(TARGET_TRACE) mkdir -p $(BENCHDIR)/execs - for i in {1..3}; do \ + for i in {1..10}; do \ CASE=$$(basename -s.log $@ | cut -d'_' -f 2- ); \ + [ -f $(BENCHDIR)/execs/$$CASE\_$$i.exec -a -f $@_$$i ] && continue; \ echo $$CASE iteration $$i; \ mkdir -p $(BENCHDIR)/infos/$$CASE ; \ ./fuzzer_bench.sh $> $@; done -benchmarks: target/bench/bench_known_edges.log target/bench/bench_afl_queue.log target/bench/bench_afl_mapmax.log target/bench/bench_state.log target/bench/bench_state_afl.log \ - target/bench/bench_graph.log target/bench/bench_graph_muta.log target/bench/bench_graph_afl.log target/bench/bench_graph_all.log +benchmarks_all: target/bench/bench_known_edges.log target/bench/bench_afl_queue.log target/bench/bench_afl_mapmax.log target/bench/bench_state.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log \ + target/bench/bench_graph.log target/bench/bench_graph_muta.log target/bench/bench_graph_afl.log target/bench/bench_graph_muta_afl.log target/bench/bench_graph_all.log -all: binaries benchmarks +benchmarks_int_mut: target/bench/bench_graph_muta_afl.log target/bench/bench_graph_all.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log + +all: binaries benchmarks_all clean_bench: rm -rf $(BENCHDIR)/bench_* +clean_aggregate: + rm -rf $(BENCHDIR)/bench_*.log + clean: rm -rf target/bench diff --git a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs index d9c0f828a3..3c2f1bd41e 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs @@ -1,4 +1,7 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use core::cmp::min; +use wcet_qemu_sys::sysstate::mutators::InterruptShifterMutator; +use wcet_qemu_sys::sysstate::IRQ_INPUT_OFFSET; use std::str::FromStr; use wcet_qemu_sys::worst::DumpEdgesMapMetadata; use wcet_qemu_sys::worst::DumpMapFeedback; @@ -34,7 +37,7 @@ use wcet_qemu_sys::sysstate::helpers::QemuSystemStateHelper; use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; use wcet_qemu_sys::sysstate::feedbacks::SysStateFeedbackState; use wcet_qemu_sys::sysstate::feedbacks::NovelSysStateFeedback; -use wcet_qemu_sys::sysstate::INPUT_BYTES_OFFSET; +use wcet_qemu_sys::sysstate::IRQ_INPUT_BYTES_NUMBER; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::minimizer::QemuCaseMinimizerStage; use hashbrown::HashMap; @@ -505,6 +508,8 @@ fn fuzz( let mutator_list = mutator_list.merge(tuple_list!(RandGraphSuffixMutator::new())); #[cfg(feature = "muta_snip")] let mutator_list = mutator_list.merge(tuple_list!(RandGraphSnippetMutator::new())); + #[cfg(all(feature = "muta_interrupt",feature = "fuzz_interrupt"))] + let mutator_list = mutator_list.merge(tuple_list!(InterruptShifterMutator::new())); // Setup a MOPT mutator let mutator = StdMOptMutator::new(&mut state, mutator_list,5)?; @@ -531,15 +536,14 @@ fn fuzz( let mut buf = target.as_slice(); let mut len = buf.len(); let mut int_tick : Option = None; - if INPUT_BYTES_OFFSET!= 0 { - if len > 2 { - let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt - t[0]=buf[0]; - t[1]=buf[1]; - int_tick = Some(u32::from_le_bytes(t)); - buf = &buf[2..]; - len = buf.len(); + if len > IRQ_INPUT_BYTES_NUMBER as usize { + let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt + for i in 0..min(4,IRQ_INPUT_BYTES_NUMBER) { + t[i as usize]=buf[i as usize]; } + int_tick = Some(u32::from_le_bytes(t)); + buf = &buf[IRQ_INPUT_BYTES_NUMBER as usize..]; + len = buf.len(); } if len >= 32 { buf = &buf[0..32]; @@ -547,8 +551,8 @@ fn fuzz( } unsafe { - if INPUT_BYTES_OFFSET!= 0 { - libafl_int_offset = 347780+int_tick.unwrap_or(0); + if IRQ_INPUT_BYTES_NUMBER!= 0 { + libafl_int_offset = IRQ_INPUT_OFFSET+int_tick.unwrap_or(0); } // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); diff --git a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs index 43404ae230..39c7d6dd62 100644 --- a/fuzzers/wcet_qemu_sys/src/bin/showmap.rs +++ b/fuzzers/wcet_qemu_sys/src/bin/showmap.rs @@ -1,10 +1,12 @@ //! A singlethreaded QEMU fuzzer that can auto-restart. +use core::cmp::min; +use wcet_qemu_sys::sysstate::IRQ_INPUT_OFFSET; +use wcet_qemu_sys::sysstate::IRQ_INPUT_BYTES_NUMBER; use wcet_qemu_sys::sysstate::helpers::INTR_OFFSET; use std::io::Read; use wcet_qemu_sys::sysstate::observers::QemuSysStateObserver; use wcet_qemu_sys::sysstate::feedbacks::DumpSystraceFeedback; -use wcet_qemu_sys::sysstate::INPUT_BYTES_OFFSET; use wcet_qemu_sys::worst::QemuHashMapObserver; use wcet_qemu_sys::{ worst::{DumpMapFeedback,DummyFeedback}, @@ -340,15 +342,14 @@ fn fuzz( let mut buf = target.as_slice(); let mut len = buf.len(); let mut int_tick : Option = None; - if INPUT_BYTES_OFFSET!= 0 { - if len > 2 { - let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt - t[0]=buf[0]; - t[1]=buf[1]; - int_tick = Some(u32::from_le_bytes(t)); - buf = &buf[2..]; - len = buf.len(); + if len > IRQ_INPUT_BYTES_NUMBER as usize { + let mut t : [u8; 4] = [0,0,0,0]; // 4 extra bytes determine the tick to execute an interrupt + for i in 0..min(4,IRQ_INPUT_BYTES_NUMBER) { + t[i as usize]=buf[i as usize]; } + int_tick = Some(u32::from_le_bytes(t)); + buf = &buf[IRQ_INPUT_BYTES_NUMBER as usize..]; + len = buf.len(); } if len >= 32 { buf = &buf[0..32]; @@ -356,8 +357,8 @@ fn fuzz( } unsafe { - if INPUT_BYTES_OFFSET!= 0 { - libafl_int_offset = 347780+int_tick.unwrap_or(0); + if IRQ_INPUT_BYTES_NUMBER!= 0 { + libafl_int_offset = IRQ_INPUT_OFFSET+int_tick.unwrap_or(0); } // INTR_OFFSET = int_tick; emu.write_mem(test_length_ptr,&(len as u32).to_le_bytes()); diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs index e12e490ed1..c26cd66e53 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/graph.rs @@ -45,12 +45,12 @@ use libafl::bolts::rands::Rand; //============================= Data Structures #[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)] -struct VariantTuple +pub struct VariantTuple { - start_tick: u64, - end_tick: u64, + pub start_tick: u64, + pub end_tick: u64, input_counter: u32, - input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime + pub input: Vec, // in the end any kind of input are bytes, regardless of type and lifetime } impl VariantTuple { fn from(other: &RefinedFreeRTOSSystemState,input: Vec) -> Self { @@ -67,7 +67,7 @@ impl VariantTuple { pub struct SysGraphNode { base: RefinedFreeRTOSSystemState, - variants: Vec, + pub variants: Vec, } impl SysGraphNode { fn from(base: RefinedFreeRTOSSystemState, input: Vec) -> Self { @@ -117,7 +117,7 @@ impl PartialEq for SysGraphNode { // Wrapper around Vec to attach as Metadata #[derive(Debug, Default, Serialize, Deserialize, Clone)] pub struct SysGraphMetadata { - inner: Vec, + pub inner: Vec, indices: Vec, tcref: isize, } diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs index ac609edf99..c5eabb82d0 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mod.rs @@ -14,8 +14,13 @@ pub mod helpers; pub mod observers; pub mod feedbacks; pub mod graph; +pub mod mutators; -pub const INPUT_BYTES_OFFSET : u32 = 2; // Offset for interrupt bytes +#[cfg(feature = "fuzz_interrupt")] +pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes +#[cfg(not(feature = "fuzz_interrupt"))] +pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes +pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start // Constants const NUM_PRIOS: usize = 5; diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs b/fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs new file mode 100644 index 0000000000..29082a572c --- /dev/null +++ b/fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs @@ -0,0 +1,123 @@ +use crate::sysstate::graph::SysGraphMetadata; +use crate::sysstate::graph::SysGraphNode; +use libafl::state::HasFeedbackStates; +use crate::sysstate::IRQ_INPUT_OFFSET; +use crate::sysstate::IRQ_INPUT_BYTES_NUMBER; +use crate::sysstate::graph::SysGraphFeedbackState; +use libafl::inputs::HasBytesVec; +use libafl::bolts::rands::RandomSeed; +use libafl::bolts::rands::StdRand; +use libafl::mutators::Mutator; +use libafl::mutators::MutationResult; +use core::marker::PhantomData; +use libafl::state::HasCorpus; +use libafl::state::HasSolutions; +use libafl::state::HasRand; + +use libafl::bolts::tuples::MatchName; +use libafl::bolts::tuples::Named; +use libafl::Error; +use libafl::{inputs::Input, state::HasMetadata}; + +use super::FreeRTOSSystemStateMetadata; + +use libafl::bolts::rands::Rand; + + +//=============================== Interrupt +/// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata +pub struct InterruptShifterMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + phantom: PhantomData<(I, S)>, +} +impl InterruptShifterMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions, +{ + pub fn new() -> Self { + InterruptShifterMutator{phantom: PhantomData} + } +} +impl Mutator for InterruptShifterMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn mutate( + &mut self, + state: &mut S, + input: &mut I, + _stage_idx: i32 + ) -> Result + { + // need our own random generator, because borrowing rules + let mut myrand = StdRand::new(); + let tmp = &mut state.rand_mut(); + myrand.set_seed(tmp.next()); + drop(tmp); + + let target_bytes = input.bytes_mut(); + let mut target_tick = 0; + + #[cfg(feature = "sched_state")] + { + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace =tmp.expect("FreeRTOSSystemStateMetadata not found"); + let target_block = myrand.choose(trace.inner.iter()); + target_tick = myrand.between(target_block.start_tick,target_block.end_tick)-IRQ_INPUT_OFFSET as u64; + } + #[cfg(feature = "sched_state")] + { + let feedbackstate = state + .feedback_states() + .match_name::("SysMap") + .unwrap(); + let g = &feedbackstate.graph; + let tmp = state.metadata().get::(); + if tmp.is_none() { // if there are no metadata it was probably not interesting anyways + return Ok(MutationResult::Skipped); + } + let trace = tmp.expect("SysGraphMetadata not found"); + let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())]; + target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) { + Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64, + None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64, + }; + + } + if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 { + for i in 0..IRQ_INPUT_BYTES_NUMBER as usize { + target_bytes[i] = u64::to_le_bytes(target_tick)[i]; + } + return Ok(MutationResult::Mutated); + } else { + return Ok(MutationResult::Skipped); + } + } + + fn post_exec( + &mut self, + _state: &mut S, + _stage_idx: i32, + _corpus_idx: Option + ) -> Result<(), Error> { + Ok(()) + } +} + +impl Named for InterruptShifterMutator +where + I: Input + HasBytesVec, + S: HasRand + HasMetadata + HasCorpus + HasSolutions + HasFeedbackStates, +{ + fn name(&self) -> &str { + "InterruptShifterMutator" + } +} \ No newline at end of file diff --git a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs index 0a33266787..91b5480a51 100644 --- a/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs +++ b/fuzzers/wcet_qemu_sys/src/sysstate/observers.rs @@ -1,4 +1,4 @@ -use crate::sysstate::INPUT_BYTES_OFFSET; +use crate::sysstate::IRQ_INPUT_BYTES_NUMBER; use libafl::inputs::HasTargetBytes; use libafl::bolts::HasLen; use libafl::bolts::tuples::Named; @@ -130,7 +130,7 @@ for mut i in input.drain(..) { start_tick: start_tick, end_tick: i.qemu_tick, ready_list_after: collector, - input_counter: i.input_counter+INPUT_BYTES_OFFSET, + input_counter: i.input_counter+IRQ_INPUT_BYTES_NUMBER, last_pc: i.last_pc, }); start_tick=i.qemu_tick;