Compare commits
84 Commits
fret
...
update_mer
Author | SHA1 | Date | |
---|---|---|---|
52d927dc61 | |||
8a661de371 | |||
e8ff45b205 | |||
aa7d78affe | |||
948ce540e8 | |||
6969b5de5a | |||
4dabfc5f00 | |||
eb3914e5c1 | |||
2466fc5cb6 | |||
eabfe020b6 | |||
2e81f386ee | |||
5b4f730187 | |||
d1c685ccf9 | |||
6a18fa75f4 | |||
ee4056a008 | |||
14e8a81d17 | |||
f994bc62bc | |||
517b3d3da7 | |||
86ca7863ca | |||
5d28754e1b | |||
70dec21c8c | |||
18a592d763 | |||
865e97cca7 | |||
01e40ded1d | |||
0b638d9020 | |||
67165640c5 | |||
6551fc31f4 | |||
5c4238e0ee | |||
865f2786a8 | |||
fc4b72274e | |||
47a4bc19d4 | |||
e8b24b0c7a | |||
4e3acf85ad | |||
66babddb02 | |||
c92cbe78d8 | |||
1d0c43081a | |||
89cf096b9d | |||
0693422e44 | |||
8691c77eb2 | |||
5176828dbb | |||
71280b89af | |||
5d08f3a9d7 | |||
a6294af2c3 | |||
5df99365f6 | |||
f5bf5605f1 | |||
53bd755647 | |||
ffdaf22b1d | |||
b73a971c51 | |||
b85e0a6d5b | |||
5bf3269c8f | |||
1650545424 | |||
44faa80556 | |||
be1ead84f4 | |||
c3f398a315 | |||
819f759be9 | |||
bec4743978 | |||
c252d6cad0 | |||
1713824c3b | |||
5c5f1f77bd | |||
d7c0193a5e | |||
2c3e1a4e4d | |||
64dfd6a642 | |||
99bd30c233 | |||
9369eae37b | |||
dbd6391b59 | |||
06d382bff8 | |||
2ead941419 | |||
1edc70c6f1 | |||
9a1251875f | |||
b3b8e81190 | |||
ba85877ab4 | |||
8a82ecac9d | |||
8676342776 | |||
44a32398d9 | |||
29f89b4b29 | |||
e0f197424c | |||
45bdab5000 | |||
d4d86927b7 | |||
d2d2862727 | |||
ac181eb99d | |||
c1db0752c1 | |||
de4481e70d | |||
edff095401 | |||
d460bab404 |
6
fuzzers/wcet_qemu_sys/.gitignore
vendored
Normal file
6
fuzzers/wcet_qemu_sys/.gitignore
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
*.axf
|
||||
*.qcow2
|
||||
demo
|
||||
*.ron
|
||||
*.bsp
|
||||
tmp*
|
56
fuzzers/wcet_qemu_sys/Cargo.toml
Normal file
56
fuzzers/wcet_qemu_sys/Cargo.toml
Normal file
@ -0,0 +1,56 @@
|
||||
[package]
|
||||
name = "wcet_qemu_sys"
|
||||
version = "0.1.2"
|
||||
authors = [ "Alwin Berger <alwin.berger@tu-dortmund.de>" ]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
default = ["std", "obj_collect"]
|
||||
std = []
|
||||
multicore = []
|
||||
|
||||
fuzz_interrupt = [] # use the first bytes of the input for a timed interrupts
|
||||
fuzz_random = [] # just process random inputs
|
||||
|
||||
# select which feedbacks to use. enable at least one.
|
||||
feed_known_edges = []
|
||||
feed_afl = []
|
||||
feed_clock = []
|
||||
feed_state = []
|
||||
feed_graph = []
|
||||
|
||||
# choose exactly one scheduler
|
||||
sched_queue = []
|
||||
sched_mapmax = []
|
||||
sched_state = []
|
||||
sched_graph = []
|
||||
|
||||
# objective selection, additive
|
||||
obj_collect = []
|
||||
obj_trace = []
|
||||
obj_edges = []
|
||||
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
|
||||
|
||||
[profile.release]
|
||||
debug = true
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../../libafl/" }
|
||||
libafl_qemu = { path = "../../libafl_qemu/", features = ["systemmode", "arm", "jmp_as_edge"] }
|
||||
clap = { version = "3.0.0-beta.2", features = ["default"] }
|
||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||
ron = "0.7" # write serialized data - including hashmaps
|
||||
hashbrown = { version = "0.11", features = ["serde", "ahash-compile-time-rng"], default-features=false } # A faster hashmap, nostd compatible
|
||||
nix = "0.23.0"
|
||||
goblin = "0.4.2"
|
||||
either = "1.6.1"
|
||||
num-traits = "0.2"
|
||||
petgraph = { version="0.6.0", features = ["serde-1"] }
|
113
fuzzers/wcet_qemu_sys/Makefile
Normal file
113
fuzzers/wcet_qemu_sys/Makefile
Normal file
@ -0,0 +1,113 @@
|
||||
BENCHDIR = target/bench
|
||||
TNAME = tmrint
|
||||
TARGET = $(TNAME).axf
|
||||
TARGET_TRACE = $(BENCHDIR)/traces/$(TNAME)_worst.ron
|
||||
TARGET_EDGES = $(BENCHDIR)/edges/$(TNAME)_worst.ron
|
||||
RUNTIME = 3600
|
||||
INT_FLAG = ,fuzz_interrupt
|
||||
COMMON_FLAGS = benchmark,dump_infos#,$(INT_FLAG)
|
||||
NUM_JOB = 3
|
||||
NUM_ITERATIONS = 10
|
||||
LOCKFILE = /tmp/bench_sem
|
||||
LOCK = ./bash_sem.sh $(LOCKFILE) lock
|
||||
RELEASE = ./bash_sem.sh $(LOCKFILE) release
|
||||
|
||||
$(BENCHDIR)/bin:
|
||||
mkdir -p $@
|
||||
|
||||
$(BENCHDIR)/target_random:
|
||||
cargo build --target-dir $@ --features $(COMMON_FLAGS),sched_queue,fuzz_random
|
||||
|
||||
$(BENCHDIR)/target_known_edges:
|
||||
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 $(COMMON_FLAGS),feed_afl,sched_queue
|
||||
|
||||
$(BENCHDIR)/target_afl_mapmax:
|
||||
cargo build --bin fuzzer --target-dir $@ --features $(COMMON_FLAGS),feed_afl,sched_mapmax
|
||||
|
||||
$(BENCHDIR)/target_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 $(COMMON_FLAGS),feed_state,feed_afl,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 $(COMMON_FLAGS),feed_graph,sched_graph
|
||||
|
||||
$(BENCHDIR)/target_graph_muta:
|
||||
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 $(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_muta_afl_int:
|
||||
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_random $(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_muta_afl_int
|
||||
|
||||
# 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..$(NUM_ITERATIONS)}; do \
|
||||
CASE=$$(basename -s.log $@ | cut -d'_' -f 2- ); \
|
||||
[ -f $(BENCHDIR)/execs/$$CASE\_$$i.exec -a -f $@_$$i ] && continue; \
|
||||
$(LOCK); \
|
||||
echo $$CASE iteration $$i; \
|
||||
mkdir -p $(BENCHDIR)/infos/$$CASE ; \
|
||||
./bench_fuzzer.sh $</debug/fuzzer $(TARGET) --libafl-traces $(TARGET_TRACE) --libafl-fuzztime $(RUNTIME) \
|
||||
--libafl-dump $(BENCHDIR)/infos/$$CASE/$$i \
|
||||
--libafl-edges $(TARGET_EDGES) --libafl-exectimes $(BENCHDIR)/execs/$$CASE\_$$i.exec > $@_$$i && \
|
||||
sed -i "1 i\\$$CASE " $(BENCHDIR)/execs/$$CASE\_$$i.exec && \
|
||||
$(RELEASE) & \
|
||||
done
|
||||
wait
|
||||
for i in $@_*; do grep Stats $$i | tail -n 1 >> $@; done
|
||||
|
||||
benchmarks_noint: 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_muta_afl.log
|
||||
# target/bench/bench_graph_all.log target/bench/bench_state_afl_int.log
|
||||
|
||||
benchmarks_int: target/bench/bench_graph_muta_afl.log target/bench/bench_graph_muta_afl_int.log target/bench/bench_state_afl.log target/bench/bench_state_afl_int.log target/bench/bench_afl_mapmax.log target/bench/bench_afl_queue.log
|
||||
|
||||
benchmark_random: target/bench/bench_random.log
|
||||
|
||||
all: binaries benchmarks_noint
|
||||
|
||||
clean_bench:
|
||||
rm -rf $(BENCHDIR)/bench_*
|
||||
|
||||
clean_aggregate:
|
||||
rm -rf $(BENCHDIR)/bench_*.log
|
||||
|
||||
clean:
|
||||
rm -rf target/bench
|
||||
echo $(NUM_JOB) > $(LOCKFILE)
|
||||
rm -rf $(LOCKFILE)_lockdir
|
||||
|
||||
reset_sem:
|
||||
echo $(NUM_JOB) > $(LOCKFILE)
|
||||
rm -rf $(LOCKFILE)_lockdir
|
||||
|
||||
%.case: %_inputs $(BENCHDIR)/target_random
|
||||
mkdir -p $(BENCHDIR)/traces $(BENCHDIR)/edges
|
||||
for i in $</*.case; do \
|
||||
CASE=$$(basename -s.case $$i); echo $$CASE; \
|
||||
PROG=$$(basename -s.case $@); \
|
||||
LD_LIBRARY_PATH=$(BENCHDIR)/target_random/debug $(BENCHDIR)/target_random/debug/showmap --libafl-snapshot tmp/dummy.qcow2 \
|
||||
--libafl-out tmp/test_out --libafl-in tmp/test_in --libafl-kernel $(TARGET) \
|
||||
--libafl-single $$i \
|
||||
--libafl-edges $(BENCHDIR)/edges/$$PROG\_$$CASE.ron \
|
||||
--libafl-traces $(BENCHDIR)/traces/$$PROG\_$$CASE.ron | \
|
||||
grep "Qemu Ticks:"; \
|
||||
done
|
||||
|
||||
$(TARGET_TRACE): $(TNAME).case
|
10
fuzzers/wcet_qemu_sys/README.md
Normal file
10
fuzzers/wcet_qemu_sys/README.md
Normal file
@ -0,0 +1,10 @@
|
||||
# Systemmode Fuzzing
|
||||
|
||||
This folder contains an example fuzzer tailored for system-images.
|
||||
For the moment it is mostly hardcoded.
|
||||
|
||||
## Build
|
||||
|
||||
To build this example, run `cargo build --release` or `cargo build --release`.
|
||||
Also build the FreeRTOS demo named CORTEX_M3_MPS2_QEMU_GCC.
|
||||
Then run `./starter.sh RTOSDemo.axf` and observe.
|
46
fuzzers/wcet_qemu_sys/bash_sem.sh
Executable file
46
fuzzers/wcet_qemu_sys/bash_sem.sh
Executable file
@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
# A generic counting semaphore in bash
|
||||
# Parameter is the lockfile and operation
|
||||
# Setup:
|
||||
# rm /tmp/test
|
||||
# echo $num > /tmp/test
|
||||
|
||||
set -e
|
||||
if [[ ! -f $1 ]]; then echo "Parameter 1: File Does not exist"; exit; fi
|
||||
if [[ $2 != "lock" ]] && [[ $2 != "release" ]]; then echo "Parameter 2: must be lock or release"; exit; fi
|
||||
if [[ $2 = "lock" ]]; then
|
||||
SEM=''
|
||||
while [[ -z $SEM ]]; do
|
||||
if (( $(cat $1 ) == 0 )); then sleep 1; wait; continue; fi
|
||||
if mkdir $1_lockdir > /dev/null 2>&1 ; then
|
||||
VAL=$(cat $1)
|
||||
if (( $VAL > 0 ))
|
||||
then
|
||||
SEM=$(sed -i "s@$VAL@$(( $VAL - 1))@w /dev/stdout" $1)
|
||||
echo "Take $VAL -> $SEM"
|
||||
rmdir $1_lockdir
|
||||
else
|
||||
rmdir $1_lockdir
|
||||
sleep 1; wait
|
||||
fi
|
||||
else
|
||||
sleep 0.5;
|
||||
fi
|
||||
done
|
||||
else
|
||||
echo "Attempt unlock"
|
||||
SEM=''
|
||||
while [[ -z $SEM ]]; do
|
||||
if mkdir $1_lockdir > /dev/null 2>&1 ; then
|
||||
VAL=$(cat $1)
|
||||
SEM=$(sed -i "s@$VAL@$(( $VAL + 1))@w /dev/stdout" $1)
|
||||
echo "Give $VAL -> $(( $VAL + 1 ))"
|
||||
else
|
||||
sleep 0.1;
|
||||
fi
|
||||
rmdir $1_lockdir
|
||||
done
|
||||
fi
|
||||
|
||||
#SEM=''; while [[ -z SEM ]]; do VAL=$(cat /tmp/test); if (( $VAL > 0 )); then SEM=$(sed -i "s@$VAL@$(( $VAL - 1))@w /dev/stdout" /tmp/test); else sleep 1; wait; fi; done
|
5
fuzzers/wcet_qemu_sys/bench_fuzzer.sh
Executable file
5
fuzzers/wcet_qemu_sys/bench_fuzzer.sh
Executable file
@ -0,0 +1,5 @@
|
||||
mkdir -p tmp/test_in tmp/test_out
|
||||
[ ! -f tmp/test_in/test ] && echo " !test" > tmp/test_in/test
|
||||
[ ! -f tmp/dummy.qcow2 ] && qemu-img create -f qcow2 tmp/dummy.qcow2 32M
|
||||
export LD_LIBRARY_PATH=target/debug
|
||||
$1 --libafl-snapshot tmp/dummy.qcow2 --libafl-out tmp/test_out --libafl-in tmp/test_in --libafl-kernel ${@:2}
|
4
fuzzers/wcet_qemu_sys/fuzzer.sh
Executable file
4
fuzzers/wcet_qemu_sys/fuzzer.sh
Executable file
@ -0,0 +1,4 @@
|
||||
mkdir -p tmp/test_in tmp/test_out
|
||||
[ ! -f tmp/test_in/test ] && echo " !test" > tmp/test_in/test
|
||||
[ ! -f tmp/dummy.qcow2 ] && qemu-img create -f qcow2 tmp/dummy.qcow2 32M
|
||||
LD_LIBRARY_PATH=target/debug target/debug/fuzzer --libafl-logfile tmp/libafl.log --libafl-snapshot tmp/dummy.qcow2 --libafl-out tmp/test_out --libafl-in tmp/test_in --libafl-kernel $@
|
4
fuzzers/wcet_qemu_sys/showmap.sh
Executable file
4
fuzzers/wcet_qemu_sys/showmap.sh
Executable file
@ -0,0 +1,4 @@
|
||||
mkdir -p tmp/test_in tmp/test_out
|
||||
[ ! -f tmp/test_in/test ] && echo " !test" > tmp/test_in/test
|
||||
[ ! -f tmp/dummy.qcow2 ] && qemu-img create -f qcow2 tmp/dummy.qcow2 32M
|
||||
LD_LIBRARY_PATH=target/debug target/debug/showmap --libafl-snapshot tmp/dummy.qcow2 --libafl-out tmp/test_out --libafl-in tmp/test_in --libafl-kernel $@
|
769
fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs
Normal file
769
fuzzers/wcet_qemu_sys/src/bin/fuzzer.rs
Normal file
@ -0,0 +1,769 @@
|
||||
//! A singlethreaded QEMU fuzzer that can auto-restart.
|
||||
use libafl::Evaluator;
|
||||
use libafl::generators::Generator;
|
||||
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;
|
||||
use libafl::inputs::HasBytesVec;
|
||||
use wcet_qemu_sys::sysstate::FreeRTOSSystemStateMetadata;
|
||||
use wcet_qemu_sys::sysstate::feedbacks::DumpSystraceFeedback;
|
||||
use libafl::state::HasSolutions;
|
||||
use wcet_qemu_sys::worst::TimeMaximizerCorpusScheduler;
|
||||
use libafl::corpus::InMemoryCorpus;
|
||||
use wcet_qemu_sys::sysstate::graph::RandGraphSuffixMutator;
|
||||
use wcet_qemu_sys::sysstate::graph::RandInputSnippetMutator;
|
||||
use wcet_qemu_sys::worst::DummyFeedback;
|
||||
use wcet_qemu_sys::worst::ExecTimeCollectorFeedback;
|
||||
use wcet_qemu_sys::worst::EXEC_TIME_COLLECTION;
|
||||
use wcet_qemu_sys::worst::ExecTimeReachedFeedback;
|
||||
use libafl::inputs::Input;
|
||||
use libafl::feedbacks::Feedback;
|
||||
use libafl::HasFeedback;
|
||||
use libafl::bolts::tuples::MatchName;
|
||||
use libafl::state::HasFeedbackStates;
|
||||
use wcet_qemu_sys::sysstate::graph::SysGraphMetadata;
|
||||
use wcet_qemu_sys::sysstate::helpers::INTR_OFFSET;
|
||||
use wcet_qemu_sys::sysstate::graph::RandGraphSnippetMutator;
|
||||
use wcet_qemu_sys::sysstate::graph::GraphMaximizerCorpusScheduler;
|
||||
use wcet_qemu_sys::sysstate::graph::SysMapFeedback;
|
||||
use wcet_qemu_sys::sysstate::graph::SysGraphFeedbackState;
|
||||
use libafl::stats::SimpleStats;
|
||||
use wcet_qemu_sys::sysstate::feedbacks::HitSysStateFeedback;
|
||||
use wcet_qemu_sys::sysstate::RefinedFreeRTOSSystemState;
|
||||
use libafl::corpus::QueueCorpusScheduler;
|
||||
use libafl_qemu::QemuInstrumentationFilter;
|
||||
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::IRQ_INPUT_BYTES_NUMBER;
|
||||
use wcet_qemu_sys::worst::QemuHashMapObserver;
|
||||
use wcet_qemu_sys::minimizer::QemuCaseMinimizerStage;
|
||||
use hashbrown::HashMap;
|
||||
use clap::{App, Arg};
|
||||
use core::{cell::RefCell, time::Duration};
|
||||
use std::time::SystemTime;
|
||||
#[cfg(unix)]
|
||||
use nix::{self, unistd::dup};
|
||||
#[cfg(unix)]
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd};
|
||||
use std::{
|
||||
env,
|
||||
fs::{self, File, OpenOptions},
|
||||
io::{self, Write},
|
||||
path::PathBuf,
|
||||
process,
|
||||
};
|
||||
use petgraph::prelude::DiGraph;
|
||||
use petgraph::dot::{Dot, Config};
|
||||
|
||||
use libafl::{
|
||||
bolts::{
|
||||
current_nanos, current_time,
|
||||
os::{dup2,Cores},
|
||||
rands::StdRand,
|
||||
tuples::{tuple_list, Merge},
|
||||
shmem::{ShMemProvider,StdShMemProvider},
|
||||
launcher::Launcher,
|
||||
},
|
||||
corpus::{
|
||||
Corpus, OnDiskCorpus, PowerQueueCorpusScheduler,
|
||||
},
|
||||
executors::{ExitKind, ShadowExecutor, TimeoutExecutor},
|
||||
feedback_or,
|
||||
feedbacks::{MapFeedbackState, MaxMapFeedback},
|
||||
fuzzer::{Fuzzer, StdFuzzer},
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
monitors::{MultiMonitor,SimpleMonitor},
|
||||
mutators::{
|
||||
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
|
||||
StdMOptMutator, StdScheduledMutator, Tokens,
|
||||
},
|
||||
observers::{VariableMapObserver},
|
||||
stages::{
|
||||
calibrate::CalibrationStage,
|
||||
power::{PowerMutationalStage, PowerSchedule},
|
||||
ShadowTracingStage, StdMutationalStage,
|
||||
},
|
||||
state::{HasCorpus, HasMetadata, StdState},
|
||||
events::{SimpleEventManager,EventConfig},
|
||||
generators::RandBytesGenerator,
|
||||
Error,
|
||||
};
|
||||
use libafl_qemu::{
|
||||
//asan::QemuAsanHelper,
|
||||
cmplog,
|
||||
cmplog::{CmpLogObserver, QemuCmpLogHelper},
|
||||
edges,
|
||||
edges::QemuEdgeCoverageHelper,
|
||||
elf::EasyElf,
|
||||
emu::Emulator, filter_qemu_args, libafl_int_offset, libafl_exec_block_hook,
|
||||
snapshot_sys::QemuSysSnapshotHelper,
|
||||
QemuExecutor,
|
||||
clock,
|
||||
QemuClockObserver,
|
||||
clock::ClockFeedback,
|
||||
clock::QemuClockIncreaseFeedback
|
||||
};
|
||||
use wcet_qemu_sys::worst::{SortedFeedback,HitFeedback,HitImprovingFeedback,TimeStateMaximizerCorpusScheduler,LenTimeMaximizerCorpusScheduler};
|
||||
|
||||
|
||||
/// The fuzzer main
|
||||
pub fn main() {
|
||||
// Registry the metadata types used in this fuzzer
|
||||
// Needed only on no_std
|
||||
//RegistryBuilder::register::<Tokens>();
|
||||
|
||||
let res = match App::new("wcet_qemu_fuzzer")
|
||||
.version("0.4.0")
|
||||
.author("Alwin Berger")
|
||||
.about("LibAFL-based fuzzer for WCET in System Kernels.")
|
||||
.arg(
|
||||
Arg::new("k")
|
||||
.long("libafl-kernel")
|
||||
.required(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("out")
|
||||
.help("The directory to place finds in ('corpus')")
|
||||
.long("libafl-out")
|
||||
.required(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("in")
|
||||
.help("The directory to read initial inputs from ('seeds')")
|
||||
.long("libafl-in")
|
||||
.required(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("tokens")
|
||||
.long("libafl-tokens")
|
||||
.help("A file to read tokens from, to be used during fuzzing")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("logfile")
|
||||
.long("libafl-logfile")
|
||||
.help("Duplicates all output to this file")
|
||||
.default_value("libafl.log"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("timeout")
|
||||
.long("libafl-timeout")
|
||||
.help("Timeout for each individual execution, in milliseconds")
|
||||
.default_value("1000"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("edges")
|
||||
.long("libafl-edges")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("traces")
|
||||
.long("libafl-traces")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("snapshot")
|
||||
.help("The qcow2 file used for snapshots")
|
||||
.long("libafl-snapshot")
|
||||
.required(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("exectimes")
|
||||
.long("libafl-exectimes")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("targettime")
|
||||
.long("libafl-targettime")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("dump")
|
||||
.long("libafl-dump")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("fuzztime")
|
||||
.long("libafl-fuzztime")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("graphdump")
|
||||
.long("libafl-graphdump")
|
||||
.takes_value(true),
|
||||
)
|
||||
.try_get_matches_from(filter_qemu_args())
|
||||
{
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
println!(
|
||||
"Syntax: {}, --libafl-in <input> --libafl-out <output>\n{:?}",
|
||||
env::current_exe()
|
||||
.unwrap_or_else(|_| "fuzzer".into())
|
||||
.to_string_lossy(),
|
||||
err.info,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
println!(
|
||||
"Workdir: {:?}",
|
||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
||||
);
|
||||
|
||||
// For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir.
|
||||
let mut out_dir = PathBuf::from(res.value_of("out").unwrap().to_string());
|
||||
if fs::create_dir(&out_dir).is_err() {
|
||||
println!("Out dir at {:?} already exists.", &out_dir);
|
||||
if !out_dir.is_dir() {
|
||||
println!("Out dir at {:?} is not a valid directory!", &out_dir);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut crashes = out_dir.clone();
|
||||
crashes.push("wcets");
|
||||
out_dir.push("queue");
|
||||
|
||||
let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string());
|
||||
if !in_dir.is_dir() {
|
||||
println!("In dir at {:?} is not a valid directory!", &in_dir);
|
||||
return;
|
||||
}
|
||||
|
||||
let tokens = res.value_of("tokens").map(PathBuf::from);
|
||||
|
||||
let logfile = PathBuf::from(res.value_of("logfile").unwrap().to_string());
|
||||
|
||||
let timeout = Duration::from_millis(
|
||||
res.value_of("timeout")
|
||||
.unwrap()
|
||||
.to_string()
|
||||
.parse()
|
||||
.expect("Could not parse timeout in milliseconds"),
|
||||
);
|
||||
|
||||
let kernel = PathBuf::from(res.value_of("k").unwrap().to_string());
|
||||
let edges = match res.value_of("edges") {
|
||||
Some(st) => Some(PathBuf::from(st.to_string())),
|
||||
None => None
|
||||
};
|
||||
|
||||
let traces = match res.value_of("traces") {
|
||||
Some(st) => Some(PathBuf::from(st.to_string())),
|
||||
None => None
|
||||
};
|
||||
|
||||
let exectimes = match res.value_of("exectimes") {
|
||||
Some(st) => Some(PathBuf::from(st.to_string())),
|
||||
None => None
|
||||
};
|
||||
|
||||
let targettime = match res.value_of("targettime") {
|
||||
Some(st) => Some(st.to_string().parse::<u64>().expect("targettime not a number")),
|
||||
None => None
|
||||
};
|
||||
|
||||
let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string());
|
||||
|
||||
let dump_path = match res.value_of("dump") {
|
||||
Some(st) => Some(PathBuf::from(st.to_string())),
|
||||
None => None
|
||||
};
|
||||
|
||||
let fuzztime = match res.value_of("fuzztime") {
|
||||
Some(st) => Some(u64::from_str(st).expect("Failed parsing fuzztime")),
|
||||
None => None
|
||||
};
|
||||
|
||||
let graph_dump = match res.value_of("graphdump") {
|
||||
Some(st) => Some(PathBuf::from(st.to_string())),
|
||||
None => None
|
||||
};
|
||||
|
||||
fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout, kernel, edges, traces, exectimes, targettime, snapshot, dump_path, fuzztime, graph_dump)
|
||||
.expect("An error occurred while fuzzing");
|
||||
}
|
||||
|
||||
fn virt2phys(vaddr : u64, tab : &goblin::elf::Elf) -> u64 {
|
||||
let ret;
|
||||
for i in &tab.program_headers {
|
||||
if i.vm_range().contains(&vaddr.try_into().expect("Can not cast u64 to usize")) {
|
||||
ret = vaddr-i.p_vaddr+i.p_paddr;
|
||||
return ret - (ret % 2);
|
||||
}
|
||||
}
|
||||
ret = vaddr;
|
||||
// unlike the arm-toolcahin goblin produces some off-by one errors when parsing arm
|
||||
return ret - (ret % 2);
|
||||
}
|
||||
|
||||
/// The actual fuzzer
|
||||
fn fuzz(
|
||||
corpus_dir: PathBuf,
|
||||
objective_dir: PathBuf,
|
||||
seed_dir: PathBuf,
|
||||
tokenfile: Option<PathBuf>,
|
||||
logfile: PathBuf,
|
||||
_timeout: Duration,
|
||||
kernel: PathBuf,
|
||||
dump_edges: Option<PathBuf>,
|
||||
dump_traces: Option<PathBuf>,
|
||||
dump_exectimes: Option<PathBuf>,
|
||||
targettime: Option<u64>,
|
||||
snapshot: PathBuf,
|
||||
dump_path: Option<PathBuf>,
|
||||
fuzztime: Option<u64>,
|
||||
graph_dump: Option<PathBuf>,
|
||||
) -> Result<(), Error> {
|
||||
env::remove_var("LD_LIBRARY_PATH");
|
||||
|
||||
//=========== Analyze the binary to find the target function address
|
||||
let mut elf_buffer = Vec::new();
|
||||
let elf = EasyElf::from_file(&kernel, &mut elf_buffer)?;
|
||||
|
||||
let input_addr = elf
|
||||
.resolve_symbol("FUZZ_INPUT", 0)
|
||||
.expect("Symbol FUZZ_INPUT not found");
|
||||
let input_addr = virt2phys(input_addr,&elf.goblin());
|
||||
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
||||
let test_length_ptr = elf
|
||||
.resolve_symbol("FUZZ_LENGTH", 0)
|
||||
.expect("Symbol FUZZ_LENGTH not found");
|
||||
let test_length_ptr = virt2phys(test_length_ptr,&elf.goblin());
|
||||
println!("FUZZ_LENGTH @ {:#x}", test_length_ptr);
|
||||
let input_counter = elf
|
||||
.resolve_symbol("FUZZ_POINTER", 0)
|
||||
.expect("Symbol FUZZ_POINTER not found");
|
||||
// let input_counter = virt2phys(input_counter,&elf.goblin());
|
||||
println!("FUZZ_LENGTH @ {:#x}", test_length_ptr);
|
||||
let check_breakpoint = elf
|
||||
.resolve_symbol("trigger_Qemu_break", 0)
|
||||
.expect("Symbol trigger_Qemu_break not found");
|
||||
let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin());
|
||||
println!("Breakpoint at {:#x}", check_breakpoint);
|
||||
|
||||
let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers
|
||||
.resolve_symbol("pxCurrentTCB", 0)
|
||||
.expect("Symbol pxCurrentTCBC not found");
|
||||
// let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf.goblin());
|
||||
println!("TCB pointer at {:#x}", curr_tcb_pointer);
|
||||
let task_queue_addr = elf
|
||||
.resolve_symbol("pxReadyTasksLists", 0)
|
||||
.expect("Symbol pxReadyTasksLists not found");
|
||||
// let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin());
|
||||
println!("Task Queue at {:#x}", task_queue_addr);
|
||||
let svh = elf
|
||||
.resolve_symbol("xPortPendSVHandler", 0)
|
||||
.expect("Symbol xPortPendSVHandler not found");
|
||||
let svh = virt2phys(svh,&elf.goblin());
|
||||
|
||||
//=========== Prepare Emulator Args
|
||||
let args: Vec<String> = vec![
|
||||
"qemu-system-arm",
|
||||
"-machine","mps2-an385",
|
||||
"-monitor", "null",
|
||||
"-semihosting",
|
||||
"--semihosting-config", "enable=on,target=native",
|
||||
"-kernel", kernel.to_str().unwrap(),
|
||||
"-serial", "stdio", "-nographic",
|
||||
"-snapshot", "-drive", format!("if=none,format=qcow2,file={}",snapshot.to_string_lossy()).as_str(),
|
||||
"-icount", "shift=3,align=off,sleep=off",
|
||||
"-S"
|
||||
].iter().map(|x| x.to_string()).collect();
|
||||
let env: Vec<(String, String)> = env::vars().collect();
|
||||
|
||||
|
||||
let log = RefCell::new(
|
||||
OpenOptions::new()
|
||||
.append(true)
|
||||
.create(true)
|
||||
.open(&logfile)?,
|
||||
);
|
||||
|
||||
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||
let monitor = MultiMonitor::new(|s| {
|
||||
println!("{}",s);
|
||||
writeln!(log.borrow_mut(), "{}",s).unwrap();
|
||||
});
|
||||
|
||||
//====== Child Function
|
||||
let mut run_client = |state: Option<StdState<_, _, _, _, _>>, mut mgr, _core_id| -> std::result::Result<(), libafl::Error> {
|
||||
|
||||
//====== Set up Emu and termination-point
|
||||
let emu = Emulator::new(&args, &env);
|
||||
emu.set_breakpoint(check_breakpoint); // trigger_Qemu_break
|
||||
// Create an observation channel using the coverage map
|
||||
let edges = unsafe { &mut edges::EDGES_MAP };
|
||||
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
|
||||
let edges_observer =
|
||||
QemuHashMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
|
||||
|
||||
// Create an observation channel to keep track of the execution time
|
||||
// let time_observer = TimeObserver::new("time");
|
||||
let clock_observer = QemuClockObserver::default();
|
||||
|
||||
// Create an observation channel using cmplog map
|
||||
// let cmplog_observer = CmpLogObserver::new("cmplog", unsafe { &mut cmplog::CMPLOG_MAP }, true);
|
||||
|
||||
// The state of the edges feedback.
|
||||
let feedback_state = MapFeedbackState::with_observer(&edges_observer);
|
||||
|
||||
let sysstate_observer = QemuSysStateObserver::new();
|
||||
#[cfg(feature = "sched_state")]
|
||||
let sysstate_feedback_state = SysStateFeedbackState::default();
|
||||
#[cfg(not(feature = "sched_state"))]
|
||||
let sysstate_feedback_state = SysGraphFeedbackState::new();
|
||||
|
||||
let target_map : HashMap<(u64,u64),u16> = match dump_edges {
|
||||
None => HashMap::new(),
|
||||
Some(ref s) => {
|
||||
let raw = fs::read(s).expect("Can not read dumped edges");
|
||||
let hmap : HashMap<(u64,u64),u16> = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse HashMap");
|
||||
hmap
|
||||
},
|
||||
};
|
||||
let target_trace : Option<Vec<RefinedFreeRTOSSystemState>> = match dump_traces {
|
||||
None => None,
|
||||
Some(ref s) => {
|
||||
let raw = fs::read(s).expect("Can not read dumped traces");
|
||||
let trace : Vec<RefinedFreeRTOSSystemState> = ron::from_str(&String::from_utf8_lossy(&raw)).expect("Can not parse traces");
|
||||
Some(trace)
|
||||
},
|
||||
};
|
||||
// Feedback to rate the interestingness of an input
|
||||
let feedback = ClockFeedback::new_with_observer(&clock_observer);
|
||||
#[cfg(all(not(feature = "feed_state"), feature = "dump_infos"))] // for diagnostic purposes it's necessary to collect the state in any case
|
||||
let feedback = feedback_or!(feedback, DumpSystraceFeedback::metadata_only());
|
||||
#[cfg(all(feature = "obj_collect", feature = "fuzz_random"))] // random fuzzing does not trigger objective feedbacks
|
||||
let feedback = feedback_or!(feedback, ExecTimeCollectorFeedback::new());
|
||||
#[cfg(feature = "dump_infos")] // for diagnostic purposes it's necessary to collect the state in any case
|
||||
let feedback = feedback_or!(feedback, DumpMapFeedback::metadata_only(&edges_observer));
|
||||
#[cfg(feature = "feed_known_edges")]
|
||||
let feedback = feedback_or!(feedback, HitImprovingFeedback::new(target_map.clone(), &edges_observer));
|
||||
#[cfg(feature = "feed_afl")]
|
||||
let feedback = feedback_or!(feedback, MaxMapFeedback::new_tracking(&feedback_state, &edges_observer, true, false));
|
||||
#[cfg(feature = "feed_clock")]
|
||||
let feedback = feedback_or!(feedback, QemuClockIncreaseFeedback::default());
|
||||
#[cfg(feature = "feed_state")]
|
||||
let feedback = feedback_or!(feedback, NovelSysStateFeedback::default());
|
||||
#[cfg(feature = "feed_graph")]
|
||||
let feedback = feedback_or!(feedback, SysMapFeedback::new());
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let objective = DummyFeedback::new(false);
|
||||
#[cfg(all(feature = "obj_collect",not(feature = "fuzz_random")))]
|
||||
let objective = ExecTimeCollectorFeedback::new();
|
||||
#[cfg(feature = "obj_trace")]
|
||||
let objective = feedback_or!(HitSysStateFeedback::new(target_trace));
|
||||
#[cfg(feature = "obj_edges")]
|
||||
let objective = feedback_or!(HitFeedback::new(target_map,0.0,&edges_observer), objective);
|
||||
#[cfg(feature = "obj_ticks")]
|
||||
let objective = feedback_or!(ExecTimeReachedFeedback::new(targettime));
|
||||
|
||||
// create a State from scratch
|
||||
#[cfg(feature = "benchmark")]
|
||||
let mut state = state.unwrap_or_else(||{
|
||||
StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved
|
||||
InMemoryCorpus::new(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
InMemoryCorpus::new(),
|
||||
// States of the feedbacks.
|
||||
// They are the data related to the feedbacks that you want to persist in the State.
|
||||
tuple_list!(feedback_state,clock::MaxIcountMetadata::default(),sysstate_feedback_state),
|
||||
)
|
||||
});
|
||||
#[cfg(not(feature = "benchmark"))]
|
||||
let mut state = state.unwrap_or_else(||{
|
||||
StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved
|
||||
OnDiskCorpus::new(&corpus_dir).unwrap(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
OnDiskCorpus::new(&objective_dir).unwrap(),
|
||||
// States of the feedbacks.
|
||||
// They are the data related to the feedbacks that you want to persist in the State.
|
||||
tuple_list!(feedback_state,clock::MaxIcountMetadata::default(),sysstate_feedback_state),
|
||||
)
|
||||
});
|
||||
|
||||
// let calibration = CalibrationStage::new(&mut state, &edges_observer);
|
||||
|
||||
// Setup a randomic Input2State stage
|
||||
// let i2s = StdMutationalStage::new(StdScheduledMutator::new(tuple_list!(I2SRandReplace::new())));
|
||||
|
||||
let mutator_list = havoc_mutations();
|
||||
#[cfg(feature = "muta_input")]
|
||||
let mutator_list = mutator_list.merge(tuple_list!(RandInputSnippetMutator::new()));
|
||||
#[cfg(feature = "muta_suffix")]
|
||||
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)?;
|
||||
|
||||
// let power = PowerMutationalStage::new(mutator, PowerSchedule::FAST, &edges_observer);
|
||||
let mutation = StdMutationalStage::new(mutator);
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
#[cfg(feature = "sched_queue")]
|
||||
let scheduler = QueueCorpusScheduler::new();
|
||||
#[cfg(feature = "sched_mapmax")]
|
||||
let scheduler = TimeMaximizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
#[cfg(feature = "sched_state")]
|
||||
let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
#[cfg(feature = "sched_graph")]
|
||||
let scheduler = GraphMaximizerCorpusScheduler::new(QueueCorpusScheduler::new());
|
||||
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let mut len = buf.len();
|
||||
let mut int_tick : Option<u32> = None;
|
||||
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];
|
||||
len = 32;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
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());
|
||||
emu.write_mem(input_addr,buf);
|
||||
|
||||
emu.run();
|
||||
// since the breakpoint interrupted the last task the last state needs to be recorded
|
||||
libafl_exec_block_hook(check_breakpoint);
|
||||
}
|
||||
|
||||
ExitKind::Ok
|
||||
};
|
||||
|
||||
let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1,check_breakpoint..check_breakpoint+1]);
|
||||
let mut executor = QemuExecutor::new(
|
||||
&mut harness,
|
||||
&emu,
|
||||
tuple_list!(
|
||||
QemuEdgeCoverageHelper::new(),
|
||||
QemuCmpLogHelper::new(),
|
||||
//QemuAsanHelper::new(),
|
||||
QemuSysSnapshotHelper::new(),
|
||||
QemuSystemStateHelper::with_instrumentation_filter(
|
||||
system_state_filter,curr_tcb_pointer.try_into().unwrap(),
|
||||
task_queue_addr.try_into().unwrap(),
|
||||
input_counter.try_into().unwrap()
|
||||
)
|
||||
),
|
||||
tuple_list!(edges_observer, clock_observer,sysstate_observer),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
)?;
|
||||
|
||||
// Create the executor for an in-process function with one observer for edge coverage and one for the execution time
|
||||
// let executor = TimeoutExecutor::new(executor, timeout);
|
||||
// Show the cmplog observer
|
||||
// let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
|
||||
|
||||
// Read tokens
|
||||
if let Some(tokenfile) = &tokenfile {
|
||||
if state.metadata().get::<Tokens>().is_none() {
|
||||
state.add_metadata(Tokens::from_tokens_file(&tokenfile)?);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fuzz_random"))]
|
||||
if state.corpus().count() < 1 {
|
||||
if _core_id == 0 && state.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[seed_dir.clone()]).is_ok() {
|
||||
// println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
} else {
|
||||
let mut generator = RandBytesGenerator::new(32);
|
||||
state
|
||||
.generate_initial_inputs(
|
||||
&mut fuzzer,
|
||||
&mut executor,
|
||||
&mut generator,
|
||||
&mut mgr,
|
||||
8,
|
||||
)
|
||||
.expect("Failed to generate the initial corpus");
|
||||
}
|
||||
}
|
||||
|
||||
// let tracing = ShadowTracingStage::new(&mut executor);
|
||||
|
||||
// The order of the stages matter!
|
||||
let mut stages = tuple_list!(mutation,QemuCaseMinimizerStage::new(16));
|
||||
|
||||
// Remove target ouput (logs still survive)
|
||||
#[cfg(unix)]
|
||||
let file_null = File::open("/dev/null")?;
|
||||
#[cfg(unix)]
|
||||
#[cfg(feature = "multicore")]
|
||||
{
|
||||
let null_fd = file_null.as_raw_fd();
|
||||
dup2(null_fd, io::stdout().as_raw_fd())?;
|
||||
dup2(null_fd, io::stderr().as_raw_fd())?;
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fuzz_random"))]
|
||||
{
|
||||
match fuzztime {
|
||||
Some(t) => fuzzer
|
||||
.fuzz_for_solution_or_n(&mut stages, &mut executor, &mut state, &mut mgr, t)
|
||||
.expect("Error in the fuzzing loop"),
|
||||
None => fuzzer
|
||||
.fuzz_for_solution(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||
.expect("Error in the fuzzing loop"),
|
||||
};
|
||||
}
|
||||
#[cfg(feature = "fuzz_random")]
|
||||
{
|
||||
let mut generator = RandBytesGenerator::new(32);
|
||||
let target_duration = Duration::from_secs(fuzztime.expect("Random fuzzing needs target time"));
|
||||
let start_time = SystemTime::now();
|
||||
while start_time.elapsed().unwrap() < target_duration {
|
||||
let inp = generator.generate(&mut state).unwrap();
|
||||
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp);
|
||||
}
|
||||
match fuzzer.fuzz_for_solution_or_n(&mut stages, &mut executor, &mut state, &mut mgr, 0) {
|
||||
_ => (),
|
||||
} // Just here for the typecheker
|
||||
}
|
||||
|
||||
|
||||
#[cfg(not(feature = "benchmark"))]
|
||||
#[cfg(feature = "feed_graph")]
|
||||
{
|
||||
let feedbackstate = state
|
||||
.feedback_states()
|
||||
.match_name::<SysGraphFeedbackState>("SysMap")
|
||||
.unwrap();
|
||||
let newgraph = feedbackstate.graph.map(
|
||||
|_, n| n.get_taskname(),
|
||||
// |_, n| format!("{} {:?}",n.get_taskname(),n.get_input_counts().iter().min().unwrap_or(&0)),
|
||||
|_, e| e,
|
||||
);
|
||||
let tempg = format!("{:?}",Dot::with_config(&newgraph, &[Config::EdgeNoLabel]));
|
||||
match graph_dump {
|
||||
Some(gd) => fs::write(gd,tempg).expect("Graph can not be written"),
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
// Wite out the collected exec times
|
||||
#[cfg(feature = "obj_collect")]
|
||||
match dump_exectimes {
|
||||
Some(et) => unsafe {
|
||||
let mut stringforms : Vec<String> = Vec::new();
|
||||
for i in EXEC_TIME_COLLECTION.drain(..) {
|
||||
stringforms.push(format!("{}\n",i));
|
||||
}
|
||||
fs::write(et,stringforms.concat()).expect("Exec times can not be written");
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
|
||||
#[cfg(feature = "dump_infos")]
|
||||
{
|
||||
let c = if state.solutions().count()>0 { state.solutions() } else { state.corpus() };
|
||||
let mut worst = Duration::new(0,0);
|
||||
let mut worst_input : Option<Vec<u8>> = Some(vec![]);
|
||||
let mut worst_trace : Option<FreeRTOSSystemStateMetadata> = None;
|
||||
let mut worst_map : Option<DumpEdgesMapMetadata> = None;
|
||||
for i in 0..c.count() {
|
||||
let tc = c.get(i).expect("Could not get element from corpus").borrow();
|
||||
if worst < tc.exec_time().expect("Testcase missing duration") {
|
||||
worst = tc.exec_time().expect("Testcase missing duration");
|
||||
let metadata = tc.metadata();
|
||||
worst_trace = metadata.get::<FreeRTOSSystemStateMetadata>().cloned();
|
||||
worst_map = metadata.get::<DumpEdgesMapMetadata>().cloned();
|
||||
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
|
||||
}
|
||||
}
|
||||
let mut dump_path = dump_path;
|
||||
match &mut dump_path {
|
||||
Some(dp) => {
|
||||
dp.set_extension("trace");
|
||||
println!("Path found: {:?}",dp);
|
||||
fs::write(&dp,ron::to_string(&worst_trace).expect("Error serializing Trace")).expect("Exec times can not be written");
|
||||
dp.set_extension("map");
|
||||
println!("Path found: {:?}",dp);
|
||||
fs::write(&dp,ron::to_string(&worst_map).expect("Error serializing hashmap")).expect("Exec times can not be written");
|
||||
dp.set_extension("input");
|
||||
println!("Path found: {:?}",dp);
|
||||
fs::write(&dp,worst_input.unwrap());
|
||||
dp.set_extension("time");
|
||||
println!("Path found: {:?}",dp);
|
||||
fs::write(&dp,format!("{}",worst.as_nanos()>>3));
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
|
||||
// Never reached
|
||||
Ok(())
|
||||
|
||||
};
|
||||
|
||||
// Multicore Variant
|
||||
#[cfg(feature = "multicore")]
|
||||
{
|
||||
match Launcher::builder()
|
||||
.shmem_provider(shmem_provider)
|
||||
.configuration(EventConfig::AlwaysUnique)
|
||||
.monitor(monitor)
|
||||
.run_client(&mut run_client)
|
||||
.cores(&Cores::from_cmdline("all").unwrap())
|
||||
// .broker_port(1337)
|
||||
// .remote_broker_addr(remote_broker_addr)
|
||||
//.stdout_file(Some("/dev/null"))
|
||||
.build()
|
||||
.launch()
|
||||
{
|
||||
Ok(_) | Err(Error::ShuttingDown) => (),
|
||||
Err(e) => panic!("{:?}", e),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Simple Variant
|
||||
#[cfg(not(feature = "multicore"))]
|
||||
{
|
||||
let stats = SimpleStats::new(|s| println!("{}", s));
|
||||
let mgr = SimpleEventManager::new(stats);
|
||||
run_client(None, mgr, 0)
|
||||
}
|
||||
}
|
432
fuzzers/wcet_qemu_sys/src/bin/showmap.rs
Normal file
432
fuzzers/wcet_qemu_sys/src/bin/showmap.rs
Normal file
@ -0,0 +1,432 @@
|
||||
//! 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::worst::QemuHashMapObserver;
|
||||
use wcet_qemu_sys::{
|
||||
worst::{DumpMapFeedback,DummyFeedback},
|
||||
sysstate::helpers::QemuSystemStateHelper,
|
||||
};
|
||||
use clap::{App, Arg};
|
||||
use std::{
|
||||
env,
|
||||
fs::{self},
|
||||
path::PathBuf,
|
||||
};
|
||||
|
||||
use libafl::{
|
||||
bolts::{
|
||||
current_nanos,
|
||||
rands::StdRand,
|
||||
tuples::{tuple_list},
|
||||
},
|
||||
corpus::{Corpus,InMemoryCorpus,QueueCorpusScheduler},
|
||||
executors::{ExitKind},
|
||||
fuzzer::{StdFuzzer},
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
observers::{VariableMapObserver},
|
||||
state::{HasCorpus,StdState},
|
||||
Error,
|
||||
Evaluator,
|
||||
stats::SimpleStats,
|
||||
events::SimpleEventManager,
|
||||
stages::StdMutationalStage,
|
||||
mutators::BitFlipMutator,
|
||||
Fuzzer,
|
||||
feedback_or,
|
||||
};
|
||||
use libafl_qemu::{
|
||||
edges,
|
||||
edges::QemuEdgeCoverageHelper,
|
||||
emu::Emulator, filter_qemu_args, libafl_int_offset, libafl_exec_block_hook,
|
||||
elf::EasyElf,
|
||||
snapshot_sys::QemuSysSnapshotHelper,
|
||||
clock::{QemuClockObserver},
|
||||
QemuInstrumentationFilter,
|
||||
QemuExecutor,
|
||||
};
|
||||
|
||||
use either::{Either,Left,Right};
|
||||
|
||||
/// The fuzzer main
|
||||
pub fn main() {
|
||||
// Registry the metadata types used in this fuzzer
|
||||
// Needed only on no_std
|
||||
//RegistryBuilder::register::<Tokens>();
|
||||
|
||||
let res = match App::new("wcet_qemu_fuzzer")
|
||||
.version("0.4.0")
|
||||
.author("Alwin Berger")
|
||||
.about("LibAFL-based fuzzer for WCET in System Kernels.")
|
||||
.arg(
|
||||
Arg::new("k")
|
||||
.long("libafl-kernel")
|
||||
.required(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("out")
|
||||
.long("libafl-out")
|
||||
.required(false)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("in")
|
||||
.long("libafl-in")
|
||||
.required(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("tokens")
|
||||
.long("libafl-tokens")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("logfile")
|
||||
.long("libafl-logfile")
|
||||
.default_value("libafl.log"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("timeout")
|
||||
.long("libafl-timeout")
|
||||
.default_value("1000"),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("edges")
|
||||
.long("libafl-edges")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("traces")
|
||||
.long("libafl-traces")
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("snapshot")
|
||||
.help("The qcow2 file used for snapshots")
|
||||
.long("libafl-snapshot")
|
||||
.required(true)
|
||||
.takes_value(true),
|
||||
)
|
||||
.arg(
|
||||
Arg::new("single")
|
||||
.long("libafl-single")
|
||||
.takes_value(true)
|
||||
)
|
||||
.try_get_matches_from(filter_qemu_args())
|
||||
{
|
||||
Ok(res) => res,
|
||||
Err(err) => {
|
||||
println!(
|
||||
"Syntax: {}, --libafl-in <input>\n{:?}",
|
||||
env::current_exe()
|
||||
.unwrap_or_else(|_| "fuzzer".into())
|
||||
.to_string_lossy(),
|
||||
err.info,
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
println!(
|
||||
"Workdir: {:?}",
|
||||
env::current_dir().unwrap().to_string_lossy().to_string()
|
||||
);
|
||||
|
||||
// For fuzzbench, crashes and finds are inside the same `corpus` directory, in the "queue" and "crashes" subdir.
|
||||
let mut out_dir = PathBuf::from(res.value_of("out").unwrap().to_string());
|
||||
if fs::create_dir(&out_dir).is_err() {
|
||||
println!("Out dir at {:?} already exists.", &out_dir);
|
||||
if !out_dir.is_dir() {
|
||||
println!("Out dir at {:?} is not a valid directory!", &out_dir);
|
||||
return;
|
||||
}
|
||||
}
|
||||
let mut worstcases = out_dir.clone();
|
||||
worstcases.push("worstcase");
|
||||
out_dir.push("queue");
|
||||
|
||||
let seed = match res.value_of("single") {
|
||||
Some(s) => if s=="-" {
|
||||
let mut buf = Vec::<u8>::new();
|
||||
std::io::stdin().read_to_end(&mut buf).expect("Could not read Stdin");
|
||||
Left(buf)
|
||||
} else {
|
||||
Left(fs::read(s).expect("Input file for --libafl-single can not be read"))
|
||||
},
|
||||
None => {
|
||||
let in_dir = PathBuf::from(res.value_of("in").unwrap().to_string());
|
||||
if !in_dir.is_dir() {
|
||||
println!("In dir at {:?} is not a valid directory!", &in_dir);
|
||||
return;
|
||||
}
|
||||
Right(in_dir)
|
||||
},
|
||||
};
|
||||
println!("{:?}",seed);
|
||||
|
||||
let kernel = PathBuf::from(res.value_of("k").unwrap().to_string());
|
||||
let edges = match res.value_of("edges") {
|
||||
Some(st) => Some(PathBuf::from(st.to_string())),
|
||||
None => None
|
||||
};
|
||||
|
||||
let traces = match res.value_of("traces") {
|
||||
Some(st) => Some(PathBuf::from(st.to_string())),
|
||||
None => None
|
||||
};
|
||||
|
||||
let snapshot = PathBuf::from(res.value_of("snapshot").unwrap().to_string());
|
||||
|
||||
fuzz(seed, kernel, edges, traces, snapshot)
|
||||
.expect("An error occurred while fuzzing");
|
||||
}
|
||||
|
||||
fn virt2phys(vaddr : u64, tab : &goblin::elf::Elf) -> u64 {
|
||||
let ret;
|
||||
for i in &tab.program_headers {
|
||||
if i.vm_range().contains(&vaddr.try_into().expect("Can not cast u64 to usize")) {
|
||||
ret = vaddr-i.p_vaddr+i.p_paddr;
|
||||
return ret - (ret % 2);
|
||||
}
|
||||
}
|
||||
ret = vaddr;
|
||||
// unlike the arm-toolcahin goblin produces some off-by one errors when parsing arm
|
||||
return ret - (ret % 2);
|
||||
}
|
||||
|
||||
/// The actual fuzzer
|
||||
fn fuzz(
|
||||
seed: Either<Vec<u8>,PathBuf>,
|
||||
kernel: PathBuf,
|
||||
dump_edges: Option<PathBuf>,
|
||||
dump_traces: Option<PathBuf>,
|
||||
snapshot: PathBuf,
|
||||
) -> Result<(), Error> {
|
||||
//=========== Setup emulator
|
||||
let mut env: Vec<(String, String)> = env::vars().collect();
|
||||
let mut args: Vec<String> = vec![
|
||||
"qemu-system-arm",
|
||||
"-machine","mps2-an385",
|
||||
"-monitor", "null",
|
||||
"-semihosting",
|
||||
"--semihosting-config", "enable=on,target=native",
|
||||
"-kernel", kernel.to_str().unwrap(),
|
||||
"-serial", "stdio", "-nographic",
|
||||
"-snapshot", "-drive", format!("if=none,format=qcow2,file={}",snapshot.to_string_lossy()).as_str(),
|
||||
"-icount", "shift=3,align=off,sleep=off",
|
||||
"-S"
|
||||
].iter().map(|x| x.to_string()).collect();
|
||||
let emu = Emulator::new(&mut args, &mut env);
|
||||
//=========== Analyze the binary to find the target function address
|
||||
let mut elf_buffer = Vec::new();
|
||||
let bin_path=kernel;
|
||||
let elf = EasyElf::from_file(bin_path, &mut elf_buffer)?;
|
||||
|
||||
let test_one_input_ptr = elf
|
||||
.resolve_symbol("FUZZ_INPUT", 0)
|
||||
.expect("Symbol FUZZ_INPUT not found");
|
||||
let test_one_input_ptr = virt2phys(test_one_input_ptr,&elf.goblin());
|
||||
println!("FUZZ_INPUT @ {:#x}", test_one_input_ptr);
|
||||
let test_length_ptr = elf
|
||||
.resolve_symbol("FUZZ_LENGTH", 0)
|
||||
.expect("Symbol FUZZ_LENGTH not found");
|
||||
let test_length_ptr = virt2phys(test_length_ptr,&elf.goblin());
|
||||
println!("FUZZ_LENGTH @ {:#x}", test_length_ptr);
|
||||
let input_counter = elf
|
||||
.resolve_symbol("FUZZ_POINTER", 0)
|
||||
.expect("Symbol FUZZ_POINTER not found");
|
||||
// let input_counter = virt2phys(input_counter,&elf.goblin());
|
||||
println!("FUZZ_LENGTH @ {:#x}", test_length_ptr);
|
||||
let check_breakpoint = elf
|
||||
.resolve_symbol("trigger_Qemu_break", 0)
|
||||
.expect("Symbol trigger_Qemu_break not found");
|
||||
let check_breakpoint = virt2phys(check_breakpoint,&elf.goblin());
|
||||
println!("Breakpoint at {:#x}", check_breakpoint);
|
||||
let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers
|
||||
.resolve_symbol("pxCurrentTCB", 0)
|
||||
.expect("Symbol pxCurrentTCBC not found");
|
||||
// let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf.goblin());
|
||||
println!("TCB pointer at {:#x}", curr_tcb_pointer);
|
||||
let task_queue_addr = elf
|
||||
.resolve_symbol("pxReadyTasksLists", 0)
|
||||
.expect("Symbol pxReadyTasksLists not found");
|
||||
// let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin());
|
||||
println!("Task Queue at {:#x}", task_queue_addr);
|
||||
let systick_handler = elf
|
||||
.resolve_symbol("xPortSysTickHandler", 0)
|
||||
.expect("Symbol xPortSysTickHandler not found");
|
||||
let systick_handler = virt2phys(systick_handler,&elf.goblin());
|
||||
println!("SysTick at {:#x}", systick_handler);
|
||||
let svc_handle = elf
|
||||
.resolve_symbol("vPortSVCHandler", 0)
|
||||
.expect("Symbol vPortSVCHandler not found");
|
||||
let svc_handle = virt2phys(svc_handle,&elf.goblin());
|
||||
println!("SVChandle at {:#x}", svc_handle);
|
||||
let svh = elf
|
||||
.resolve_symbol("xPortPendSVHandler", 0)
|
||||
.expect("Symbol xPortPendSVHandler not found");
|
||||
let svh = virt2phys(svh,&elf.goblin());
|
||||
println!("PendHandle at {:#x}", svh);
|
||||
let app_code_start = elf
|
||||
.resolve_symbol("__APP_CODE_START__", 0)
|
||||
.expect("Symbol __APP_CODE_START__ not found");
|
||||
let app_code_start = virt2phys(app_code_start,&elf.goblin());
|
||||
let app_code_end = elf
|
||||
.resolve_symbol("__APP_CODE_END__", 0)
|
||||
.expect("Symbol __APP_CODE_END__ not found");
|
||||
let app_code_end = virt2phys(app_code_end,&elf.goblin());
|
||||
let app_range = app_code_start..app_code_end;
|
||||
println!("App Code {:x}-{:x}",app_code_start,app_code_end);
|
||||
|
||||
|
||||
|
||||
//====== Create the input field
|
||||
let input_addr = test_one_input_ptr;
|
||||
println!("Placing input at {:#x}", input_addr);
|
||||
emu.set_breakpoint(check_breakpoint);
|
||||
|
||||
//====== Create the most simple status display and managers.
|
||||
|
||||
let stats = SimpleStats::new(|s| println!("{}", s));
|
||||
|
||||
let mut mgr = SimpleEventManager::new(stats);
|
||||
|
||||
|
||||
//========== EDGES_MAP is static field which somehow(?) gets handed over to the Qemu instrumentation
|
||||
//========== Otherwise setup generic observers
|
||||
// Create an observation channel using the coverage map
|
||||
let edges = unsafe { &mut edges::EDGES_MAP };
|
||||
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
|
||||
let edges_observer =
|
||||
QemuHashMapObserver::new(VariableMapObserver::new("edges", edges, edges_counter));
|
||||
|
||||
//========== Observe Execution Cycles
|
||||
let clock_observer = QemuClockObserver::default();
|
||||
|
||||
//========= Feedback-Function evaluate the Maps. Need to dump it for debugging and check if it reaches targets.
|
||||
let feedback = feedback_or!(DumpMapFeedback::with_dump(dump_edges, &edges_observer),DumpSystraceFeedback::with_dump(dump_traces));
|
||||
|
||||
// A feedback to choose if an input is a solution or not
|
||||
let objective = DummyFeedback::new(false);
|
||||
|
||||
// create a State from scratch
|
||||
let mut state = StdState::new(
|
||||
// RNG
|
||||
StdRand::with_seed(current_nanos()),
|
||||
// Corpus that will be evolved, we keep it in memory for performance
|
||||
InMemoryCorpus::new(),
|
||||
// Corpus in which we store solutions (crashes in this example),
|
||||
// on disk so the user can get them after stopping the fuzzer
|
||||
InMemoryCorpus::new(),
|
||||
// States of the feedbacks.
|
||||
// They are the data related to the feedbacks that you want to persist in the State.
|
||||
(),
|
||||
);
|
||||
|
||||
// A minimization+queue policy to get testcasess from the corpus
|
||||
let scheduler = QueueCorpusScheduler::new();
|
||||
|
||||
// A fuzzer with feedbacks and a corpus scheduler
|
||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||
|
||||
//======== The harness has to start the execution of the emulator
|
||||
// The wrapped harness function, calling out to the LLVM-style harness
|
||||
let mut harness = |input: &BytesInput| {
|
||||
let target = input.target_bytes();
|
||||
let mut buf = target.as_slice();
|
||||
let mut len = buf.len();
|
||||
let mut int_tick : Option<u32> = None;
|
||||
if len > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER!=0{
|
||||
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];
|
||||
len = 32;
|
||||
}
|
||||
|
||||
unsafe {
|
||||
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());
|
||||
emu.write_mem(input_addr,buf);
|
||||
|
||||
emu.run();
|
||||
// since the breakpoint interrupted the last task the last state needs to be recorded
|
||||
libafl_exec_block_hook(check_breakpoint);
|
||||
println!("Qemu Ticks: {}",emu.get_ticks());
|
||||
}
|
||||
|
||||
ExitKind::Ok
|
||||
};
|
||||
//======= Set System-State watchpoints
|
||||
let system_state_filter = QemuInstrumentationFilter::AllowList(vec![svh..svh+1,check_breakpoint..check_breakpoint+1]);
|
||||
|
||||
//======= Construct the executor, including the Helpers. The edges_observer still contains the ref to EDGES_MAP
|
||||
let mut executor = QemuExecutor::new(
|
||||
&mut harness,
|
||||
&emu,
|
||||
tuple_list!(
|
||||
QemuEdgeCoverageHelper::with_app_range(app_range),
|
||||
// QemuCmpLogHelper::new(),
|
||||
// QemuAsanHelper::new(),
|
||||
QemuSysSnapshotHelper::new(),
|
||||
QemuSystemStateHelper::with_instrumentation_filter(
|
||||
system_state_filter,curr_tcb_pointer.try_into().unwrap(),
|
||||
task_queue_addr.try_into().unwrap(),
|
||||
input_counter.try_into().unwrap()
|
||||
)
|
||||
),
|
||||
tuple_list!(edges_observer,clock_observer,QemuSysStateObserver::new()),
|
||||
&mut fuzzer,
|
||||
&mut state,
|
||||
&mut mgr,
|
||||
)?;
|
||||
match seed {
|
||||
Right(pb) => {
|
||||
if state.corpus().count() < 1 {
|
||||
state
|
||||
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[pb.clone()])
|
||||
.unwrap_or_else(|_| {
|
||||
println!("Failed to load initial corpus at {:?}", &pb);
|
||||
return;
|
||||
});
|
||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||
}
|
||||
// fuzzer
|
||||
// .fuzz_one(&mut tuple_list!(StdMutationalStage::new(BitFlipMutator::new())), &mut executor, &mut state, &mut mgr)
|
||||
// .expect("Error in the fuzzing loop");
|
||||
},
|
||||
Left(s) => {
|
||||
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(s)).expect("Evaluation failed");
|
||||
}
|
||||
}
|
||||
// let firstinput = match seed.clone().is_dir() {
|
||||
// true => seed.clone().read_dir().expect("Directory not a directory?").next().expect("Directory empty?").expect("File not in directory?").path(),
|
||||
// false => seed.clone()
|
||||
// };
|
||||
// let secondinput = match seed_dir.clone().is_dir() {
|
||||
// true => {
|
||||
// let mut a = seed_dir.clone().read_dir().expect("Directory not a directory?");
|
||||
// a.advance_by(1);
|
||||
// a.next().unwrap().expect("File not in directory?").path()
|
||||
// },
|
||||
// false => seed_dir.clone()
|
||||
// };
|
||||
// fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, Input::from_file(&secondinput).expect("Could not load file")).expect("Evaluation failed");
|
||||
// println!("Nach Eval");
|
||||
return Ok(());
|
||||
}
|
7
fuzzers/wcet_qemu_sys/src/lib.rs
Normal file
7
fuzzers/wcet_qemu_sys/src/lib.rs
Normal file
@ -0,0 +1,7 @@
|
||||
#![feature(iter_advance_by)]
|
||||
#![feature(is_sorted)]
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod sysstate;
|
||||
|
||||
pub mod worst;
|
||||
pub mod minimizer;
|
70
fuzzers/wcet_qemu_sys/src/minimizer.rs
Normal file
70
fuzzers/wcet_qemu_sys/src/minimizer.rs
Normal file
@ -0,0 +1,70 @@
|
||||
|
||||
use libafl::inputs::HasBytesVec;
|
||||
use libafl::corpus::Corpus;
|
||||
use libafl::bolts::rands::Rand;
|
||||
use libafl::inputs::BytesInput;
|
||||
use libafl::stages::Stage;
|
||||
use libafl::stages::MutationalStage;
|
||||
use libafl::stages::mutational::DEFAULT_MUTATIONAL_MAX_ITERATIONS;
|
||||
use core::marker::PhantomData;
|
||||
use libafl::Evaluator;
|
||||
use libafl::state::HasRand;
|
||||
use libafl::state::HasCorpus;
|
||||
use libafl::Error;
|
||||
use libafl::state::HasClientPerfMonitor;
|
||||
use libafl::mutators::Mutator;
|
||||
use libafl::inputs::Input;
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct QemuCaseMinimizerStage<E, EM, S, Z>
|
||||
where
|
||||
S: HasClientPerfMonitor + HasCorpus<BytesInput> + HasRand,
|
||||
Z: Evaluator<E, EM, BytesInput, S>,
|
||||
{
|
||||
max_input_length: usize,
|
||||
#[allow(clippy::type_complexity)]
|
||||
phantom: PhantomData<(E, EM, S, Z)>,
|
||||
}
|
||||
|
||||
impl<E, EM, S, Z> Stage<E, EM, S, Z> for QemuCaseMinimizerStage<E, EM, S, Z>
|
||||
where
|
||||
S: HasClientPerfMonitor + HasCorpus<BytesInput> + HasRand,
|
||||
Z: Evaluator<E, EM, BytesInput, S>,
|
||||
{
|
||||
#[inline]
|
||||
#[allow(clippy::let_and_return)]
|
||||
fn perform(
|
||||
&mut self,
|
||||
fuzzer: &mut Z,
|
||||
executor: &mut E,
|
||||
state: &mut S,
|
||||
manager: &mut EM,
|
||||
corpus_idx: usize,
|
||||
) -> Result<(), Error> {
|
||||
let mut corpus = state.corpus_mut();
|
||||
let mut case = corpus.get(corpus_idx).unwrap().borrow_mut();
|
||||
let mut input = case.input_mut().as_mut().unwrap();
|
||||
let mut bytes = input.bytes_mut();
|
||||
if bytes.len() > self.max_input_length {
|
||||
bytes.drain(self.max_input_length..);
|
||||
}
|
||||
|
||||
#[cfg(feature = "introspection")]
|
||||
state.introspection_monitor_mut().finish_stage();
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<E, EM, S, Z> QemuCaseMinimizerStage<E, EM, S, Z>
|
||||
where
|
||||
S: HasClientPerfMonitor + HasCorpus<BytesInput> + HasRand,
|
||||
Z: Evaluator<E, EM, BytesInput, S>,
|
||||
{
|
||||
/// Creates a new default mutational stage
|
||||
pub fn new(max: usize) -> Self {
|
||||
Self {
|
||||
max_input_length: max,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
288
fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs
Normal file
288
fuzzers/wcet_qemu_sys/src/sysstate/feedbacks.rs
Normal file
@ -0,0 +1,288 @@
|
||||
use libafl::bolts::ownedref::OwnedSlice;
|
||||
use libafl::inputs::BytesInput;
|
||||
use libafl::prelude::UsesInput;
|
||||
use std::path::PathBuf;
|
||||
use libafl_qemu::clock::QemuClockObserver;
|
||||
// use libafl::feedbacks::FeedbackState;
|
||||
use libafl::corpus::Testcase;
|
||||
// use libafl::state::HasFeedbackStates;
|
||||
use libafl::bolts::tuples::MatchName;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hasher;
|
||||
use std::hash::Hash;
|
||||
use libafl::events::EventFirer;
|
||||
use libafl::state::HasClientPerfMonitor;
|
||||
use libafl::feedbacks::Feedback;
|
||||
use libafl::bolts::tuples::Named;
|
||||
use libafl::Error;
|
||||
use hashbrown::HashMap;
|
||||
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::RefinedFreeRTOSSystemState;
|
||||
use super::FreeRTOSSystemStateMetadata;
|
||||
use super::observers::QemuSysStateObserver;
|
||||
use petgraph::prelude::DiGraph;
|
||||
use petgraph::graph::NodeIndex;
|
||||
use petgraph::Direction;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
//============================= Feedback
|
||||
|
||||
/// Shared Metadata for a SysStateFeedback
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct SysStateFeedbackState
|
||||
{
|
||||
known_traces: HashMap<u64,(u64,u64,usize)>, // encounters,ticks,length
|
||||
longest: Vec<RefinedFreeRTOSSystemState>,
|
||||
}
|
||||
impl Named for SysStateFeedbackState
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"sysstate"
|
||||
}
|
||||
}
|
||||
// impl FeedbackState for SysStateFeedbackState
|
||||
// {
|
||||
// fn reset(&mut self) -> Result<(), Error> {
|
||||
// self.longest.clear();
|
||||
// self.known_traces.clear();
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct NovelSysStateFeedback
|
||||
{
|
||||
last_trace: Option<Vec<RefinedFreeRTOSSystemState>>,
|
||||
// known_traces: HashMap<u64,(u64,usize)>,
|
||||
}
|
||||
|
||||
impl<S> Feedback<S> for NovelSysStateFeedback
|
||||
where
|
||||
S: UsesInput + HasClientPerfMonitor,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &S::Input,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<S>,
|
||||
{
|
||||
let observer = observers.match_name::<QemuSysStateObserver>("sysstate")
|
||||
.expect("QemuSysStateObserver not found");
|
||||
let clock_observer = observers.match_name::<QemuClockObserver>("clock") //TODO not fixed
|
||||
.expect("QemuSysStateObserver not found");
|
||||
let feedbackstate = state
|
||||
.feedback_states_mut()
|
||||
.match_name_mut::<SysStateFeedbackState>("sysstate")
|
||||
.unwrap();
|
||||
// Do Stuff
|
||||
let mut hasher = DefaultHasher::new();
|
||||
observer.last_run.hash(&mut hasher);
|
||||
let somehash = hasher.finish();
|
||||
let mut is_novel = false;
|
||||
let mut takes_longer = false;
|
||||
match feedbackstate.known_traces.get_mut(&somehash) {
|
||||
None => {
|
||||
is_novel = true;
|
||||
feedbackstate.known_traces.insert(somehash,(1,clock_observer.last_runtime(),observer.last_run.len()));
|
||||
}
|
||||
Some(s) => {
|
||||
s.0+=1;
|
||||
if s.1 < clock_observer.last_runtime() {
|
||||
s.1 = clock_observer.last_runtime();
|
||||
takes_longer = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if observer.last_run.len() > feedbackstate.longest.len() {
|
||||
feedbackstate.longest=observer.last_run.clone();
|
||||
}
|
||||
self.last_trace = Some(observer.last_run.clone());
|
||||
// if (!is_novel) { println!("not novel") };
|
||||
Ok(is_novel | takes_longer)
|
||||
}
|
||||
|
||||
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||
#[inline]
|
||||
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
|
||||
let a = self.last_trace.take();
|
||||
match a {
|
||||
Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)),
|
||||
None => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||
#[inline]
|
||||
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||
self.last_trace = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for NovelSysStateFeedback
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"sysstate"
|
||||
}
|
||||
}
|
||||
|
||||
//=============================
|
||||
|
||||
pub fn match_traces(target: &Vec<RefinedFreeRTOSSystemState>, last: &Vec<RefinedFreeRTOSSystemState>) -> bool {
|
||||
let mut ret = true;
|
||||
if target.len() > last.len() {return false;}
|
||||
for i in 0..target.len() {
|
||||
ret &= target[i].current_task.task_name==last[i].current_task.task_name;
|
||||
}
|
||||
ret
|
||||
}
|
||||
pub fn match_traces_name(target: &Vec<String>, last: &Vec<RefinedFreeRTOSSystemState>) -> bool {
|
||||
let mut ret = true;
|
||||
if target.len() > last.len() {return false;}
|
||||
for i in 0..target.len() {
|
||||
ret &= target[i]==last[i].current_task.task_name;
|
||||
}
|
||||
ret
|
||||
}
|
||||
|
||||
/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct HitSysStateFeedback
|
||||
{
|
||||
target: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
impl<S> Feedback<S> for HitSysStateFeedback
|
||||
where
|
||||
S: UsesInput + HasClientPerfMonitor,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &S::Input,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<S>,
|
||||
{
|
||||
let observer = observers.match_name::<QemuSysStateObserver>("sysstate")
|
||||
.expect("QemuSysStateObserver not found");
|
||||
// Do Stuff
|
||||
match &self.target {
|
||||
Some(s) => {
|
||||
// #[cfg(debug_assertions)] eprintln!("Hit SysState Feedback trigger");
|
||||
Ok(match_traces_name(s, &observer.last_run))
|
||||
},
|
||||
None => Ok(false),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for HitSysStateFeedback
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"hit_sysstate"
|
||||
}
|
||||
}
|
||||
|
||||
impl HitSysStateFeedback {
|
||||
pub fn new(target: Option<Vec<RefinedFreeRTOSSystemState>>) -> Self {
|
||||
Self {target: target.map(|x| x.into_iter().map(|y| y.current_task.task_name).collect())}
|
||||
}
|
||||
}
|
||||
//=========================== Debugging Feedback
|
||||
/// A [`Feedback`] meant to dump the system-traces for debugging. Depends on [`QemuSysStateObserver`]
|
||||
#[derive(Debug)]
|
||||
pub struct DumpSystraceFeedback
|
||||
{
|
||||
dumpfile: Option<PathBuf>,
|
||||
dump_metadata: bool,
|
||||
last_trace: Option<Vec<RefinedFreeRTOSSystemState>>,
|
||||
}
|
||||
|
||||
impl<S> Feedback<S> for DumpSystraceFeedback
|
||||
where
|
||||
S: UsesInput + HasClientPerfMonitor,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &S::Input,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<S>,
|
||||
{
|
||||
let observer = observers.match_name::<QemuSysStateObserver>("sysstate")
|
||||
.expect("QemuSysStateObserver not found");
|
||||
match &self.dumpfile {
|
||||
Some(s) => {
|
||||
std::fs::write(s,ron::to_string(&observer.last_run).expect("Error serializing hashmap")).expect("Can not dump to file");
|
||||
self.dumpfile = None
|
||||
},
|
||||
None => if !self.dump_metadata {println!("{:?}",observer.last_run);}
|
||||
};
|
||||
if self.dump_metadata {self.last_trace=Some(observer.last_run.clone());}
|
||||
Ok(!self.dump_metadata)
|
||||
}
|
||||
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||
#[inline]
|
||||
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
|
||||
if !self.dump_metadata {return Ok(());}
|
||||
let a = self.last_trace.take();
|
||||
match a {
|
||||
Some(s) => testcase.metadata_mut().insert(FreeRTOSSystemStateMetadata::new(s)),
|
||||
None => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||
#[inline]
|
||||
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||
self.last_trace = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for DumpSystraceFeedback
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"DumpSysState"
|
||||
}
|
||||
}
|
||||
|
||||
impl DumpSystraceFeedback
|
||||
{
|
||||
/// Creates a new [`DumpSystraceFeedback`]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {dumpfile: None, dump_metadata: false, last_trace: None}
|
||||
}
|
||||
pub fn with_dump(dumpfile: Option<PathBuf>) -> Self {
|
||||
Self {dumpfile: dumpfile, dump_metadata: false, last_trace: None}
|
||||
}
|
||||
pub fn metadata_only() -> Self {
|
||||
Self {dumpfile: None, dump_metadata: true, last_trace: None}
|
||||
}
|
||||
}
|
122
fuzzers/wcet_qemu_sys/src/sysstate/freertos.rs
Normal file
122
fuzzers/wcet_qemu_sys/src/sysstate/freertos.rs
Normal file
@ -0,0 +1,122 @@
|
||||
#![allow(non_camel_case_types,non_snake_case,non_upper_case_globals,deref_nullptr)]
|
||||
use serde::{Deserialize, Serialize};
|
||||
// Manual Types
|
||||
use libafl_qemu::Emulator;
|
||||
|
||||
/*========== Start of generated Code =============*/
|
||||
pub type char_ptr = ::std::os::raw::c_uint;
|
||||
pub type ListItem_t_ptr = ::std::os::raw::c_uint;
|
||||
pub type StackType_t_ptr = ::std::os::raw::c_uint;
|
||||
pub type void_ptr = ::std::os::raw::c_uint;
|
||||
pub type tskTaskControlBlock_ptr = ::std::os::raw::c_uint;
|
||||
pub type xLIST_ptr = ::std::os::raw::c_uint;
|
||||
pub type xLIST_ITEM_ptr = ::std::os::raw::c_uint;
|
||||
/* automatically generated by rust-bindgen 0.59.2 */
|
||||
|
||||
pub type __uint8_t = ::std::os::raw::c_uchar;
|
||||
pub type __uint16_t = ::std::os::raw::c_ushort;
|
||||
pub type __uint32_t = ::std::os::raw::c_uint;
|
||||
pub type StackType_t = u32;
|
||||
pub type UBaseType_t = ::std::os::raw::c_uint;
|
||||
pub type TickType_t = u32;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct xLIST_ITEM {
|
||||
pub xItemValue: TickType_t,
|
||||
pub pxNext: xLIST_ITEM_ptr,
|
||||
pub pxPrevious: xLIST_ITEM_ptr,
|
||||
pub pvOwner: void_ptr,
|
||||
pub pvContainer: xLIST_ptr,
|
||||
}
|
||||
pub type ListItem_t = xLIST_ITEM;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct xMINI_LIST_ITEM {
|
||||
pub xItemValue: TickType_t,
|
||||
pub pxNext: xLIST_ITEM_ptr,
|
||||
pub pxPrevious: xLIST_ITEM_ptr,
|
||||
}
|
||||
pub type MiniListItem_t = xMINI_LIST_ITEM;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct xLIST {
|
||||
pub uxNumberOfItems: UBaseType_t,
|
||||
pub pxIndex: ListItem_t_ptr,
|
||||
pub xListEnd: MiniListItem_t,
|
||||
}
|
||||
pub type List_t = xLIST;
|
||||
pub type TaskHandle_t = tskTaskControlBlock_ptr;
|
||||
pub const eTaskState_eRunning: eTaskState = 0;
|
||||
pub const eTaskState_eReady: eTaskState = 1;
|
||||
pub const eTaskState_eBlocked: eTaskState = 2;
|
||||
pub const eTaskState_eSuspended: eTaskState = 3;
|
||||
pub const eTaskState_eDeleted: eTaskState = 4;
|
||||
pub const eTaskState_eInvalid: eTaskState = 5;
|
||||
pub type eTaskState = ::std::os::raw::c_uint;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct xTASK_STATUS {
|
||||
pub xHandle: TaskHandle_t,
|
||||
pub pcTaskName: char_ptr,
|
||||
pub xTaskNumber: UBaseType_t,
|
||||
pub eCurrentState: eTaskState,
|
||||
pub uxCurrentPriority: UBaseType_t,
|
||||
pub uxBasePriority: UBaseType_t,
|
||||
pub ulRunTimeCounter: u32,
|
||||
pub pxStackBase: StackType_t_ptr,
|
||||
pub usStackHighWaterMark: u16,
|
||||
}
|
||||
pub type TaskStatus_t = xTASK_STATUS;
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Default, Serialize, Deserialize)]
|
||||
pub struct tskTaskControlBlock {
|
||||
pub pxTopOfStack: StackType_t_ptr,
|
||||
pub xStateListItem: ListItem_t,
|
||||
pub xEventListItem: ListItem_t,
|
||||
pub uxPriority: UBaseType_t,
|
||||
pub pxStack: StackType_t_ptr,
|
||||
pub pcTaskName: [::std::os::raw::c_char; 10usize],
|
||||
pub uxBasePriority: UBaseType_t,
|
||||
pub uxMutexesHeld: UBaseType_t,
|
||||
pub ulNotifiedValue: [u32; 1usize],
|
||||
pub ucNotifyState: [u8; 1usize],
|
||||
pub ucStaticallyAllocated: u8,
|
||||
pub ucDelayAborted: u8,
|
||||
}
|
||||
pub type tskTCB = tskTaskControlBlock;
|
||||
pub type TCB_t = tskTCB;
|
||||
/*========== End of generated Code =============*/
|
||||
|
||||
pub trait emu_lookup {
|
||||
fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> Self;
|
||||
}
|
||||
|
||||
|
||||
#[derive(Debug, Copy, Clone, Serialize, Deserialize)]
|
||||
pub enum rtos_struct {
|
||||
TCB_struct(TCB_t),
|
||||
List_struct(List_t),
|
||||
List_Item_struct(ListItem_t),
|
||||
List_MiniItem_struct(MiniListItem_t),
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! impl_emu_lookup {
|
||||
($struct_name:ident) => {
|
||||
impl $crate::sysstate::freertos::emu_lookup for $struct_name {
|
||||
fn lookup(emu: &Emulator, addr: ::std::os::raw::c_uint) -> $struct_name {
|
||||
let mut tmp : [u8; std::mem::size_of::<$struct_name>()] = [0u8; std::mem::size_of::<$struct_name>()];
|
||||
unsafe {
|
||||
emu.read_mem(addr.into(), &mut tmp);
|
||||
std::mem::transmute::<[u8; std::mem::size_of::<$struct_name>()], $struct_name>(tmp)
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
impl_emu_lookup!(TCB_t);
|
||||
impl_emu_lookup!(List_t);
|
||||
impl_emu_lookup!(ListItem_t);
|
||||
impl_emu_lookup!(MiniListItem_t);
|
||||
impl_emu_lookup!(void_ptr);
|
||||
impl_emu_lookup!(TaskStatus_t);
|
592
fuzzers/wcet_qemu_sys/src/sysstate/graph.rs
Normal file
592
fuzzers/wcet_qemu_sys/src/sysstate/graph.rs
Normal file
@ -0,0 +1,592 @@
|
||||
|
||||
/// Feedbacks organizing SystemStates as a graph
|
||||
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 crate::worst::MaxExecsLenFavFactor;
|
||||
// use libafl::corpus::MinimizerCorpusScheduler;
|
||||
use libafl::bolts::HasRefCnt;
|
||||
use libafl::bolts::AsSlice;
|
||||
use libafl::bolts::ownedref::OwnedSlice;
|
||||
use libafl::inputs::BytesInput;
|
||||
use std::path::PathBuf;
|
||||
use libafl_qemu::clock::QemuClockObserver;
|
||||
// use libafl::feedbacks::FeedbackState;
|
||||
use libafl::corpus::Testcase;
|
||||
// use libafl::state::HasFeedbackStates;
|
||||
use libafl::bolts::tuples::MatchName;
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use std::hash::Hasher;
|
||||
use std::hash::Hash;
|
||||
use libafl::events::EventFirer;
|
||||
use libafl::state::HasClientPerfMonitor;
|
||||
use libafl::feedbacks::Feedback;
|
||||
use libafl::bolts::tuples::Named;
|
||||
use libafl::Error;
|
||||
use hashbrown::HashMap;
|
||||
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::RefinedFreeRTOSSystemState;
|
||||
use super::FreeRTOSSystemStateMetadata;
|
||||
use super::observers::QemuSysStateObserver;
|
||||
use petgraph::prelude::DiGraph;
|
||||
use petgraph::graph::NodeIndex;
|
||||
use petgraph::Direction;
|
||||
use std::cmp::Ordering;
|
||||
|
||||
use libafl::bolts::rands::Rand;
|
||||
|
||||
//============================= Data Structures
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Default)]
|
||||
pub struct VariantTuple
|
||||
{
|
||||
pub start_tick: u64,
|
||||
pub end_tick: u64,
|
||||
input_counter: u32,
|
||||
pub input: Vec<u8>, // in the end any kind of input are bytes, regardless of type and lifetime
|
||||
}
|
||||
impl VariantTuple {
|
||||
fn from(other: &RefinedFreeRTOSSystemState,input: Vec<u8>) -> Self {
|
||||
VariantTuple{
|
||||
start_tick: other.start_tick,
|
||||
end_tick: other.end_tick,
|
||||
input_counter: other.input_counter,
|
||||
input: input,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct SysGraphNode
|
||||
{
|
||||
base: RefinedFreeRTOSSystemState,
|
||||
pub variants: Vec<VariantTuple>,
|
||||
}
|
||||
impl SysGraphNode {
|
||||
fn from(base: RefinedFreeRTOSSystemState, input: Vec<u8>) -> Self {
|
||||
SysGraphNode{variants: vec![VariantTuple::from(&base, input)], base:base }
|
||||
}
|
||||
/// unites the variants of this value with another, draining the other if the bases are equal
|
||||
fn unite(&mut self, other: &mut SysGraphNode) -> bool {
|
||||
if self!=other {return false;}
|
||||
self.variants.append(&mut other.variants);
|
||||
self.variants.dedup();
|
||||
return true;
|
||||
}
|
||||
/// add a Varint from a [`RefinedFreeRTOSSystemState`]
|
||||
fn unite_raw(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec<u8>) -> bool {
|
||||
if &self.base!=other {return false;}
|
||||
self.variants.push(VariantTuple::from(other, input.clone()));
|
||||
self.variants.dedup();
|
||||
return true;
|
||||
}
|
||||
/// add a Varint from a [`RefinedFreeRTOSSystemState`], if it's interesting
|
||||
fn unite_interesting(&mut self, other: &RefinedFreeRTOSSystemState, input: &Vec<u8>) -> bool {
|
||||
if &self.base!=other {return false;}
|
||||
let interesting =
|
||||
self.variants.iter().all(|x| x.end_tick-x.start_tick<other.end_tick-other.start_tick) || // longest variant
|
||||
self.variants.iter().all(|x| x.end_tick-x.start_tick>other.end_tick-other.start_tick) || // shortest variant
|
||||
self.variants.iter().all(|x| x.input_counter>other.input_counter) || // longest input
|
||||
self.variants.iter().all(|x| x.input_counter<other.input_counter); // shortest input
|
||||
if interesting {
|
||||
let var = VariantTuple::from(other, input.clone());
|
||||
self.variants.push(var);
|
||||
}
|
||||
return interesting;
|
||||
}
|
||||
pub fn get_taskname(&self) -> &str {
|
||||
&self.base.current_task.task_name
|
||||
}
|
||||
pub fn get_input_counts(&self) -> Vec<u32> {
|
||||
self.variants.iter().map(|x| x.input_counter).collect()
|
||||
}
|
||||
}
|
||||
impl PartialEq for SysGraphNode {
|
||||
fn eq(&self, other: &SysGraphNode) -> bool {
|
||||
self.base==other.base
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
pub struct SysGraphMetadata {
|
||||
pub inner: Vec<NodeIndex>,
|
||||
indices: Vec<usize>,
|
||||
tcref: isize,
|
||||
}
|
||||
impl SysGraphMetadata {
|
||||
pub fn new(inner: Vec<NodeIndex>) -> Self{
|
||||
Self {indices: inner.iter().map(|x| x.index()).collect(), inner: inner, tcref: 0}
|
||||
}
|
||||
}
|
||||
impl AsSlice<usize> for SysGraphMetadata {
|
||||
/// Convert the slice of system-states to a slice of hashes over enumerated states
|
||||
fn as_slice(&self) -> &[usize] {
|
||||
self.indices.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasRefCnt for SysGraphMetadata {
|
||||
fn refcnt(&self) -> isize {
|
||||
self.tcref
|
||||
}
|
||||
|
||||
fn refcnt_mut(&mut self) -> &mut isize {
|
||||
&mut self.tcref
|
||||
}
|
||||
}
|
||||
|
||||
libafl::impl_serdeany!(SysGraphMetadata);
|
||||
|
||||
pub type GraphMaximizerCorpusScheduler<CS, I, S> =
|
||||
MinimizerCorpusScheduler<CS, MaxExecsLenFavFactor<I>, I, SysGraphMetadata, S>;
|
||||
|
||||
//============================= Graph Feedback
|
||||
|
||||
/// Improved System State Graph
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct SysGraphFeedbackState
|
||||
{
|
||||
pub graph: DiGraph<SysGraphNode, ()>,
|
||||
entrypoint: NodeIndex,
|
||||
exit: NodeIndex,
|
||||
name: String,
|
||||
}
|
||||
impl SysGraphFeedbackState
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
let mut graph = DiGraph::<SysGraphNode, ()>::new();
|
||||
let mut entry = SysGraphNode::default();
|
||||
entry.base.current_task.task_name="Start".to_string();
|
||||
let mut exit = SysGraphNode::default();
|
||||
exit.base.current_task.task_name="End".to_string();
|
||||
let entry = graph.add_node(entry);
|
||||
let exit = graph.add_node(exit);
|
||||
Self {graph: graph, entrypoint: entry, exit: exit, name: String::from("SysMap")}
|
||||
}
|
||||
fn insert(&mut self, list: Vec<RefinedFreeRTOSSystemState>, input: &Vec<u8>) {
|
||||
let mut current_index = self.entrypoint;
|
||||
for n in list {
|
||||
let mut done = false;
|
||||
for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) {
|
||||
if n == self.graph[i].base {
|
||||
done = true;
|
||||
current_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if !done {
|
||||
let j = self.graph.add_node(SysGraphNode::from(n,input.clone()));
|
||||
self.graph.add_edge(current_index, j, ());
|
||||
current_index = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// Try adding a system state path from a [Vec<RefinedFreeRTOSSystemState>], return true if the path was interesting
|
||||
fn update(&mut self, list: &Vec<RefinedFreeRTOSSystemState>, input: &Vec<u8>) -> (bool, Vec<NodeIndex>) {
|
||||
let mut current_index = self.entrypoint;
|
||||
let mut novel = false;
|
||||
let mut trace : Vec<NodeIndex> = vec![current_index];
|
||||
for n in list {
|
||||
let mut matching : Option<NodeIndex> = None;
|
||||
for i in self.graph.neighbors_directed(current_index, Direction::Outgoing) {
|
||||
let tmp = &self.graph[i];
|
||||
if n == &tmp.base {
|
||||
matching = Some(i);
|
||||
current_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
match matching {
|
||||
None => {
|
||||
novel = true;
|
||||
let j = self.graph.add_node(SysGraphNode::from(n.clone(),input.clone()));
|
||||
self.graph.add_edge(current_index, j, ());
|
||||
current_index = j;
|
||||
},
|
||||
Some(i) => {
|
||||
novel |= self.graph[i].unite_interesting(&n, input);
|
||||
}
|
||||
}
|
||||
trace.push(current_index);
|
||||
}
|
||||
self.graph.update_edge(current_index, self.exit, ()); // every path ends in the exit noded
|
||||
return (novel, trace);
|
||||
}
|
||||
}
|
||||
impl Named for SysGraphFeedbackState
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
impl FeedbackState for SysGraphFeedbackState
|
||||
{
|
||||
fn reset(&mut self) -> Result<(), Error> {
|
||||
self.graph.clear();
|
||||
let mut entry = SysGraphNode::default();
|
||||
entry.base.current_task.task_name="Start".to_string();
|
||||
let mut exit = SysGraphNode::default();
|
||||
exit.base.current_task.task_name="End".to_string();
|
||||
self.entrypoint = self.graph.add_node(entry);
|
||||
self.exit = self.graph.add_node(exit);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSysStateObserver`]
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct SysMapFeedback
|
||||
{
|
||||
name: String,
|
||||
last_trace: Option<Vec<NodeIndex>>,
|
||||
}
|
||||
impl SysMapFeedback {
|
||||
pub fn new() -> Self {
|
||||
Self {name: String::from("SysMapFeedback"), last_trace: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Feedback<I, S> for SysMapFeedback
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfMonitor + HasMetadata,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &I,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
let observer = observers.match_name::<QemuSysStateObserver>("sysstate")
|
||||
.expect("QemuSysStateObserver not found");
|
||||
let feedbackstate = state
|
||||
.feedback_states_mut()
|
||||
.match_name_mut::<SysGraphFeedbackState>("SysMap")
|
||||
.unwrap();
|
||||
let ret = feedbackstate.update(&observer.last_run, &observer.last_input);
|
||||
self.last_trace = Some(ret.1);
|
||||
Ok(ret.0)
|
||||
}
|
||||
|
||||
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||
#[inline]
|
||||
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
|
||||
let a = self.last_trace.take();
|
||||
match a {
|
||||
Some(s) => testcase.metadata_mut().insert(SysGraphMetadata::new(s)),
|
||||
None => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||
#[inline]
|
||||
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
|
||||
self.last_trace = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl Named for SysMapFeedback
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
//============================= Mutators
|
||||
//=============================== Snippets
|
||||
pub struct RandGraphSnippetMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||
{
|
||||
phantom: PhantomData<(I, S)>,
|
||||
}
|
||||
impl<I, S> RandGraphSnippetMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
RandGraphSnippetMutator{phantom: PhantomData}
|
||||
}
|
||||
}
|
||||
impl<I, S> Mutator<I, S> for RandGraphSnippetMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
_stage_idx: i32
|
||||
) -> Result<MutationResult, Error>
|
||||
{
|
||||
// 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 feedbackstate = state
|
||||
.feedback_states()
|
||||
.match_name::<SysGraphFeedbackState>("SysMap")
|
||||
.unwrap();
|
||||
let g = &feedbackstate.graph;
|
||||
let tmp = state.metadata().get::<SysGraphMetadata>();
|
||||
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");
|
||||
// follow the path, extract snippets from last reads, find common snippets.
|
||||
// those are likley keys parts. choose random parts from other sibling traces
|
||||
let sibling_inputs : Vec<&Vec<u8>>= g[*trace.inner.last().unwrap()].variants.iter().map(|x| &x.input).collect();
|
||||
let mut snippet_collector = vec![];
|
||||
let mut per_input_counters = HashMap::<&Vec<u8>,usize>::new(); // ugly workaround to track multiple inputs
|
||||
for t in &trace.inner {
|
||||
let node = &g[*t];
|
||||
let mut per_node_snippets = HashMap::<&Vec<u8>,&[u8]>::new();
|
||||
for v in &node.variants {
|
||||
match per_input_counters.get_mut(&v.input) {
|
||||
None => {
|
||||
if sibling_inputs.iter().any(|x| *x==&v.input) { // only collect info about siblin inputs from target
|
||||
per_input_counters.insert(&v.input, v.input_counter.try_into().unwrap());
|
||||
}
|
||||
},
|
||||
Some(x) => {
|
||||
let x_u = *x;
|
||||
if x_u<v.input_counter as usize {
|
||||
*x=v.input_counter as usize;
|
||||
per_node_snippets.insert(&v.input,&v.input[x_u..v.input_counter as usize]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
snippet_collector.push(per_node_snippets);
|
||||
}
|
||||
let mut new_input : Vec<u8> = vec![];
|
||||
for c in snippet_collector {
|
||||
new_input.extend_from_slice(myrand.choose(c).1);
|
||||
}
|
||||
for i in new_input.iter().enumerate() {
|
||||
input.bytes_mut()[i.0]=*i.1;
|
||||
}
|
||||
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
|
||||
fn post_exec(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_stage_idx: i32,
|
||||
_corpus_idx: Option<usize>
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Named for RandGraphSnippetMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"RandGraphSnippetMutator"
|
||||
}
|
||||
}
|
||||
//=============================== Snippets
|
||||
pub struct RandInputSnippetMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||
{
|
||||
phantom: PhantomData<(I, S)>,
|
||||
}
|
||||
impl<I, S> RandInputSnippetMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
RandInputSnippetMutator{phantom: PhantomData}
|
||||
}
|
||||
}
|
||||
impl<I, S> Mutator<I, S> for RandInputSnippetMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
_stage_idx: i32
|
||||
) -> Result<MutationResult, Error>
|
||||
{
|
||||
// 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 feedbackstate = state
|
||||
.feedback_states()
|
||||
.match_name::<SysGraphFeedbackState>("SysMap")
|
||||
.unwrap();
|
||||
let g = &feedbackstate.graph;
|
||||
let tmp = state.metadata().get::<SysGraphMetadata>();
|
||||
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 mut collection : Vec<Vec<u8>> = Vec::new();
|
||||
let mut current_pointer : usize = 0;
|
||||
for t in &trace.inner {
|
||||
let node = &g[*t];
|
||||
for v in &node.variants {
|
||||
if v.input == input.bytes() {
|
||||
if v.input_counter > current_pointer.try_into().unwrap() {
|
||||
collection.push(v.input[current_pointer..v.input_counter as usize].to_owned());
|
||||
current_pointer = v.input_counter as usize;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
let index_to_mutate = myrand.below(collection.len() as u64) as usize;
|
||||
for i in 0..collection[index_to_mutate].len() {
|
||||
collection[index_to_mutate][i] = myrand.below(0xFF) as u8;
|
||||
}
|
||||
for i in collection.concat().iter().enumerate() {
|
||||
input.bytes_mut()[i.0]=*i.1;
|
||||
}
|
||||
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
|
||||
fn post_exec(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_stage_idx: i32,
|
||||
_corpus_idx: Option<usize>
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Named for RandInputSnippetMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"RandInputSnippetMutator"
|
||||
}
|
||||
}
|
||||
//=============================== Suffix
|
||||
pub struct RandGraphSuffixMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||
{
|
||||
phantom: PhantomData<(I, S)>,
|
||||
}
|
||||
impl<I, S> RandGraphSuffixMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
RandGraphSuffixMutator{phantom: PhantomData}
|
||||
}
|
||||
}
|
||||
impl<I, S> Mutator<I, S> for RandGraphSuffixMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
_stage_idx: i32
|
||||
) -> Result<MutationResult, Error>
|
||||
{
|
||||
// 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 feedbackstate = state
|
||||
.feedback_states()
|
||||
.match_name::<SysGraphFeedbackState>("SysMap")
|
||||
.unwrap();
|
||||
let g = &feedbackstate.graph;
|
||||
let tmp = state.metadata().get::<SysGraphMetadata>();
|
||||
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");
|
||||
// follow the path, extract snippets from last reads, find common snippets.
|
||||
// those are likley keys parts. choose random parts from other sibling traces
|
||||
let inp_c_end = g[*trace.inner.last().unwrap()].base.input_counter;
|
||||
let mut num_to_reverse = myrand.below(trace.inner.len().try_into().unwrap());
|
||||
for t in trace.inner.iter().rev() {
|
||||
let int_c_prefix = g[*t].base.input_counter;
|
||||
if int_c_prefix < inp_c_end {
|
||||
num_to_reverse-=1;
|
||||
if num_to_reverse<=0 {
|
||||
let mut new_input=input.bytes()[..(int_c_prefix as usize)].to_vec();
|
||||
let mut ext : Vec<u8> = (int_c_prefix..inp_c_end).map(|_| myrand.next().to_le_bytes()).flatten().collect();
|
||||
new_input.append(&mut ext);
|
||||
for i in new_input.iter().enumerate() {
|
||||
if input.bytes_mut().len()>i.0 {
|
||||
input.bytes_mut()[i.0]=*i.1;
|
||||
}
|
||||
else { break };
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(MutationResult::Mutated)
|
||||
}
|
||||
|
||||
fn post_exec(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_stage_idx: i32,
|
||||
_corpus_idx: Option<usize>
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Named for RandGraphSuffixMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"RandGraphSuffixMutator"
|
||||
}
|
||||
}
|
163
fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs
Normal file
163
fuzzers/wcet_qemu_sys/src/sysstate/helpers.rs
Normal file
@ -0,0 +1,163 @@
|
||||
use libafl::prelude::UsesInput;
|
||||
use libafl_qemu::edges::QemuEdgesMapMetadata;
|
||||
use crate::sysstate::RawFreeRTOSSystemState;
|
||||
use crate::sysstate::CURRENT_SYSSTATE_VEC;
|
||||
use crate::sysstate::NUM_PRIOS;
|
||||
use super::freertos::TCB_t;
|
||||
use super::freertos::rtos_struct::List_Item_struct;
|
||||
use super::freertos::rtos_struct::*;
|
||||
use super::freertos;
|
||||
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
|
||||
|
||||
use libafl_qemu::{
|
||||
emu::Emulator,
|
||||
executor::QemuExecutor,
|
||||
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
||||
edges::SAVED_JUMP,
|
||||
};
|
||||
|
||||
//============================= Struct definitions
|
||||
|
||||
pub static mut INTR_OFFSET : Option<u64> = None;
|
||||
pub static mut INTR_DONE : bool = true;
|
||||
|
||||
//============================= Qemu Helper
|
||||
|
||||
/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur
|
||||
#[derive(Debug)]
|
||||
pub struct QemuSystemStateHelper {
|
||||
filter: QemuInstrumentationFilter,
|
||||
tcb_addr: u32,
|
||||
ready_queues: u32,
|
||||
input_counter: u32,
|
||||
}
|
||||
|
||||
impl QemuSystemStateHelper {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
tcb_addr: u32,
|
||||
ready_queues: u32,
|
||||
input_counter: u32
|
||||
) -> Self {
|
||||
Self {
|
||||
filter: QemuInstrumentationFilter::None,
|
||||
tcb_addr: tcb_addr,
|
||||
ready_queues: ready_queues,
|
||||
input_counter: input_counter,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn with_instrumentation_filter(
|
||||
filter: QemuInstrumentationFilter,
|
||||
tcb_addr: u32,
|
||||
ready_queues: u32,
|
||||
input_counter: u32
|
||||
) -> Self {
|
||||
Self { filter, tcb_addr, ready_queues, input_counter}
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn must_instrument(&self, addr: u64) -> bool {
|
||||
self.filter.allowed(addr)
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> QemuHelper<S> for QemuSystemStateHelper
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn init_hooks<QT>(&self, executor: &QemuExecutor<QT>)
|
||||
where
|
||||
QT: QemuHelperTuple<S>,
|
||||
{
|
||||
// emu::Emulator{_private: ()}.set_gen_block_hook(test_gen_hook);
|
||||
executor.hook_block_generation(gen_not_exec_block_hook::<I, QT, S>);
|
||||
executor.hook_block_execution(exec_syscall_hook::<I, QT, S>);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_syscall_hook<I, QT, S>(
|
||||
emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
state: &mut S,
|
||||
pc: u64,
|
||||
)
|
||||
where
|
||||
S: HasMetadata,
|
||||
I: Input,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
let h = helpers.match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
|
||||
if !h.must_instrument(pc) {
|
||||
return;
|
||||
}
|
||||
let listbytes : u32 = u32::try_from(std::mem::size_of::<freertos::List_t>()).unwrap();
|
||||
let mut sysstate = RawFreeRTOSSystemState::default();
|
||||
sysstate.qemu_tick = emulator.get_ticks();
|
||||
let mut buf : [u8; 4] = [0,0,0,0];
|
||||
unsafe { emulator.read_mem(h.input_counter.into(), &mut buf) };
|
||||
sysstate.input_counter = u32::from_le_bytes(buf);
|
||||
|
||||
let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr);
|
||||
sysstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr);
|
||||
|
||||
unsafe {
|
||||
match SAVED_JUMP.take() {
|
||||
Some(s) => {
|
||||
sysstate.last_pc = Some(s.0);
|
||||
},
|
||||
None => (),
|
||||
}
|
||||
}
|
||||
// println!("{:?}",std::str::from_utf8(¤t_tcb.pcTaskName));
|
||||
|
||||
for i in 0..NUM_PRIOS {
|
||||
let target : u32 = listbytes*u32::try_from(i).unwrap()+h.ready_queues;
|
||||
sysstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target);
|
||||
// println!("List at {}: {:?}",target, sysstate.prio_ready_lists[i]);
|
||||
let mut next_index = sysstate.prio_ready_lists[i].pxIndex;
|
||||
for _j in 0..sysstate.prio_ready_lists[i].uxNumberOfItems {
|
||||
// always jump over the xListEnd marker
|
||||
if (target..target+listbytes).contains(&next_index) {
|
||||
let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index);
|
||||
let new_next_index=next_item.pxNext;
|
||||
sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item));
|
||||
next_index = new_next_index;
|
||||
}
|
||||
let next_item : freertos::ListItem_t = freertos::emu_lookup::lookup(emulator, next_index);
|
||||
// println!("Item at {}: {:?}",next_index,next_item);
|
||||
assert_eq!(next_item.pvContainer,target);
|
||||
let new_next_index=next_item.pxNext;
|
||||
let next_tcb : TCB_t= freertos::emu_lookup::lookup(emulator,next_item.pvOwner);
|
||||
// println!("TCB at {}: {:?}",next_item.pvOwner,next_tcb);
|
||||
sysstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone()));
|
||||
sysstate.dumping_ground.insert(next_index,List_Item_struct(next_item));
|
||||
next_index=new_next_index;
|
||||
}
|
||||
// Handle edge case where the end marker was not included yet
|
||||
if (target..target+listbytes).contains(&next_index) {
|
||||
let next_item : freertos::MiniListItem_t = freertos::emu_lookup::lookup(emulator, next_index);
|
||||
sysstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item));
|
||||
}
|
||||
}
|
||||
|
||||
unsafe { CURRENT_SYSSTATE_VEC.push(sysstate); }
|
||||
}
|
||||
|
||||
pub fn gen_not_exec_block_hook<I, QT, S>(
|
||||
_emulator: &Emulator,
|
||||
helpers: &mut QT,
|
||||
_state: &mut S,
|
||||
pc: u64,
|
||||
)
|
||||
-> Option<u64> where
|
||||
S: HasMetadata,
|
||||
I: Input,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
let h = helpers.match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
|
||||
if !h.must_instrument(pc) {
|
||||
None
|
||||
} else {Some(1)}
|
||||
}
|
163
fuzzers/wcet_qemu_sys/src/sysstate/mod.rs
Normal file
163
fuzzers/wcet_qemu_sys/src/sysstate/mod.rs
Normal file
@ -0,0 +1,163 @@
|
||||
//! Sysstate referes to the State of a FreeRTOS fuzzing target
|
||||
use std::collections::hash_map::DefaultHasher;
|
||||
use libafl::bolts::HasRefCnt;
|
||||
use libafl::bolts::AsSlice;
|
||||
use std::hash::Hasher;
|
||||
use std::hash::Hash;
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use freertos::TCB_t;
|
||||
|
||||
pub mod freertos;
|
||||
pub mod helpers;
|
||||
pub mod observers;
|
||||
pub mod feedbacks;
|
||||
pub mod graph;
|
||||
pub mod mutators;
|
||||
|
||||
#[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;
|
||||
|
||||
//============================= Struct definitions
|
||||
/// Raw info Dump from Qemu
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
pub struct RawFreeRTOSSystemState {
|
||||
qemu_tick: u64,
|
||||
current_tcb: TCB_t,
|
||||
prio_ready_lists: [freertos::List_t; NUM_PRIOS],
|
||||
dumping_ground: HashMap<u32,freertos::rtos_struct>,
|
||||
input_counter: u32,
|
||||
last_pc: Option<u64>,
|
||||
}
|
||||
/// List of system state dumps from QemuHelpers
|
||||
static mut CURRENT_SYSSTATE_VEC: Vec<RawFreeRTOSSystemState> = vec![];
|
||||
|
||||
/// A reduced version of freertos::TCB_t
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct RefinedTCB {
|
||||
task_name: String,
|
||||
priority: u32,
|
||||
base_priority: u32,
|
||||
mutexes_held: u32,
|
||||
notify_value: u32,
|
||||
notify_state: u8,
|
||||
}
|
||||
|
||||
impl Hash for RefinedTCB {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.task_name.hash(state);
|
||||
// self.priority.hash(state);
|
||||
// self.mutexes_held.hash(state);
|
||||
// self.notify_state.hash(state);
|
||||
// self.notify_value.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl RefinedTCB {
|
||||
pub fn from_tcb(input: &TCB_t) -> Self {
|
||||
unsafe {
|
||||
let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName);
|
||||
let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::<String>();
|
||||
Self {
|
||||
task_name: name,
|
||||
priority: input.uxPriority,
|
||||
base_priority: input.uxBasePriority,
|
||||
mutexes_held: input.uxMutexesHeld,
|
||||
notify_value: input.ulNotifiedValue[0],
|
||||
notify_state: input.ucNotifyState[0],
|
||||
}
|
||||
}
|
||||
}
|
||||
pub fn from_tcb_owned(input: TCB_t) -> Self {
|
||||
unsafe {
|
||||
let tmp = std::mem::transmute::<[i8; 10],[u8; 10]>(input.pcTaskName);
|
||||
let name : String = std::str::from_utf8(&tmp).expect("TCB name was not utf8").chars().filter(|x| *x != '\0').collect::<String>();
|
||||
Self {
|
||||
task_name: name,
|
||||
priority: input.uxPriority,
|
||||
base_priority: input.uxBasePriority,
|
||||
mutexes_held: input.uxMutexesHeld,
|
||||
notify_value: input.ulNotifiedValue[0],
|
||||
notify_state: input.ucNotifyState[0],
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Refined information about the states an execution transitioned between
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
pub struct RefinedFreeRTOSSystemState {
|
||||
start_tick: u64,
|
||||
end_tick: u64,
|
||||
last_pc: Option<u64>,
|
||||
input_counter: u32,
|
||||
current_task: RefinedTCB,
|
||||
ready_list_after: Vec<RefinedTCB>,
|
||||
}
|
||||
impl PartialEq for RefinedFreeRTOSSystemState {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.current_task == other.current_task && self.ready_list_after == other.ready_list_after &&
|
||||
self.last_pc == other.last_pc
|
||||
}
|
||||
}
|
||||
|
||||
impl Hash for RefinedFreeRTOSSystemState {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
self.current_task.hash(state);
|
||||
self.ready_list_after.hash(state);
|
||||
self.last_pc.hash(state);
|
||||
}
|
||||
}
|
||||
impl RefinedFreeRTOSSystemState {
|
||||
fn get_time(&self) -> u64 {
|
||||
self.end_tick-self.start_tick
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
pub struct FreeRTOSSystemStateMetadata {
|
||||
inner: Vec<RefinedFreeRTOSSystemState>,
|
||||
indices: Vec<usize>, // Hashed enumeration of States
|
||||
tcref: isize,
|
||||
}
|
||||
impl FreeRTOSSystemStateMetadata {
|
||||
pub fn new(inner: Vec<RefinedFreeRTOSSystemState>) -> Self{
|
||||
let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect();
|
||||
Self {inner: inner, indices: tmp, tcref: 0}
|
||||
}
|
||||
}
|
||||
pub fn compute_hash<T>(obj: T) -> u64
|
||||
where
|
||||
T: Hash
|
||||
{
|
||||
let mut s = DefaultHasher::new();
|
||||
obj.hash(&mut s);
|
||||
s.finish()
|
||||
}
|
||||
|
||||
impl AsSlice<usize> for FreeRTOSSystemStateMetadata {
|
||||
/// Convert the slice of system-states to a slice of hashes over enumerated states
|
||||
fn as_slice(&self) -> &[usize] {
|
||||
self.indices.as_slice()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasRefCnt for FreeRTOSSystemStateMetadata {
|
||||
fn refcnt(&self) -> isize {
|
||||
self.tcref
|
||||
}
|
||||
|
||||
fn refcnt_mut(&mut self) -> &mut isize {
|
||||
&mut self.tcref
|
||||
}
|
||||
}
|
||||
|
||||
libafl::impl_serdeany!(FreeRTOSSystemStateMetadata);
|
123
fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs
Normal file
123
fuzzers/wcet_qemu_sys/src/sysstate/mutators.rs
Normal file
@ -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<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||
{
|
||||
phantom: PhantomData<(I, S)>,
|
||||
}
|
||||
impl<I, S> InterruptShifterMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I>,
|
||||
{
|
||||
pub fn new() -> Self {
|
||||
InterruptShifterMutator{phantom: PhantomData}
|
||||
}
|
||||
}
|
||||
impl<I, S> Mutator<I, S> for InterruptShifterMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||
{
|
||||
fn mutate(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
input: &mut I,
|
||||
_stage_idx: i32
|
||||
) -> Result<MutationResult, Error>
|
||||
{
|
||||
// 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::<FreeRTOSSystemStateMetadata>();
|
||||
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::<SysGraphFeedbackState>("SysMap")
|
||||
.unwrap();
|
||||
let g = &feedbackstate.graph;
|
||||
let tmp = state.metadata().get::<SysGraphMetadata>();
|
||||
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<usize>
|
||||
) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> Named for InterruptShifterMutator<I, S>
|
||||
where
|
||||
I: Input + HasBytesVec,
|
||||
S: HasRand + HasMetadata + HasCorpus<I> + HasSolutions<I> + HasFeedbackStates,
|
||||
{
|
||||
fn name(&self) -> &str {
|
||||
"InterruptShifterMutator"
|
||||
}
|
||||
}
|
139
fuzzers/wcet_qemu_sys/src/sysstate/observers.rs
Normal file
139
fuzzers/wcet_qemu_sys/src/sysstate/observers.rs
Normal file
@ -0,0 +1,139 @@
|
||||
use crate::sysstate::IRQ_INPUT_BYTES_NUMBER;
|
||||
use libafl::inputs::HasTargetBytes;
|
||||
use libafl::bolts::HasLen;
|
||||
use libafl::bolts::tuples::Named;
|
||||
use libafl::Error;
|
||||
use libafl::observers::Observer;
|
||||
use hashbrown::HashMap;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use super::{
|
||||
CURRENT_SYSSTATE_VEC,
|
||||
RawFreeRTOSSystemState,
|
||||
RefinedTCB,
|
||||
RefinedFreeRTOSSystemState,
|
||||
freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*},
|
||||
};
|
||||
|
||||
//============================= Observer
|
||||
|
||||
/// The QemuSysState Observer retrieves the SysState
|
||||
/// that will get updated by the target.
|
||||
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||
#[allow(clippy::unsafe_derive_deserialize)]
|
||||
pub struct QemuSysStateObserver
|
||||
{
|
||||
pub last_run: Vec<RefinedFreeRTOSSystemState>,
|
||||
pub last_input: Vec<u8>,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl<I, S> Observer<I, S> for QemuSysStateObserver
|
||||
where
|
||||
I: HasTargetBytes
|
||||
{
|
||||
#[inline]
|
||||
fn pre_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
|
||||
unsafe {CURRENT_SYSSTATE_VEC.clear(); }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn post_exec(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
|
||||
unsafe {self.last_run = refine_system_states(&mut CURRENT_SYSSTATE_VEC);}
|
||||
self.last_input=_input.target_bytes().as_slice().to_owned();
|
||||
// let mut hasher = DefaultHasher::new();
|
||||
// let mut a = self.parse_last();
|
||||
// a[0].start_tick=21355;
|
||||
// a[0].end_tick=2131;
|
||||
// a.hash(&mut hasher);
|
||||
// let somehash = hasher.finish();
|
||||
// println!("HashValue: {}",somehash);
|
||||
// println!("{:#?}",self.parse_last());
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for QemuSysStateObserver
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl HasLen for QemuSysStateObserver
|
||||
{
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.last_run.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl QemuSysStateObserver {
|
||||
pub fn new() -> Self {
|
||||
Self{last_run: vec![], last_input: vec![], name: "sysstate".to_string()}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//============================= Parsing helpers
|
||||
|
||||
/// Parse a List_t containing TCB_t into Vec<TCB_t> from cache. Consumes the elements from cache
|
||||
fn tcb_list_to_vec_cached(list: List_t, dump: &mut HashMap<u32,rtos_struct>) -> Vec<TCB_t>
|
||||
{
|
||||
let mut ret : Vec<TCB_t> = Vec::new();
|
||||
if list.uxNumberOfItems == 0 {return ret;}
|
||||
let last_list_item = match dump.remove(&list.pxIndex).expect("List_t entry was not in Hashmap") {
|
||||
List_Item_struct(li) => li,
|
||||
List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") {
|
||||
List_Item_struct(li) => li,
|
||||
_ => panic!("MiniListItem of a non empty List does not point to ListItem"),
|
||||
},
|
||||
_ => panic!("List_t entry was not a ListItem"),
|
||||
};
|
||||
let mut next_index = last_list_item.pxNext;
|
||||
let last_tcb = match dump.remove(&last_list_item.pvOwner).expect("ListItem Owner not in Hashmap") {
|
||||
TCB_struct(t) => t,
|
||||
_ => panic!("List content does not equal type"),
|
||||
};
|
||||
for _ in 0..list.uxNumberOfItems-1 {
|
||||
let next_list_item = match dump.remove(&next_index).expect("List_t entry was not in Hashmap") {
|
||||
List_Item_struct(li) => li,
|
||||
List_MiniItem_struct(mli) => match dump.remove(&mli.pxNext).expect("MiniListItem pointer invaild") {
|
||||
List_Item_struct(li) => li,
|
||||
_ => panic!("MiniListItem of a non empty List does not point to ListItem"),
|
||||
},
|
||||
_ => panic!("List_t entry was not a ListItem"),
|
||||
};
|
||||
match dump.remove(&next_list_item.pvOwner).expect("ListItem Owner not in Hashmap") {
|
||||
TCB_struct(t) => {ret.push(t)},
|
||||
_ => panic!("List content does not equal type"),
|
||||
}
|
||||
next_index=next_list_item.pxNext;
|
||||
}
|
||||
ret.push(last_tcb);
|
||||
ret
|
||||
}
|
||||
/// Drains a List of raw SystemStates to produce a refined trace
|
||||
fn refine_system_states(input: &mut Vec<RawFreeRTOSSystemState>) -> Vec<RefinedFreeRTOSSystemState> {
|
||||
let mut ret = Vec::<RefinedFreeRTOSSystemState>::new();
|
||||
let mut start_tick : u64 = 0;
|
||||
for mut i in input.drain(..) {
|
||||
let mut collector = Vec::<RefinedTCB>::new();
|
||||
for j in i.prio_ready_lists.into_iter().rev() {
|
||||
let mut tmp = tcb_list_to_vec_cached(j,&mut i.dumping_ground).iter().map(|x| RefinedTCB::from_tcb(x)).collect();
|
||||
collector.append(&mut tmp);
|
||||
}
|
||||
ret.push(RefinedFreeRTOSSystemState {
|
||||
current_task: RefinedTCB::from_tcb_owned(i.current_tcb),
|
||||
start_tick: start_tick,
|
||||
end_tick: i.qemu_tick,
|
||||
ready_list_after: collector,
|
||||
input_counter: i.input_counter+IRQ_INPUT_BYTES_NUMBER,
|
||||
last_pc: i.last_pc,
|
||||
});
|
||||
start_tick=i.qemu_tick;
|
||||
}
|
||||
return ret;
|
||||
}
|
763
fuzzers/wcet_qemu_sys/src/worst.rs
Normal file
763
fuzzers/wcet_qemu_sys/src/worst.rs
Normal file
@ -0,0 +1,763 @@
|
||||
use libafl::feedbacks::FeedbackState;
|
||||
use libafl_qemu::QemuClockObserver;
|
||||
use crate::sysstate::FreeRTOSSystemStateMetadata;
|
||||
use num_traits::PrimInt;
|
||||
use core::fmt::Debug;
|
||||
use core::cmp::Ordering::{Greater,Less,Equal};
|
||||
use libafl::inputs::BytesInput;
|
||||
use libafl::inputs::HasTargetBytes;
|
||||
use libafl::feedbacks::MapIndexesMetadata;
|
||||
use libafl::corpus::Testcase;
|
||||
use libafl::corpus::FavFactor;
|
||||
use core::marker::PhantomData;
|
||||
use libafl::corpus::MinimizerCorpusScheduler;
|
||||
use std::path::PathBuf;
|
||||
use std::fs;
|
||||
use hashbrown::{HashMap};
|
||||
use libafl::observers::ObserversTuple;
|
||||
use libafl::executors::ExitKind;
|
||||
use libafl::events::EventFirer;
|
||||
use libafl::state::HasClientPerfMonitor;
|
||||
use libafl::inputs::Input;
|
||||
use libafl::feedbacks::Feedback;
|
||||
use libafl::state::HasMetadata;
|
||||
use libafl_qemu::edges::QemuEdgesMapMetadata;
|
||||
use libafl::observers::MapObserver;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::cmp;
|
||||
|
||||
use libafl::{
|
||||
bolts::{
|
||||
tuples::Named,
|
||||
HasLen,
|
||||
},
|
||||
observers::Observer,
|
||||
Error,
|
||||
};
|
||||
//===================================================================
|
||||
/// A wrapper around some other [`MapObserver`], using [`QemuEdgesMapMetadata`] to offer a convinient Hashmap.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(bound = "M: serde::de::DeserializeOwned")]
|
||||
pub struct QemuHashMapObserver<M,T>
|
||||
where
|
||||
M: serde::Serialize + serde::de::DeserializeOwned,
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
{
|
||||
base: M,
|
||||
pub edgemap: HashMap<(u64,u64),T>,
|
||||
}
|
||||
|
||||
impl<I, S, M, T> Observer<I, S> for QemuHashMapObserver<M, T>
|
||||
where
|
||||
M: MapObserver<T> + Observer<I, S>,
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
S: HasMetadata, // Need to grab the HashMap from a Helper
|
||||
{
|
||||
#[inline]
|
||||
fn pre_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
|
||||
self.edgemap = HashMap::new();
|
||||
self.base.pre_exec(state, input)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn post_exec(&mut self, state: &mut S, input: &I) -> Result<(), Error> {
|
||||
let original_hashmap=&state.metadata().get::<QemuEdgesMapMetadata>().expect("QemuEdgesMapMetadata not found").map;
|
||||
for (key, val) in original_hashmap.iter() {
|
||||
self.edgemap.insert(*key,*self.base.get(*val as usize));
|
||||
}
|
||||
self.base.post_exec(state, input)
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, T> Named for QemuHashMapObserver<M, T>
|
||||
where
|
||||
M: Named + serde::Serialize + serde::de::DeserializeOwned,
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
self.base.name()
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, T> HasLen for QemuHashMapObserver<M, T>
|
||||
where
|
||||
M: MapObserver<T>,
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
{
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
self.base.len()
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, T> MapObserver<T> for QemuHashMapObserver<M, T>
|
||||
where
|
||||
M: MapObserver<T>,
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
{
|
||||
#[inline]
|
||||
fn map(&self) -> Option<&[T]> {
|
||||
self.base.map()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn map_mut(&mut self) -> Option<&mut [T]> {
|
||||
self.base.map_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn usable_count(&self) -> usize {
|
||||
self.base.usable_count()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initial(&self) -> T {
|
||||
self.base.initial()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn initial_mut(&mut self) -> &mut T {
|
||||
self.base.initial_mut()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn set_initial(&mut self, initial: T) {
|
||||
self.base.set_initial(initial);
|
||||
}
|
||||
}
|
||||
|
||||
impl<M, T> QemuHashMapObserver<M, T>
|
||||
where
|
||||
M: serde::Serialize + serde::de::DeserializeOwned,
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
{
|
||||
/// Creates a new [`MapObserver`]
|
||||
pub fn new(base: M) -> Self {
|
||||
Self { edgemap:HashMap::new(), base }
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
|
||||
/// A [`HitFeedback`] reports as interesting when all predicted worst case edges have been matched.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||
pub struct HitFeedback<O, T>
|
||||
where
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
target_map: HashMap<(u64,u64),T>,
|
||||
target_msd: f64,
|
||||
phantom: PhantomData<(O, T)>,
|
||||
}
|
||||
|
||||
impl<I, S, T, O> Feedback<I, S> for HitFeedback<O, T>
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfMonitor,
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &I,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
let observer = _observers.match_name::<QemuHashMapObserver<O, T>>("edges")
|
||||
.expect("QemuHashMapObserver not found");
|
||||
if self.target_map.len() == 0 { return Ok(false) };
|
||||
|
||||
let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target
|
||||
for (edg, val) in &self.target_map {
|
||||
match observer.edgemap.get(&edg) {
|
||||
Some(x) => {
|
||||
sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)).to_u64().unwrap()).pow(2);
|
||||
},
|
||||
None => sum_of_square_difference+=((*val).to_u64().unwrap()).pow(2),
|
||||
}
|
||||
}
|
||||
let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64);
|
||||
let hit_target = mean_sum_of_squares <= self.target_msd;
|
||||
if hit_target {
|
||||
// #[cfg(debug_assertions)] eprintln!("Hit Feedback trigger");
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<O, T> Named for HitFeedback<O, T>
|
||||
where
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"HitFeedback"
|
||||
}
|
||||
}
|
||||
|
||||
impl<O, T> HitFeedback<O, T>
|
||||
where
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
/// Creates a new [`HitFeedback`]
|
||||
#[must_use]
|
||||
pub fn new(target_map: HashMap<(u64,u64),T>, target_msd: f64, _map_observer: &QemuHashMapObserver<O, T>) -> Self {
|
||||
Self {target_map: target_map, target_msd: target_msd, phantom: PhantomData}
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
|
||||
// /// A [`MapHitIncreaseFeedback`] reports as interesting when the total number of used edges increases.
|
||||
// #[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
// pub struct MapHitIncreaseFeedback {
|
||||
// record_high : u64,
|
||||
// }
|
||||
|
||||
// impl<I, S, O, T> Feedback<I, S> for MapHitIncreaseFeedback
|
||||
// where
|
||||
// I: Input,
|
||||
// S: HasClientPerfMonitor,
|
||||
// T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
// O: MapObserver<T>,
|
||||
// {
|
||||
// fn is_interesting<EM, OT>(
|
||||
// &mut self,
|
||||
// _state: &mut S,
|
||||
// _manager: &mut EM,
|
||||
// _input: &I,
|
||||
// _observers: &OT,
|
||||
// _exit_kind: &ExitKind,
|
||||
// ) -> Result<bool, Error>
|
||||
// where
|
||||
// EM: EventFirer<I>,
|
||||
// OT: ObserversTuple<I, S>,
|
||||
// {
|
||||
// let observer = _observers.match_name::<QemuHashMapObserver<O, T>>("edges")
|
||||
// .expect("QemuHashMapObserver not found");
|
||||
// let cur = observer.edgemap.values().fold(0,|a,b| a+(*b as u64));
|
||||
// if cur > self.record_high {
|
||||
// self.record_high = cur;
|
||||
// return Ok(true);
|
||||
// }
|
||||
// return Ok(false);
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Named for MapHitIncreaseFeedback {
|
||||
// #[inline]
|
||||
// fn name(&self) -> &str {
|
||||
// "HitFeedback"
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl MapHitIncreaseFeedback {
|
||||
// /// Creates a new [`HitFeedback`]
|
||||
// #[must_use]
|
||||
// pub fn new() -> Self {
|
||||
// Self {record_high: 0}
|
||||
// }
|
||||
// }
|
||||
|
||||
// impl Default for MapHitIncreaseFeedback {
|
||||
// fn default() -> Self {
|
||||
// Self::new()
|
||||
// }
|
||||
// }
|
||||
|
||||
//===================================================================
|
||||
|
||||
|
||||
/// A [`HitFeedback`] reports as interesting when all predicted worst case edges have been matched.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||
pub struct HitImprovingFeedback<O, T>
|
||||
where
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
target_map: HashMap<(u64,u64),T>,
|
||||
best_msd: f64,
|
||||
phantom: PhantomData<(O, T)>,
|
||||
}
|
||||
|
||||
impl<O, T, I, S> Feedback<I, S> for HitImprovingFeedback<O, T>
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfMonitor,
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &I,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
let observer = _observers.match_name::<QemuHashMapObserver<O, T>>("edges")
|
||||
.expect("QemuHashMapObserver not found");
|
||||
if self.target_map.len() == 0 { return Ok(false) };
|
||||
|
||||
let mut sum_of_square_difference : u64 = 0; // does not include found edges not in target
|
||||
for (edg, val) in &self.target_map {
|
||||
match observer.edgemap.get(&edg) {
|
||||
Some(x) => {
|
||||
sum_of_square_difference+=((cmp::max(*x,*val)-cmp::min(*x,*val)).to_u64().unwrap()).pow(2);
|
||||
},
|
||||
None => sum_of_square_difference+=((*val).to_u64().unwrap()).pow(2),
|
||||
}
|
||||
}
|
||||
let mean_sum_of_squares = (sum_of_square_difference as f64) / (self.target_map.len() as f64);
|
||||
let hit_target = mean_sum_of_squares < self.best_msd;
|
||||
if hit_target {
|
||||
// #[cfg(debug_assertions)] eprintln!("Hit Improving: {}",mean_sum_of_squares);
|
||||
self.best_msd = mean_sum_of_squares;
|
||||
Ok(true)
|
||||
} else {
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<O, T> Named for HitImprovingFeedback<O, T>
|
||||
where
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"HitImprovingFeedback"
|
||||
}
|
||||
}
|
||||
|
||||
impl<O, T> HitImprovingFeedback<O, T>
|
||||
where
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
/// Creates a new [`HitImprovingFeedback`]
|
||||
#[must_use]
|
||||
pub fn new(target_map: HashMap<(u64,u64),T>, _map_observer: &QemuHashMapObserver<O, T>) -> Self {
|
||||
Self {target_map: target_map, best_msd: f64::MAX, phantom: PhantomData}
|
||||
}
|
||||
}
|
||||
|
||||
//=========================== Debugging Feedback
|
||||
#[derive(Debug, Default, Serialize, Deserialize, Clone)]
|
||||
pub struct DumpEdgesMapMetadata
|
||||
{
|
||||
pub map: HashMap<(u64, u64), usize>,
|
||||
}
|
||||
|
||||
impl DumpEdgesMapMetadata
|
||||
{
|
||||
#[must_use]
|
||||
pub fn new(input: HashMap<(u64,u64),usize>) -> Self {
|
||||
Self {
|
||||
map: input,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
libafl::impl_serdeany!(DumpEdgesMapMetadata);
|
||||
/// A [`Feedback`] meant to dump the edgemap for debugging.
|
||||
#[derive(Debug)]
|
||||
pub struct DumpMapFeedback<O, T>
|
||||
where
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
dumpfile: Option<PathBuf>,
|
||||
phantom: PhantomData<(O, T)>,
|
||||
dump_metadata: bool,
|
||||
last_map: Option<HashMap<(u64,u64),T>>,
|
||||
}
|
||||
|
||||
impl<I, S, O, T> Feedback<I, S> for DumpMapFeedback<O, T>
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfMonitor,
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &I,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
let observer = _observers.match_name::<QemuHashMapObserver<O, T>>("edges")
|
||||
.expect("QemuHashMapObserver not found");
|
||||
match &self.dumpfile {
|
||||
Some(s) => {
|
||||
fs::write(s,ron::to_string(&observer.edgemap).expect("Error serializing hashmap")).expect("Can not dump to file");
|
||||
self.dumpfile = None
|
||||
},
|
||||
None => if !self.dump_metadata {println!("{:?}",observer.edgemap);}
|
||||
};
|
||||
if self.dump_metadata {self.last_map=Some(observer.edgemap.clone());}
|
||||
Ok(!self.dump_metadata)
|
||||
}
|
||||
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||
#[inline]
|
||||
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<I>) -> Result<(), Error> {
|
||||
if !self.dump_metadata {return Ok(());}
|
||||
let a = self.last_map.take();
|
||||
match a {
|
||||
Some(s) => {
|
||||
let mut tmp : HashMap<(u64,u64),usize> = HashMap::new();
|
||||
for (k, v) in s.iter() {
|
||||
tmp.insert(*k, T::to_usize(v).unwrap());
|
||||
}
|
||||
testcase.metadata_mut().insert(DumpEdgesMapMetadata::new(tmp));
|
||||
}
|
||||
None => (),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||
#[inline]
|
||||
fn discard_metadata(&mut self, _state: &mut S, _input: &I) -> Result<(), Error> {
|
||||
self.last_map = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<O, T> Named for DumpMapFeedback<O, T>
|
||||
where
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"HitFeedback"
|
||||
}
|
||||
}
|
||||
|
||||
impl<O, T> DumpMapFeedback<O, T>
|
||||
where
|
||||
T: PrimInt + Default + Copy + 'static + Serialize + serde::de::DeserializeOwned + Debug,
|
||||
O: MapObserver<T>,
|
||||
{
|
||||
/// Creates a new [`HitFeedback`]
|
||||
#[must_use]
|
||||
pub fn new(_map_observer: &QemuHashMapObserver<O, T>) -> Self {
|
||||
Self {dumpfile: None, phantom: PhantomData, dump_metadata: false, last_map: None}
|
||||
}
|
||||
pub fn with_dump(dumpfile: Option<PathBuf>,_map_observer: &QemuHashMapObserver<O, T>) -> Self {
|
||||
Self {dumpfile: dumpfile, phantom: PhantomData, dump_metadata: false, last_map: None}
|
||||
}
|
||||
pub fn metadata_only(_map_observer: &QemuHashMapObserver<O, T>) -> Self {
|
||||
Self {dumpfile: None, phantom: PhantomData, dump_metadata: true, last_map: None}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//=========================== Debugging Feedback
|
||||
/// A NoOp [`Feedback`] with fixed response.
|
||||
#[derive(Debug)]
|
||||
pub struct DummyFeedback {
|
||||
response: bool
|
||||
}
|
||||
|
||||
impl<I, S> Feedback<I, S> for DummyFeedback
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfMonitor,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &I,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
// eprintln!("Input was: {:?}",_input);
|
||||
Ok(self.response)
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for DummyFeedback {
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"DummyFeedback"
|
||||
}
|
||||
}
|
||||
|
||||
impl DummyFeedback {
|
||||
/// Creates a new [`HitFeedback`]
|
||||
#[must_use]
|
||||
pub fn new(response: bool) -> Self {
|
||||
Self {response: response}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DummyFeedback {
|
||||
fn default() -> Self {
|
||||
Self::new(true)
|
||||
}
|
||||
}
|
||||
|
||||
//=========================== Scheduler
|
||||
|
||||
pub type TimeMaximizerCorpusScheduler<CS, I, S> =
|
||||
MinimizerCorpusScheduler<CS, MaxTimeFavFactor<I>, I, MapIndexesMetadata, S>;
|
||||
|
||||
/// Multiply the testcase size with the execution time.
|
||||
/// This favors small and quick testcases.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MaxTimeFavFactor<I>
|
||||
where
|
||||
I: Input + HasLen,
|
||||
{
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<I> FavFactor<I> for MaxTimeFavFactor<I>
|
||||
where
|
||||
I: Input + HasLen,
|
||||
{
|
||||
fn compute(entry: &mut Testcase<I>) -> Result<u64, Error> {
|
||||
// TODO maybe enforce entry.exec_time().is_some()
|
||||
let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()) as u64;
|
||||
Ok(execs_per_hour)
|
||||
}
|
||||
}
|
||||
|
||||
pub type LenTimeMaximizerCorpusScheduler<CS, I, S> =
|
||||
MinimizerCorpusScheduler<CS, MaxExecsLenFavFactor<I>, I, MapIndexesMetadata, S>;
|
||||
|
||||
pub type TimeStateMaximizerCorpusScheduler<CS, I, S> =
|
||||
MinimizerCorpusScheduler<CS, MaxExecsLenFavFactor<I>, I, FreeRTOSSystemStateMetadata, S>;
|
||||
|
||||
/// Multiply the testcase size with the execution time.
|
||||
/// This favors small and quick testcases.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct MaxExecsLenFavFactor<I>
|
||||
where
|
||||
I: Input + HasLen,
|
||||
{
|
||||
phantom: PhantomData<I>,
|
||||
}
|
||||
|
||||
impl<I> FavFactor<I> for MaxExecsLenFavFactor<I>
|
||||
where
|
||||
I: Input + HasLen,
|
||||
{
|
||||
fn compute(entry: &mut Testcase<I>) -> Result<u64, Error> {
|
||||
let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64()) as u64;
|
||||
let execs_times_length_per_hour = execs_per_hour*entry.cached_len()? as u64;
|
||||
Ok(execs_times_length_per_hour)
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
|
||||
/// A Feedback reporting if the Input consists of strictly decreasing bytes.
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct SortedFeedback {
|
||||
}
|
||||
|
||||
impl<S> Feedback<BytesInput, S> for SortedFeedback
|
||||
where
|
||||
S: HasClientPerfMonitor,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &BytesInput,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<BytesInput>,
|
||||
OT: ObserversTuple<BytesInput, S>,
|
||||
{
|
||||
let t = _input.target_bytes();
|
||||
let tmp = t.as_slice();
|
||||
if tmp.len()<32 {return Ok(false);}
|
||||
let tmp = Vec::<u8>::from(&tmp[0..32]);
|
||||
// tmp.reverse();
|
||||
if tmp.is_sorted_by(|a,b| match a.partial_cmp(b).unwrap_or(Less) {
|
||||
Less => Some(Greater),
|
||||
Equal => Some(Greater),
|
||||
Greater => Some(Less),
|
||||
}) {return Ok(true)};
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for SortedFeedback {
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"Sorted"
|
||||
}
|
||||
}
|
||||
|
||||
impl SortedFeedback {
|
||||
/// Creates a new [`HitFeedback`]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for SortedFeedback {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
/// A Feedback which expects a certain minimum execution time
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ExecTimeReachedFeedback
|
||||
{
|
||||
target_time: u64,
|
||||
}
|
||||
|
||||
impl<I, S> Feedback<I, S> for ExecTimeReachedFeedback
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfMonitor,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &I,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
let observer = _observers.match_name::<QemuClockObserver>("clock")
|
||||
.expect("QemuClockObserver not found");
|
||||
Ok(observer.last_runtime() >= self.target_time)
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for ExecTimeReachedFeedback
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"ExecTimeReachedFeedback"
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecTimeReachedFeedback
|
||||
where
|
||||
{
|
||||
/// Creates a new [`ExecTimeReachedFeedback`]
|
||||
#[must_use]
|
||||
pub fn new(target_time : u64) -> Self {
|
||||
Self {target_time: target_time}
|
||||
}
|
||||
}
|
||||
|
||||
pub static mut EXEC_TIME_COLLECTION : Vec<u32> = Vec::new();
|
||||
|
||||
/// A Noop Feedback which records a list of all execution times
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ExecTimeCollectorFeedback
|
||||
{
|
||||
}
|
||||
|
||||
impl<I, S> Feedback<I, S> for ExecTimeCollectorFeedback
|
||||
where
|
||||
I: Input,
|
||||
S: HasClientPerfMonitor,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &I,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<I>,
|
||||
OT: ObserversTuple<I, S>,
|
||||
{
|
||||
let observer = _observers.match_name::<QemuClockObserver>("clock")
|
||||
.expect("QemuClockObserver not found");
|
||||
unsafe { EXEC_TIME_COLLECTION.push(observer.last_runtime().try_into().unwrap()); }
|
||||
Ok(false)
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for ExecTimeCollectorFeedback
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"ExecTimeCollectorFeedback"
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecTimeCollectorFeedback
|
||||
where
|
||||
{
|
||||
/// Creates a new [`ExecTimeCollectorFeedback`]
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shared Metadata for a SysStateFeedback
|
||||
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||
pub struct ExecTimeCollectorFeedbackState
|
||||
{
|
||||
collection: Vec<u32>,
|
||||
}
|
||||
impl Named for ExecTimeCollectorFeedbackState
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
"ExecTimeCollectorFeedbackState"
|
||||
}
|
||||
}
|
||||
impl FeedbackState for ExecTimeCollectorFeedbackState
|
||||
{
|
||||
fn reset(&mut self) -> Result<(), Error> {
|
||||
self.collection.clear();
|
||||
Ok(())
|
||||
}
|
||||
}
|
1
fuzzers/wcet_qemu_sys/tmr_inputs/.gitignore
vendored
Normal file
1
fuzzers/wcet_qemu_sys/tmr_inputs/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.case
|
4
fuzzers/wcet_qemu_sys/tmr_inputs/inputs.sh
Normal file
4
fuzzers/wcet_qemu_sys/tmr_inputs/inputs.sh
Normal file
@ -0,0 +1,4 @@
|
||||
echo "" > best.case
|
||||
echo "\x00\x00" > best_success.case
|
||||
echo "\x05\x29\x07\x1f\x0b\x17\x00\x17" > worst_trace.case
|
||||
echo "\xFF\xF6\xFC\xF8\xFD\xFD\xFF\xFD" > worst.case
|
1
fuzzers/wcet_qemu_sys/tmrint_inputs/.gitignore
vendored
Normal file
1
fuzzers/wcet_qemu_sys/tmrint_inputs/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
*.case
|
6
fuzzers/wcet_qemu_sys/tmrint_inputs/inputs.sh
Normal file
6
fuzzers/wcet_qemu_sys/tmrint_inputs/inputs.sh
Normal file
@ -0,0 +1,6 @@
|
||||
echo "\xff\xff" > best.case
|
||||
echo "\xff\xff\x00\x00" > best_success.case
|
||||
echo "\xff\x05\x05\x29\x07\x1f\x0b\x17\x00\x17" > worst_trace.case
|
||||
echo "\xff\x05\xFF\xF6\xFC\xF8\xFD\xFD\xFF\xFD" > worst.case
|
||||
echo "\x3F\x05\xFF\xF6\xFC\xF8\xFD\xFD\xFF\xFD" > worst_preempt.case
|
||||
echo "\x93\x1F\xFF\xF6\xFC\xF8\xFD\xFD\xFF\xFD" > worst_preempt_adv.case
|
@ -2273,11 +2273,12 @@ where
|
||||
loop {
|
||||
match listener.accept() {
|
||||
ListenerStream::Tcp(mut stream, addr) => {
|
||||
eprintln!(
|
||||
"New connection: {:?}/{:?}",
|
||||
addr,
|
||||
stream.peer_addr().unwrap()
|
||||
);
|
||||
// For some reason stderr is not avalible in child
|
||||
// eprintln!(
|
||||
// "New connection: {:?}/{:?}",
|
||||
// addr,
|
||||
// stream.peer_addr().unwrap()
|
||||
// );
|
||||
|
||||
// Send initial information, without anyone asking.
|
||||
// This makes it a tiny bit easier to map the broker map for new Clients.
|
||||
|
@ -190,6 +190,44 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// /// Fuzz until the first solution.
|
||||
// fn fuzz_for_solution(
|
||||
// &mut self,
|
||||
// stages: &mut ST,
|
||||
// executor: &mut E,
|
||||
// state: &mut EM::State,
|
||||
// manager: &mut EM,
|
||||
// ) -> Result<usize, Error> {
|
||||
// let mut last = current_time();
|
||||
// let monitor_timeout = STATS_TIMEOUT_DEFAULT;
|
||||
// let mut done = false;
|
||||
// while !done {
|
||||
// self.fuzz_one(stages, executor, state, manager)?;
|
||||
// last = manager.maybe_report_progress(state, last, monitor_timeout)?;
|
||||
// done = state.solutions().count() > 0;
|
||||
// }
|
||||
// return Ok(0);
|
||||
// }
|
||||
|
||||
// /// Fuzz until solution with limit
|
||||
// fn fuzz_for_solution_or_n(
|
||||
// &mut self,
|
||||
// stages: &mut ST,
|
||||
// executor: &mut E,
|
||||
// state: &mut EM::State,
|
||||
// manager: &mut EM,
|
||||
// iters: u64,
|
||||
// ) -> Result<usize, Error> {
|
||||
// let mut last = current_time();
|
||||
// let monitor_timeout = STATS_TIMEOUT_DEFAULT;
|
||||
// for _ in 0..iters {
|
||||
// self.fuzz_one(stages, executor, state, manager)?;
|
||||
// last = manager.maybe_report_progress(state, last, monitor_timeout)?;
|
||||
// if state.solutions().count() > 0 {break;}
|
||||
// }
|
||||
// return Ok(0);
|
||||
// }
|
||||
|
||||
/// Fuzz for n iterations.
|
||||
/// Returns the index of the last fuzzed corpus item.
|
||||
/// (Note: An iteration represents a complete run of every stage.
|
||||
|
@ -30,6 +30,8 @@ slirp = [ "systemmode", "libafl_qemu_sys/slirp" ] # build qemu with host libslir
|
||||
|
||||
clippy = [] # special feature for clippy, don't use in normal projects§
|
||||
|
||||
jmp_as_edge = [] # Add all jumps in app code to edges, circumvents bugs in the original instrumentation
|
||||
|
||||
[dependencies]
|
||||
libafl = { path = "../libafl", version = "0.8.2", default-features = false, features = ["std", "derive", "llmp_compression"] }
|
||||
libafl_targets = { path = "../libafl_targets", version = "0.8.2" }
|
||||
|
290
libafl_qemu/src/clock.rs
Normal file
290
libafl_qemu/src/clock.rs
Normal file
@ -0,0 +1,290 @@
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use libafl::{
|
||||
bolts::{
|
||||
current_nanos,
|
||||
rands::StdRand,
|
||||
tuples::{tuple_list},
|
||||
},
|
||||
executors::{ExitKind},
|
||||
fuzzer::{StdFuzzer},
|
||||
inputs::{BytesInput, HasTargetBytes},
|
||||
observers::{Observer,VariableMapObserver},
|
||||
state::{StdState, HasNamedMetadata},
|
||||
Error,
|
||||
observers::ObserversTuple, prelude::UsesInput,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{cell::UnsafeCell, cmp::max};
|
||||
use libafl::bolts::tuples::Named;
|
||||
|
||||
use crate::{
|
||||
emu,
|
||||
emu::Emulator,
|
||||
executor::QemuExecutor,
|
||||
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
||||
};
|
||||
use libafl::events::EventFirer;
|
||||
use libafl::state::HasClientPerfMonitor;
|
||||
use libafl::inputs::Input;
|
||||
use libafl::feedbacks::Feedback;
|
||||
use libafl::SerdeAny;
|
||||
use libafl::state::HasMetadata;
|
||||
use libafl::corpus::testcase::Testcase;
|
||||
use core::{fmt::Debug, time::Duration};
|
||||
// use libafl::feedbacks::FeedbackState;
|
||||
// use libafl::state::HasFeedbackStates;
|
||||
use libafl::bolts::tuples::MatchName;
|
||||
|
||||
//========== Metadata
|
||||
#[derive(Debug, Serialize, Deserialize, SerdeAny)]
|
||||
pub struct QemuIcountMetadata {
|
||||
runtime: u64,
|
||||
}
|
||||
// libafl::impl_serdeany!(QemuIcountMetadata);
|
||||
|
||||
/// Metadata for [`QemuClockIncreaseFeedback`]
|
||||
#[derive(Debug, Serialize, Deserialize, SerdeAny)]
|
||||
pub struct MaxIcountMetadata {
|
||||
pub max_icount_seen: u64,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
// impl FeedbackState for MaxIcountMetadata
|
||||
// {
|
||||
// fn reset(&mut self) -> Result<(), Error> {
|
||||
// self.max_icount_seen = 0;
|
||||
// Ok(())
|
||||
// }
|
||||
// }
|
||||
|
||||
impl Named for MaxIcountMetadata
|
||||
{
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl MaxIcountMetadata
|
||||
{
|
||||
/// Create new `MaxIcountMetadata`
|
||||
#[must_use]
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
max_icount_seen: 0,
|
||||
name: name.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for MaxIcountMetadata {
|
||||
fn default() -> Self {
|
||||
Self::new("MaxClock")
|
||||
}
|
||||
}
|
||||
|
||||
//========== Observer
|
||||
|
||||
/// A simple observer, just overlooking the runtime of the target.
|
||||
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||
pub struct QemuClockObserver {
|
||||
name: String,
|
||||
last_runtime: u64,
|
||||
}
|
||||
|
||||
impl QemuClockObserver {
|
||||
/// Creates a new [`QemuClockObserver`] with the given name.
|
||||
#[must_use]
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
name: name.to_string(),
|
||||
last_runtime: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the runtime for the last execution of this target.
|
||||
#[must_use]
|
||||
pub fn last_runtime(&self) -> u64 {
|
||||
self.last_runtime
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> Observer<S> for QemuClockObserver
|
||||
where
|
||||
S: UsesInput,
|
||||
{
|
||||
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||
self.last_runtime=0;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> {
|
||||
unsafe { self.last_runtime = emu::icount_get_raw() };
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for QemuClockObserver {
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for QemuClockObserver {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
name: String::from("clock"),
|
||||
last_runtime: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//========== Feedback
|
||||
/// Nop feedback that annotates execution time in the new testcase, if any
|
||||
/// for this Feedback, the testcase is never interesting (use with an OR)
|
||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||
pub struct ClockFeedback {
|
||||
exec_time: Option<u64>,
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl<S> Feedback<S> for ClockFeedback
|
||||
where
|
||||
S: UsesInput + HasClientPerfMonitor,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
_state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &S::Input,
|
||||
observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<S>,
|
||||
{
|
||||
// TODO Replace with match_name_type when stable
|
||||
let observer = observers.match_name::<QemuClockObserver>(self.name()).unwrap();
|
||||
self.exec_time = Some(observer.last_runtime());
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||
#[inline]
|
||||
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
|
||||
*testcase.exec_time_mut() = match self.exec_time {
|
||||
Some(s) => Some(Duration::from_nanos(s << 3)), //emulated time is << 3, real time more like * 360
|
||||
None => None,
|
||||
};
|
||||
self.exec_time = None;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||
#[inline]
|
||||
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||
self.exec_time = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Named for ClockFeedback {
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
self.name.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockFeedback {
|
||||
/// Creates a new [`ClockFeedback`], deciding if the value of a [`TimeObserver`] with the given `name` of a run is interesting.
|
||||
#[must_use]
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {
|
||||
exec_time: None,
|
||||
name: name.to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new [`ClockFeedback`], deciding if the given [`TimeObserver`] value of a run is interesting.
|
||||
#[must_use]
|
||||
pub fn new_with_observer(observer: &QemuClockObserver) -> Self {
|
||||
Self {
|
||||
exec_time: None,
|
||||
name: observer.name().to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A [`Feedback`] rewarding increasing the execution cycles on Qemu.
|
||||
#[derive(Debug)]
|
||||
pub struct QemuClockIncreaseFeedback {
|
||||
name: String,
|
||||
}
|
||||
|
||||
impl<S> Feedback<S> for QemuClockIncreaseFeedback
|
||||
where
|
||||
S: UsesInput + HasNamedMetadata + HasClientPerfMonitor + Debug,
|
||||
{
|
||||
fn is_interesting<EM, OT>(
|
||||
&mut self,
|
||||
state: &mut S,
|
||||
_manager: &mut EM,
|
||||
_input: &S::Input,
|
||||
_observers: &OT,
|
||||
_exit_kind: &ExitKind,
|
||||
) -> Result<bool, Error>
|
||||
where
|
||||
EM: EventFirer<State = S>,
|
||||
OT: ObserversTuple<S>,
|
||||
{
|
||||
let observer = _observers.match_name::<QemuClockObserver>("clock")
|
||||
.expect("QemuClockObserver not found");
|
||||
let clock_state = state
|
||||
.named_metadata_mut()
|
||||
.get_mut::<MaxIcountMetadata>(&self.name)
|
||||
.unwrap();
|
||||
if observer.last_runtime() > clock_state.max_icount_seen {
|
||||
// println!("Clock improving {}",observer.last_runtime());
|
||||
clock_state.max_icount_seen = observer.last_runtime();
|
||||
return Ok(true);
|
||||
}
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||
#[inline]
|
||||
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
|
||||
// testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||
#[inline]
|
||||
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
impl Named for QemuClockIncreaseFeedback {
|
||||
#[inline]
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl QemuClockIncreaseFeedback {
|
||||
/// Creates a new [`HitFeedback`]
|
||||
#[must_use]
|
||||
pub fn new(name: &'static str) -> Self {
|
||||
Self {name: String::from(name)}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for QemuClockIncreaseFeedback {
|
||||
fn default() -> Self {
|
||||
Self::new("MaxClock")
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
use std::{cell::UnsafeCell, cmp::max};
|
||||
use std::{cell::UnsafeCell, cmp::max, ops::Range};
|
||||
|
||||
use hashbrown::{hash_map::Entry, HashMap};
|
||||
use libafl::{inputs::UsesInput, state::HasMetadata};
|
||||
use libafl::{inputs::UsesInput, state::HasMetadata, prelude::Input};
|
||||
pub use libafl_targets::{
|
||||
edges_max_num, EDGES_MAP, EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE, EDGES_MAP_SIZE, MAX_EDGES_NUM,
|
||||
};
|
||||
@ -9,8 +9,9 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
emu::GuestAddr,
|
||||
Emulator,
|
||||
helper::{hash_me, QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
||||
hooks::QemuHooks,
|
||||
hooks::QemuHooks, executor,
|
||||
};
|
||||
|
||||
#[derive(Debug, Default, Serialize, Deserialize)]
|
||||
@ -35,6 +36,7 @@ libafl::impl_serdeany!(QemuEdgesMapMetadata);
|
||||
pub struct QemuEdgeCoverageHelper {
|
||||
filter: QemuInstrumentationFilter,
|
||||
use_hitcounts: bool,
|
||||
app_range: Option<Range<u64>>,
|
||||
}
|
||||
|
||||
impl QemuEdgeCoverageHelper {
|
||||
@ -43,6 +45,7 @@ impl QemuEdgeCoverageHelper {
|
||||
Self {
|
||||
filter,
|
||||
use_hitcounts: true,
|
||||
app_range: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -51,6 +54,7 @@ impl QemuEdgeCoverageHelper {
|
||||
Self {
|
||||
filter,
|
||||
use_hitcounts: false,
|
||||
app_range: None,
|
||||
}
|
||||
}
|
||||
|
||||
@ -58,6 +62,21 @@ impl QemuEdgeCoverageHelper {
|
||||
pub fn must_instrument(&self, addr: u64) -> bool {
|
||||
self.filter.allowed(addr)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn must_save(&self, src: u64, dst: u64) -> bool {
|
||||
match &self.app_range {
|
||||
None => false,
|
||||
Some(s) => {
|
||||
// if src != 0 {
|
||||
// println!("must_save {} {:x} {:x}",s.contains(&src) && !s.contains(&dst),src,dst);
|
||||
// }
|
||||
s.contains(&src) || s.contains(&dst)
|
||||
// println!("must_save {} {:x} {:x}",src==0&&dst!=0x9cc,src,dst);
|
||||
// src==0&&dst!=0x9cc
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for QemuEdgeCoverageHelper {
|
||||
@ -144,11 +163,48 @@ where
|
||||
Some(trace_edge_single_ptr),
|
||||
);
|
||||
}
|
||||
// if self.app_range.is_some() {
|
||||
// executor.hook_jmp_generation(gen_jmp_instrument::<I, QT, S>);
|
||||
// executor.emulator().set_exec_jmp_hook(trace_jmp)
|
||||
// }
|
||||
}
|
||||
|
||||
// fn pre_exec(&mut self, _emulator: &Emulator, _input: &I) {
|
||||
// unsafe { SAVED_JUMP=None; }
|
||||
// }
|
||||
}
|
||||
|
||||
thread_local!(static PREV_LOC : UnsafeCell<u64> = UnsafeCell::new(0));
|
||||
|
||||
/// Save the source and destination of last interesting jump
|
||||
pub static mut SAVED_JUMP: Option<(u64, u64)> = None;
|
||||
|
||||
// pub fn gen_jmp_instrument<I, QT, S>(
|
||||
// _emulator: &Emulator,
|
||||
// helpers: &mut QT,
|
||||
// state: &mut S,
|
||||
// src: u64,
|
||||
// dest: u64,
|
||||
// ) -> Option<u64>
|
||||
// where
|
||||
// S: HasMetadata,
|
||||
// S: UsesInput,
|
||||
// QT: QemuHelperTuple<S>,
|
||||
// {
|
||||
// if let Some(h) = helpers.match_first_type::<QemuEdgeCoverageHelper>() {
|
||||
// if !h.must_instrument(src) && !h.must_instrument(dest) {
|
||||
// return None;
|
||||
// }
|
||||
// if !h.must_save(src, dest) {
|
||||
// return None;
|
||||
// }
|
||||
// }
|
||||
// // Temporary fux for missing edges
|
||||
// #[cfg(feature = "jmp_as_edge")]
|
||||
// return gen_unique_edge_ids(_emulator,helpers,state,src,dest);
|
||||
// #[cfg(not(feature = "jmp_as_edge"))]
|
||||
// return Some(1);
|
||||
// }
|
||||
pub fn gen_unique_edge_ids<QT, S>(
|
||||
hooks: &mut QemuHooks<'_, QT, S>,
|
||||
state: Option<&mut S>,
|
||||
@ -197,6 +253,15 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// pub extern "C" fn trace_jmp(src: u64, des: u64, id: u64) {
|
||||
// unsafe {
|
||||
// SAVED_JUMP=Some((src, des));
|
||||
// }
|
||||
// // temporary hack to catch app code blocks
|
||||
// #[cfg(feature = "jmp_as_edge")]
|
||||
// unsafe { trace_edge_hitcount(id); }
|
||||
// }
|
||||
|
||||
pub extern "C" fn trace_edge_hitcount(id: u64, _data: u64) {
|
||||
unsafe {
|
||||
EDGES_MAP[id as usize] = EDGES_MAP[id as usize].wrapping_add(1);
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Expose QEMU user `LibAFL` C api to Rust
|
||||
|
||||
use libc::c_char;
|
||||
use core::{
|
||||
convert::Into,
|
||||
ffi::c_void,
|
||||
@ -245,7 +246,10 @@ impl MapInfo {
|
||||
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
extern "C" {
|
||||
#[cfg(not(feature = "systemmode"))]
|
||||
fn qemu_user_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
|
||||
#[cfg(feature = "systemmode")]
|
||||
fn libafl_qemu_sys_init(argc: i32, argv: *const *const u8, envp: *const *const u8) -> i32;
|
||||
|
||||
fn libafl_qemu_run() -> i32;
|
||||
|
||||
@ -258,14 +262,27 @@ extern "C" {
|
||||
|
||||
fn libafl_maps_next(map_info: *const c_void, ret: *mut MapInfo) -> *const c_void;
|
||||
|
||||
#[cfg(feature = "systemmode")]
|
||||
#[cfg(feature = "arm")]
|
||||
pub fn libafl_send_irq(irqn: u32);
|
||||
#[cfg(feature = "systemmode")]
|
||||
#[cfg(feature = "arm")]
|
||||
pub static mut libafl_int_offset: u32;
|
||||
|
||||
static exec_path: *const u8;
|
||||
static guest_base: usize;
|
||||
static mut mmap_next_start: GuestAddr;
|
||||
|
||||
static mut libafl_on_thread_hook: unsafe extern "C" fn(u32);
|
||||
#[cfg(feature = "systemmode")]
|
||||
static mut libafl_exec_jmp_hook: unsafe extern "C" fn(u64, u64, u64);
|
||||
#[cfg(feature = "systemmode")]
|
||||
static mut libafl_gen_jmp_hook: unsafe extern "C" fn(u64, u64) -> u64;
|
||||
|
||||
#[cfg(not(feature = "systemmode"))]
|
||||
static mut libafl_pre_syscall_hook:
|
||||
unsafe extern "C" fn(i32, u64, u64, u64, u64, u64, u64, u64, u64) -> SyscallHookResult;
|
||||
#[cfg(not(feature = "systemmode"))]
|
||||
static mut libafl_post_syscall_hook:
|
||||
unsafe extern "C" fn(u64, i32, u64, u64, u64, u64, u64, u64, u64, u64) -> u64;
|
||||
}
|
||||
@ -281,6 +298,7 @@ extern "C" {
|
||||
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
||||
#[allow(unused)]
|
||||
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
||||
pub fn icount_get_raw() -> u64;
|
||||
}
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
@ -1054,6 +1072,25 @@ impl Emulator {
|
||||
let s = CString::new(name).expect("Invalid snapshot name");
|
||||
unsafe { libafl_save_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||
}
|
||||
// #[cfg(feature = "systemmode")]
|
||||
// pub fn set_exec_jmp_hook(&self, hook: extern "C" fn(src: u64, dest: u64, id: u64)) {
|
||||
// unsafe {
|
||||
// libafl_exec_jmp_hook = hook;
|
||||
// }
|
||||
// }
|
||||
|
||||
// #[cfg(feature = "systemmode")]
|
||||
// pub fn set_gen_jmp_hook(&self, hook: extern "C" fn(src: u64, dest: u64) -> u64) {
|
||||
// unsafe {
|
||||
// libafl_gen_jmp_hook = hook;
|
||||
// }
|
||||
// }
|
||||
|
||||
// pub fn set_exec_block_hook(&self, hook: extern "C" fn(pc: u64)) {
|
||||
// unsafe {
|
||||
// libafl_exec_block_hook = hook;
|
||||
// }
|
||||
// }
|
||||
|
||||
#[cfg(emulation_mode = "systemmode")]
|
||||
pub fn load_snapshot(&self, name: &str, sync: bool) {
|
||||
|
@ -1,5 +1,10 @@
|
||||
//! A `QEMU`-based executor for binary-only instrumentation in `LibAFL`
|
||||
use core::fmt::{self, Debug, Formatter};
|
||||
use core::mem::transmute;
|
||||
use libafl::prelude::inprocess::inprocess_get_state;
|
||||
use libafl::prelude::Input;
|
||||
use core::ffi::c_void;
|
||||
use crate::SKIP_EXEC_HOOK;
|
||||
|
||||
#[cfg(feature = "fork")]
|
||||
use libafl::{
|
||||
@ -19,6 +24,40 @@ use libafl::{
|
||||
|
||||
use crate::{emu::Emulator, helper::QemuHelperTuple, hooks::QemuHooks};
|
||||
|
||||
// static mut QEMU_HELPERS_PTR: *const c_void = ptr::null();
|
||||
|
||||
// static mut GEN_JMP_HOOK_PTR: *const c_void = ptr::null();
|
||||
// extern "C" fn gen_jmp_hook_wrapper<I, QT, S>(src: u64, dst: u64) -> u64
|
||||
// where
|
||||
// S: UsesInput,
|
||||
// QT: QemuHelperTuple<S>,
|
||||
// {
|
||||
// unsafe {
|
||||
// let helpers = (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap();
|
||||
// let state = inprocess_get_state::<S>().unwrap();
|
||||
// let emulator = Emulator::new_empty();
|
||||
// let func: fn(&Emulator, &mut QT, &mut S, u64, u64) -> Option<u64> =
|
||||
// transmute(GEN_JMP_HOOK_PTR);
|
||||
// (func)(&emulator, helpers, state, src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
|
||||
// }
|
||||
// }
|
||||
|
||||
// static mut JMP_HOOKS: Vec<*const c_void> = vec![];
|
||||
// extern "C" fn jmp_hooks_wrapper<I, QT, S>(src: u64, dst: u64, id: u64)
|
||||
// where
|
||||
// S: UsesInput,
|
||||
// QT: QemuHelperTuple<S>,
|
||||
// {
|
||||
// let helpers = unsafe { (QEMU_HELPERS_PTR as *mut QT).as_mut().unwrap() };
|
||||
// let state = inprocess_get_state::<S>().unwrap();
|
||||
// let emulator = Emulator::new_empty();
|
||||
// for hook in unsafe { &JMP_HOOKS } {
|
||||
// let func: fn(&Emulator, &mut QT, &mut S, u64, u64) = unsafe { transmute(*hook) };
|
||||
// (func)(&emulator, helpers, state, src, dst);
|
||||
// }
|
||||
// }
|
||||
|
||||
// static mut GEN_EDGE_HOOK_PTR: *const c_void = ptr::null();
|
||||
pub struct QemuExecutor<'a, H, OT, QT, S>
|
||||
where
|
||||
H: FnMut(&S::Input) -> ExitKind,
|
||||
@ -93,6 +132,29 @@ where
|
||||
pub fn emulator(&self) -> &Emulator {
|
||||
self.hooks.emulator()
|
||||
}
|
||||
// #[cfg(feature = "systemmode")]
|
||||
// #[allow(clippy::unused_self)]
|
||||
// pub fn hook_jmp_generation(
|
||||
// &self,
|
||||
// hook: fn(&Emulator, &mut QT, &mut S, src: u64, dest: u64) -> Option<u64>,
|
||||
// ) {
|
||||
// unsafe {
|
||||
// GEN_JMP_HOOK_PTR = hook as *const _;
|
||||
// }
|
||||
// self.emulator
|
||||
// .set_gen_jmp_hook(gen_jmp_hook_wrapper::<I, QT, S>);
|
||||
// }
|
||||
|
||||
// #[cfg(feature = "systemmode")]
|
||||
// #[allow(clippy::unused_self)]
|
||||
// pub fn hook_jmp_execution(&self, hook: fn(&Emulator, &mut QT, &mut S, src: u64, dest: u64, id: u64)) {
|
||||
// unsafe {
|
||||
// JMP_HOOKS.push(hook as *const _);
|
||||
// }
|
||||
// self.emulator
|
||||
// .set_exec_jmp_hook(jmp_hooks_wrapper::<I, QT, S>);
|
||||
// }
|
||||
|
||||
}
|
||||
|
||||
impl<'a, EM, H, OT, QT, S, Z> Executor<EM, Z> for QemuExecutor<'a, H, OT, QT, S>
|
||||
|
@ -856,6 +856,33 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// pub fn jmp_raw(
|
||||
// &self,
|
||||
// generation_hook: Option<
|
||||
// fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option<u64>,
|
||||
// >,
|
||||
// execution_hook: Option<extern "C" fn(id: u64, data: u64)>,
|
||||
// ) {
|
||||
// unsafe {
|
||||
// let index = EDGE_HOOKS.len();
|
||||
// self.emulator.add_jmp_hooks(
|
||||
// if generation_hook.is_none() {
|
||||
// None
|
||||
// } else {
|
||||
// Some(gen_jmp_hook_wrapper::<QT, S>)
|
||||
// },
|
||||
// execution_hook,
|
||||
// index as u64,
|
||||
// );
|
||||
// EDGE_HOOKS.push((
|
||||
// generation_hook.map_or(Hook::Empty, |hook| {
|
||||
// Hook::Function(hook as *const libc::c_void)
|
||||
// }),
|
||||
// Hook::Empty,
|
||||
// ));
|
||||
// }
|
||||
// }
|
||||
|
||||
pub fn blocks(
|
||||
&self,
|
||||
generation_hook: Option<fn(&mut Self, Option<&mut S>, pc: GuestAddr) -> Option<u64>>,
|
||||
|
@ -64,6 +64,8 @@ pub use snapshot::QemuSnapshotHelper;
|
||||
pub mod asan;
|
||||
#[cfg(emulation_mode = "usermode")]
|
||||
pub use asan::{init_with_asan, QemuAsanHelper};
|
||||
#[cfg(not(emulation_mode = "usermode"))]
|
||||
pub mod clock;
|
||||
|
||||
pub mod blocks;
|
||||
pub mod calls;
|
||||
|
63
libafl_qemu/src/snapshot_sys.rs
Normal file
63
libafl_qemu/src/snapshot_sys.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use crate::Emulator;
|
||||
use crate::QemuExecutor;
|
||||
use crate::QemuHelper;
|
||||
use crate::QemuHelperTuple;
|
||||
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
|
||||
|
||||
use crate::{
|
||||
emu,
|
||||
};
|
||||
// TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html
|
||||
#[derive(Debug)]
|
||||
pub struct QemuSysSnapshotHelper {
|
||||
pub empty: bool,
|
||||
}
|
||||
|
||||
impl QemuSysSnapshotHelper {
|
||||
#[must_use]
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
empty: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot(&mut self, emulator: &Emulator) {
|
||||
if self.empty {
|
||||
let ret = emulator.snapshot_save("Start");
|
||||
if !ret { panic!("QemuSysSnapshotHelper failed to take a snapshot") };
|
||||
self.empty = false;
|
||||
}
|
||||
}
|
||||
pub fn reset(&mut self, emulator: &Emulator) {
|
||||
let ret = emulator.snapshot_load("Start");
|
||||
if !ret { panic!("QemuSysSnapshotHelper failed to load a snapshot") };
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for QemuSysSnapshotHelper {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl<I, S> QemuHelper<I, S> for QemuSysSnapshotHelper
|
||||
where
|
||||
I: Input,
|
||||
S: HasMetadata,
|
||||
{
|
||||
fn init<'a, H, OT, QT>(&self, _executor: &QemuExecutor<'a, H, I, OT, QT, S>)
|
||||
where
|
||||
H: FnMut(&I) -> ExitKind,
|
||||
OT: ObserversTuple<I, S>,
|
||||
QT: QemuHelperTuple<I, S>,
|
||||
{
|
||||
}
|
||||
|
||||
fn pre_exec(&mut self, emulator: &Emulator, _input: &I) {
|
||||
if self.empty {
|
||||
self.snapshot(emulator);
|
||||
} else {
|
||||
self.reset(emulator);
|
||||
}
|
||||
}
|
||||
}
|
@ -7,7 +7,7 @@ use crate::{ACCOUNTING_MAP_SIZE, EDGES_MAP_SIZE};
|
||||
|
||||
/// The map for edges.
|
||||
#[no_mangle]
|
||||
pub static mut __afl_area_ptr_local: [u8; EDGES_MAP_SIZE] = [0; EDGES_MAP_SIZE];
|
||||
pub static mut __afl_area_ptr_local: [u16; EDGES_MAP_SIZE] = [0u16; EDGES_MAP_SIZE];
|
||||
pub use __afl_area_ptr_local as EDGES_MAP;
|
||||
|
||||
/// The map for accounting mem writes.
|
||||
@ -20,7 +20,7 @@ pub static mut MAX_EDGES_NUM: usize = 0;
|
||||
|
||||
extern "C" {
|
||||
/// The area pointer points to the edges map.
|
||||
pub static mut __afl_area_ptr: *mut u8;
|
||||
pub static mut __afl_area_ptr: *mut u16;
|
||||
|
||||
/// The area pointer points to the accounting mem operations map.
|
||||
pub static mut __afl_acc_memop_ptr: *mut u32;
|
||||
@ -62,7 +62,7 @@ use libafl::bolts::ownedref::OwnedSliceMut;
|
||||
/// This function will crash if `EDGES_MAP_PTR` is not a valid pointer.
|
||||
/// The `EDGES_MAP_PTR_SIZE` needs to be smaller than, or equal to the size of the map.
|
||||
#[must_use]
|
||||
pub unsafe fn edges_map_from_ptr<'a>() -> OwnedSliceMut<'a, u8> {
|
||||
pub unsafe fn edges_map_from_ptr<'a>() -> OwnedSliceMut<'a, u16> {
|
||||
debug_assert!(!EDGES_MAP_PTR.is_null());
|
||||
OwnedSliceMut::from_raw_parts_mut(EDGES_MAP_PTR, EDGES_MAP_PTR_SIZE)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user