Compare commits
100 Commits
update_mer
...
fret
Author | SHA1 | Date | |
---|---|---|---|
798aa2ceb9 | |||
183ff32beb | |||
89979b64d9 | |||
35da9fdf24 | |||
1bca346b39 | |||
8b90886299 | |||
1bd7d853ac | |||
253048e534 | |||
52cc00fedc | |||
eec998c426 | |||
a328ddfd5f | |||
6a042da5c1 | |||
2e20a22dc6 | |||
bbc83ef6be | |||
48466ac2d7 | |||
ad8cecdba4 | |||
c2afc0186e | |||
4df67db479 | |||
402eff7b47 | |||
a8a6c175c8 | |||
8a79e12f91 | |||
a3e38b6abb | |||
eb04325f09 | |||
cfb8fa2b32 | |||
2889e9bf61 | |||
960764cf85 | |||
e6816cc2de | |||
f3180a35cc | |||
54312b2577 | |||
6d920fd962 | |||
281979ecd8 | |||
c628afaa81 | |||
c548c6bc09 | |||
6e8769907d | |||
bf639e42fa | |||
a05ff97d0c | |||
f09034b7fe | |||
d118eeacbd | |||
57fc441118 | |||
10b5fe8a74 | |||
7f987b037d | |||
58be280a62 | |||
3c586f5047 | |||
9336b932d0 | |||
e0f73778e2 | |||
e5ac5ba825 | |||
2acf3ef301 | |||
28bac2a850 | |||
41586dd8b1 | |||
7420aabeeb | |||
d118ff0056 | |||
dfe4f713b9 | |||
f7a05d2a7c | |||
2593bdf42f | |||
8c8ab7c44e | |||
9cadc5d61c | |||
594554eca0 | |||
267309b954 | |||
35435fbd97 | |||
8fcc54bbdd | |||
1f538f9834 | |||
ba01f600ee | |||
2cb479581d | |||
1fbf948478 | |||
6e1d5695e3 | |||
8d31196614 | |||
4c90144db5 | |||
eeaf7eb43f | |||
68c4887dad | |||
7ca2d43f3d | |||
9f97852e4a | |||
f4e1990387 | |||
d936234976 | |||
795fbff61a | |||
6a9df35e28 | |||
9b9fbc3677 | |||
decae09931 | |||
b812e994a6 | |||
4587f442d0 | |||
c748fecbe2 | |||
7595d25192 | |||
79bca99cc7 | |||
b07f7ccbca | |||
e3f38edd0a | |||
6ad55e3b29 | |||
f7ee38ebb2 | |||
![]() |
50708f4d9c | ||
![]() |
4e2e4eb5c0 | ||
![]() |
2f9b279428 | ||
![]() |
f9eac18542 | ||
![]() |
61aa764dc4 | ||
![]() |
abfd834e98 | ||
![]() |
68fbfc8914 | ||
![]() |
ee58375ac5 | ||
![]() |
5d7fd8f914 | ||
![]() |
93d99beecf | ||
![]() |
cd8367d3e9 | ||
![]() |
3bad100cb7 | ||
![]() |
c879a0a8d3 | ||
![]() |
2a2e70a636 |
4
.gitignore
vendored
4
.gitignore
vendored
@ -18,6 +18,7 @@ vendor
|
|||||||
*.dll
|
*.dll
|
||||||
*.exe
|
*.exe
|
||||||
*.dSYM
|
*.dSYM
|
||||||
|
*.obj
|
||||||
|
|
||||||
.cur_input
|
.cur_input
|
||||||
.venv
|
.venv
|
||||||
@ -50,3 +51,6 @@ __pycache__
|
|||||||
**/libxml2
|
**/libxml2
|
||||||
**/corpus_discovered
|
**/corpus_discovered
|
||||||
**/libxml2-*.tar.gz
|
**/libxml2-*.tar.gz
|
||||||
|
|
||||||
|
libafl_nyx/QEMU-Nyx
|
||||||
|
libafl_nyx/packer
|
@ -6,6 +6,7 @@ members = [
|
|||||||
"libafl_targets",
|
"libafl_targets",
|
||||||
"libafl_frida",
|
"libafl_frida",
|
||||||
"libafl_qemu",
|
"libafl_qemu",
|
||||||
|
"libafl_tinyinst",
|
||||||
"libafl_sugar",
|
"libafl_sugar",
|
||||||
"libafl_nyx",
|
"libafl_nyx",
|
||||||
"libafl_concolic/symcc_runtime",
|
"libafl_concolic/symcc_runtime",
|
||||||
|
@ -72,6 +72,9 @@ COPY scripts/dummy.rs libafl_concolic/symcc_libafl/src/lib.rs
|
|||||||
COPY libafl_nyx/Cargo.toml libafl_nyx/build.rs libafl_nyx/
|
COPY libafl_nyx/Cargo.toml libafl_nyx/build.rs libafl_nyx/
|
||||||
COPY scripts/dummy.rs libafl_nyx/src/lib.rs
|
COPY scripts/dummy.rs libafl_nyx/src/lib.rs
|
||||||
|
|
||||||
|
COPY libafl_tinyinst/Cargo.toml libafl_tinyinst/
|
||||||
|
COPY scripts/dummy.rs libafl_tinyinst/src/lib.rs
|
||||||
|
|
||||||
COPY utils utils
|
COPY utils utils
|
||||||
|
|
||||||
RUN cargo build && cargo build --release
|
RUN cargo build && cargo build --release
|
||||||
|
@ -34,6 +34,7 @@ LibAFL offers integrations with popular instrumentation frameworks. At the momen
|
|||||||
+ SanitizerCoverage, in [libafl_targets](./libafl_targets)
|
+ SanitizerCoverage, in [libafl_targets](./libafl_targets)
|
||||||
+ Frida, in [libafl_frida](./libafl_frida)
|
+ Frida, in [libafl_frida](./libafl_frida)
|
||||||
+ QEMU user-mode, in [libafl_qemu](./libafl_qemu)
|
+ QEMU user-mode, in [libafl_qemu](./libafl_qemu)
|
||||||
|
+ TinyInst, in [libafl_tinyinst](./libafl_tinyinst) by [elbiazo](https://github.com/elbiazo)
|
||||||
|
|
||||||
## Getting started
|
## Getting started
|
||||||
|
|
||||||
|
4
fuzzers/FRET/.gitignore
vendored
Normal file
4
fuzzers/FRET/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
*.qcow2
|
||||||
|
corpus
|
||||||
|
*.axf
|
||||||
|
demo
|
41
fuzzers/FRET/Cargo.toml
Normal file
41
fuzzers/FRET/Cargo.toml
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
[package]
|
||||||
|
name = "fret"
|
||||||
|
version = "0.8.2"
|
||||||
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["std", "snapshot_restore", "singlecore", "restarting", "feed_systemtrace", "fuzz_int" ]
|
||||||
|
std = []
|
||||||
|
snapshot_restore = []
|
||||||
|
snapshot_fast = [ "snapshot_restore" ]
|
||||||
|
singlecore = []
|
||||||
|
restarting = ['singlecore']
|
||||||
|
trace_abbs = []
|
||||||
|
systemstate = []
|
||||||
|
feed_systemgraph = [ "systemstate" ]
|
||||||
|
feed_systemtrace = [ "systemstate" ]
|
||||||
|
feed_longest = [ ]
|
||||||
|
feed_afl = [ ]
|
||||||
|
feed_genetic = [ ]
|
||||||
|
fuzz_int = [ ]
|
||||||
|
gensize_1 = [ ]
|
||||||
|
gensize_10 = [ ]
|
||||||
|
gensize_100 = [ ]
|
||||||
|
observer_hitcounts = []
|
||||||
|
no_hash_state = []
|
||||||
|
run_until_saturation = []
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
debug = true
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libafl = { path = "../../libafl/" }
|
||||||
|
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
||||||
|
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||||
|
hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
||||||
|
petgraph = { version="0.6.0", features = ["serde-1"] }
|
||||||
|
ron = "0.7" # write serialized data - including hashmaps
|
||||||
|
rand = "0.5"
|
26
fuzzers/FRET/README.md
Normal file
26
fuzzers/FRET/README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Qemu systemmode with launcher
|
||||||
|
|
||||||
|
This folder contains an example fuzzer for the qemu systemmode, using LLMP for fast multi-process fuzzing and crash detection.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
To build this example, run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --release
|
||||||
|
cd example; sh build.sh; cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
This will build the the fuzzer (src/fuzzer.rs) and a small example binary based on FreeRTOS, which can run under a qemu emulation target.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
Since the instrumentation is based on snapshtos QEMU needs a virtual drive (even if it is unused...).
|
||||||
|
Create on and then run the fuzzer:
|
||||||
|
```bash
|
||||||
|
# create an image
|
||||||
|
qemu-img create -f qcow2 dummy.qcow2 32M
|
||||||
|
# run the fuzzer
|
||||||
|
KERNEL=./example/example.elf target/release/qemu_systemmode -icount shift=auto,align=off,sleep=off -machine mps2-an385 -monitor null -kernel ./example/example.elf -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S
|
||||||
|
```
|
||||||
|
Currently the ``KERNEL`` variable is needed because the fuzzer does not parse QEMUs arguments to find the binary.
|
12
fuzzers/FRET/benchmark/.gitignore
vendored
Normal file
12
fuzzers/FRET/benchmark/.gitignore
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
*dump
|
||||||
|
timedump*
|
||||||
|
corpora
|
||||||
|
build
|
||||||
|
mnt
|
||||||
|
.R*
|
||||||
|
*.png
|
||||||
|
*.pdf
|
||||||
|
bins
|
||||||
|
.snakemake
|
||||||
|
*.zip
|
||||||
|
*.tar.*
|
57
fuzzers/FRET/benchmark/Makefile
Normal file
57
fuzzers/FRET/benchmark/Makefile
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
TIME=7200
|
||||||
|
|
||||||
|
corpora/%/seed:
|
||||||
|
mkdir -p $$(dirname $@)
|
||||||
|
LINE=$$(grep "^$$(basename $*)" target_symbols.csv); \
|
||||||
|
export \
|
||||||
|
KERNEL=benchmark/build/$*.elf \
|
||||||
|
FUZZ_MAIN=$$(echo $$LINE | cut -d, -f2) \
|
||||||
|
FUZZ_INPUT=$$(echo $$LINE | cut -d, -f3) \
|
||||||
|
FUZZ_INPUT_LEN=$$(echo $$LINE | cut -d, -f4) \
|
||||||
|
BREAKPOINT=$$(echo $$LINE | cut -d, -f5) \
|
||||||
|
SEED_DIR=benchmark/corpora/$* \
|
||||||
|
DUMP_SEED=seed; \
|
||||||
|
../fuzzer.sh
|
||||||
|
|
||||||
|
timedump/%$(FUZZ_RANDOM)$(SUFFIX): corpora/%/seed
|
||||||
|
mkdir -p $$(dirname $@)
|
||||||
|
LINE=$$(grep "^$$(basename $*)" target_symbols.csv); \
|
||||||
|
export \
|
||||||
|
KERNEL=benchmark/build/$*.elf \
|
||||||
|
FUZZ_MAIN=$$(echo $$LINE | cut -d, -f2) \
|
||||||
|
FUZZ_INPUT=$$(echo $$LINE | cut -d, -f3) \
|
||||||
|
FUZZ_INPUT_LEN=$$(echo $$LINE | cut -d, -f4) \
|
||||||
|
BREAKPOINT=$$(echo $$LINE | cut -d, -f5) \
|
||||||
|
SEED_RANDOM=1 \
|
||||||
|
TIME_DUMP=benchmark/$@ \
|
||||||
|
CASE_DUMP=benchmark/$@; \
|
||||||
|
../fuzzer.sh + + + + + $(TIME) + + + > $@_log
|
||||||
|
#SEED_DIR=benchmark/corpora/$*
|
||||||
|
|
||||||
|
all_sequential: timedump/sequential/mpeg2$(FUZZ_RANDOM) timedump/sequential/dijkstra$(FUZZ_RANDOM) timedump/sequential/epic$(FUZZ_RANDOM) \
|
||||||
|
timedump/sequential/g723_enc$(FUZZ_RANDOM) timedump/sequential/audiobeam$(FUZZ_RANDOM) \
|
||||||
|
timedump/sequential/gsm_enc$(FUZZ_RANDOM)
|
||||||
|
|
||||||
|
all_kernel: timedump/kernel/bsort$(FUZZ_RANDOM) timedump/kernel/insertsort$(FUZZ_RANDOM) #timedump/kernel/fft$(FUZZ_RANDOM)
|
||||||
|
|
||||||
|
all_app: timedump/app/lift$(FUZZ_RANDOM)
|
||||||
|
|
||||||
|
all_system: timedump/lift$(FUZZ_RANDOM)$(SUFFIX)
|
||||||
|
|
||||||
|
all_period: timedump/waters$(FUZZ_RANDOM)$(SUFFIX)
|
||||||
|
|
||||||
|
tacle_rtos: timedump/tacle_rtos$(FUZZ_RANDOM)
|
||||||
|
|
||||||
|
graphics:
|
||||||
|
Rscript --vanilla plot_comparison.r mnt/timedump/sequential audiobeam
|
||||||
|
Rscript --vanilla plot_comparison.r mnt/timedump/sequential dijkstra
|
||||||
|
Rscript --vanilla plot_comparison.r mnt/timedump/sequential epic
|
||||||
|
Rscript --vanilla plot_comparison.r mnt/timedump/sequential g723_enc
|
||||||
|
# Rscript --vanilla plot_comparison.r mnt/timedump/sequential gsm_enc
|
||||||
|
# Rscript --vanilla plot_comparison.r mnt/timedump/sequential huff_dec
|
||||||
|
Rscript --vanilla plot_comparison.r mnt/timedump/sequential mpeg2
|
||||||
|
# Rscript --vanilla plot_comparison.r mnt/timedump/sequential rijndael_dec
|
||||||
|
# Rscript --vanilla plot_comparison.r mnt/timedump/sequential rijndael_enc
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -rf corpora timedump
|
281
fuzzers/FRET/benchmark/Snakefile
Normal file
281
fuzzers/FRET/benchmark/Snakefile
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
import csv
|
||||||
|
import os
|
||||||
|
def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,run_until_saturation"
|
||||||
|
remote="timedump_253048_1873f6_all/"
|
||||||
|
RUNTIME=10
|
||||||
|
TARGET_REPS_A=2
|
||||||
|
TARGET_REPS_B=2
|
||||||
|
NUM_NODES=2
|
||||||
|
REP_PER_NODE_A=int(TARGET_REPS_A/NUM_NODES)
|
||||||
|
REP_PER_NODE_B=int(TARGET_REPS_B/NUM_NODES)
|
||||||
|
NODE_ID= 0 if os.getenv('NODE_ID') == None else int(os.environ['NODE_ID'])
|
||||||
|
MY_RANGE_A=range(NODE_ID*REP_PER_NODE_A,(NODE_ID+1)*REP_PER_NODE_A)
|
||||||
|
MY_RANGE_B=range(NODE_ID*REP_PER_NODE_B,(NODE_ID+1)*REP_PER_NODE_B)
|
||||||
|
|
||||||
|
rule build_showmap:
|
||||||
|
output:
|
||||||
|
directory("bins/target_showmap")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},systemstate"
|
||||||
|
|
||||||
|
rule build_random:
|
||||||
|
output:
|
||||||
|
directory("bins/target_random")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_longest"
|
||||||
|
|
||||||
|
rule build_feedlongest:
|
||||||
|
output:
|
||||||
|
directory("bins/target_feedlongest")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_longest"
|
||||||
|
|
||||||
|
rule build_frafl:
|
||||||
|
output:
|
||||||
|
directory("bins/target_frafl")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_afl,feed_longest"
|
||||||
|
|
||||||
|
rule build_afl:
|
||||||
|
output:
|
||||||
|
directory("bins/target_afl")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_afl,observer_hitcounts"
|
||||||
|
|
||||||
|
rule build_state:
|
||||||
|
output:
|
||||||
|
directory("bins/target_state")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_systemtrace"
|
||||||
|
|
||||||
|
rule build_nohashstate:
|
||||||
|
output:
|
||||||
|
directory("bins/target_nohashstate")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_systemtrace,no_hash_state"
|
||||||
|
|
||||||
|
rule build_graph:
|
||||||
|
output:
|
||||||
|
directory("bins/target_graph")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_systemgraph"
|
||||||
|
|
||||||
|
rule build_showmap_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_showmap_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},systemstate,fuzz_int"
|
||||||
|
|
||||||
|
rule build_random_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_random_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_longest,fuzz_int"
|
||||||
|
|
||||||
|
rule build_state_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_state_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_systemtrace,fuzz_int"
|
||||||
|
|
||||||
|
rule build_nohashstate_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_nohashstate_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_systemtrace,fuzz_int,no_hash_state"
|
||||||
|
|
||||||
|
rule build_frafl_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_frafl_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_afl,feed_longest,fuzz_int"
|
||||||
|
|
||||||
|
rule build_afl_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_afl_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_afl,fuzz_int,observer_hitcounts"
|
||||||
|
|
||||||
|
rule build_feedlongest_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_feedlongest_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_longest,fuzz_int"
|
||||||
|
|
||||||
|
rule build_feedgeneration1:
|
||||||
|
output:
|
||||||
|
directory("bins/target_feedgeneration1")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_genetic,gensize_1"
|
||||||
|
|
||||||
|
rule build_feedgeneration1_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_feedgeneration1_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_genetic,fuzz_int,gensize_1"
|
||||||
|
|
||||||
|
rule build_feedgeneration10:
|
||||||
|
output:
|
||||||
|
directory("bins/target_feedgeneration10")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_genetic,gensize_10"
|
||||||
|
|
||||||
|
rule build_feedgeneration10_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_feedgeneration10_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_genetic,fuzz_int,gensize_10"
|
||||||
|
|
||||||
|
rule build_feedgeneration100:
|
||||||
|
output:
|
||||||
|
directory("bins/target_feedgeneration100")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_genetic,gensize_100"
|
||||||
|
|
||||||
|
rule build_feedgeneration100_int:
|
||||||
|
output:
|
||||||
|
directory("bins/target_feedgeneration100_int")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_genetic,fuzz_int,gensize_100"
|
||||||
|
|
||||||
|
rule run_bench:
|
||||||
|
input:
|
||||||
|
"build/{target}.elf",
|
||||||
|
"bins/target_{fuzzer}"
|
||||||
|
output:
|
||||||
|
multiext("timedump/{fuzzer}/{target}.{num}", "", ".log") # , ".case"
|
||||||
|
run:
|
||||||
|
with open('target_symbols.csv') as csvfile:
|
||||||
|
reader = csv.DictReader(csvfile)
|
||||||
|
line = next((x for x in reader if x['kernel']==wildcards.target), None)
|
||||||
|
if line == None:
|
||||||
|
return False
|
||||||
|
kernel=line['kernel']
|
||||||
|
fuzz_main=line['main_function']
|
||||||
|
fuzz_input=line['input_symbol']
|
||||||
|
fuzz_len=line['input_size']
|
||||||
|
bkp=line['return_function']
|
||||||
|
script="""
|
||||||
|
mkdir -p $(dirname {output[0]})
|
||||||
|
export KERNEL=$(pwd)/{input[0]}
|
||||||
|
export FUZZ_MAIN={fuzz_main}
|
||||||
|
export FUZZ_INPUT={fuzz_input}
|
||||||
|
export FUZZ_INPUT_LEN={fuzz_len}
|
||||||
|
export BREAKPOINT={bkp}
|
||||||
|
export SEED_RANDOM={wildcards.num}
|
||||||
|
export TIME_DUMP=$(pwd)/{output[0]}
|
||||||
|
export CASE_DUMP=$(pwd)/{output[0]}.case
|
||||||
|
export TRACE_DUMP=$(pwd)/{output[0]}.trace
|
||||||
|
export FUZZ_ITERS={RUNTIME}
|
||||||
|
export FUZZER=$(pwd)/{input[1]}/debug/fret
|
||||||
|
set +e
|
||||||
|
../fuzzer.sh > {output[1]} 2>&1
|
||||||
|
exit 0
|
||||||
|
"""
|
||||||
|
if wildcards.fuzzer.find('random') >= 0:
|
||||||
|
script="export FUZZ_RANDOM={output[1]}\n"+script
|
||||||
|
shell(script)
|
||||||
|
|
||||||
|
rule run_showmap:
|
||||||
|
input:
|
||||||
|
"{remote}build/{target}.elf",
|
||||||
|
"bins/target_showmap",
|
||||||
|
"bins/target_showmap_int",
|
||||||
|
"{remote}timedump/{fuzzer}/{target}.{num}.case"
|
||||||
|
output:
|
||||||
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.ron",
|
||||||
|
"{remote}timedump/{fuzzer}/{target}.{num}.case.time",
|
||||||
|
run:
|
||||||
|
with open('target_symbols.csv') as csvfile:
|
||||||
|
reader = csv.DictReader(csvfile)
|
||||||
|
line = next((x for x in reader if x['kernel']==wildcards.target), None)
|
||||||
|
if line == None:
|
||||||
|
return False
|
||||||
|
kernel=line['kernel']
|
||||||
|
fuzz_main=line['main_function']
|
||||||
|
fuzz_input=line['input_symbol']
|
||||||
|
fuzz_len=line['input_size']
|
||||||
|
bkp=line['return_function']
|
||||||
|
script=""
|
||||||
|
if wildcards.fuzzer.find('_int') > -1:
|
||||||
|
script="export FUZZER=$(pwd)/{input[2]}/debug/fret\n"
|
||||||
|
else:
|
||||||
|
script="export FUZZER=$(pwd)/{input[1]}/debug/fret\n"
|
||||||
|
script+="""
|
||||||
|
mkdir -p $(dirname {output})
|
||||||
|
export KERNEL=$(pwd)/{input[0]}
|
||||||
|
export FUZZ_MAIN={fuzz_main}
|
||||||
|
export FUZZ_INPUT={fuzz_input}
|
||||||
|
export FUZZ_INPUT_LEN={fuzz_len}
|
||||||
|
export BREAKPOINT={bkp}
|
||||||
|
export TRACE_DUMP=$(pwd)/{output[0]}
|
||||||
|
export DO_SHOWMAP=$(pwd)/{input[3]}
|
||||||
|
export TIME_DUMP=$(pwd)/{output[1]}
|
||||||
|
set +e
|
||||||
|
../fuzzer.sh
|
||||||
|
exit 0
|
||||||
|
"""
|
||||||
|
if wildcards.fuzzer.find('random') >= 0:
|
||||||
|
script="export FUZZ_RANDOM=1\n"+script
|
||||||
|
shell(script)
|
||||||
|
|
||||||
|
rule tarnsform_trace:
|
||||||
|
input:
|
||||||
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.ron"
|
||||||
|
output:
|
||||||
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.csv"
|
||||||
|
shell:
|
||||||
|
"$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} > {output[0]}"
|
||||||
|
|
||||||
|
rule trace2gantt:
|
||||||
|
input:
|
||||||
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.csv"
|
||||||
|
output:
|
||||||
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png"
|
||||||
|
shell:
|
||||||
|
"Rscript --vanilla $(pwd)/../../../../state2gantt/gantt.R {input}"
|
||||||
|
|
||||||
|
rule all_main:
|
||||||
|
input:
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3))
|
||||||
|
|
||||||
|
rule all_main_int:
|
||||||
|
input:
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_int','feedgeneration10_int','state_int'], target=['waters_int','watersv2_int'],num=range(0,4))
|
||||||
|
|
||||||
|
rule all_compare_feedgeneration:
|
||||||
|
input:
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters_int','watersv2'],num=range(0,10))
|
||||||
|
|
||||||
|
rule all_compare_feedgeneration_int:
|
||||||
|
input:
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1_int','feedgeneration10_int','feedgeneration100_int'], target=['waters_int','watersv2_int'],num=range(0,10))
|
||||||
|
|
||||||
|
rule all_compare_afl:
|
||||||
|
input:
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters','watersv2'],num=range(0,10))
|
||||||
|
|
||||||
|
rule all_compare_afl_int:
|
||||||
|
input:
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=range(0,10))
|
||||||
|
|
||||||
|
rule all_images:
|
||||||
|
input:
|
||||||
|
expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3))
|
||||||
|
|
||||||
|
rule all_images_int:
|
||||||
|
input:
|
||||||
|
expand("{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png",remote=remote, fuzzer=['afl_int','feedgeneration10_int','state_int'], target=['waters_int','watersv2_int'],num=range(0,3))
|
||||||
|
|
||||||
|
rule clusterfuzz:
|
||||||
|
input:
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','feedgeneration10','state'], target=['waters','watersv2'],num=MY_RANGE_A),
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_int','feedgeneration10_int','state_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_A),
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters_int','watersv2'],num=MY_RANGE_B),
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1_int','feedgeneration10_int','feedgeneration100_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B),
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','frafl','feedlongest'], target=['waters','watersv2'],num=MY_RANGE_B),
|
||||||
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl_int','frafl_int','feedlongest_int'], target=['waters_int','watersv2_int'],num=MY_RANGE_B),
|
||||||
|
|
||||||
|
rule all_bins:
|
||||||
|
input:
|
||||||
|
expand("bins/target_{target}{flag}",target=['random','afl','frafl','state','feedgeneration100'],flag=['','_int'])
|
83
fuzzers/FRET/benchmark/plot_comparison.r
Normal file
83
fuzzers/FRET/benchmark/plot_comparison.r
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
library("mosaic")
|
||||||
|
args = commandArgs(trailingOnly=TRUE)
|
||||||
|
|
||||||
|
#myolors=c("#339933","#0066ff","#993300") # grün, balu, rot
|
||||||
|
myolors=c("dark green","dark blue","dark red", "yellow") # grün, balu, rot
|
||||||
|
|
||||||
|
if (length(args)==0) {
|
||||||
|
runtype="timedump"
|
||||||
|
target="waters"
|
||||||
|
filename_1=sprintf("%s.png",target)
|
||||||
|
filename_2=sprintf("%s_maxline.png",target)
|
||||||
|
filename_3=sprintf("%s_hist.png",target)
|
||||||
|
} else {
|
||||||
|
runtype=args[1]
|
||||||
|
target=args[2]
|
||||||
|
filename_1=sprintf("%s.png",args[2])
|
||||||
|
filename_2=sprintf("%s_maxline.png",args[2])
|
||||||
|
filename_3=sprintf("%s_hist.png",args[2])
|
||||||
|
# filename_1=args[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
file_1=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_state",runtype,target)
|
||||||
|
file_2=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_afl",runtype,target)
|
||||||
|
file_3=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_random",runtype,target)
|
||||||
|
file_4=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_graph",runtype,target)
|
||||||
|
timetrace <- read.table(file_1, quote="\"", comment.char="")
|
||||||
|
timetrace_afl <- read.table(file_2, quote="\"", comment.char="")
|
||||||
|
timetrace_rand <- read.table(file_3, quote="\"", comment.char="")
|
||||||
|
timetrace_graph <- read.table(file_4, quote="\"", comment.char="")
|
||||||
|
timetrace[[2]]=seq_len(length(timetrace[[1]]))
|
||||||
|
timetrace_afl[[2]]=seq_len(length(timetrace_afl[[1]]))
|
||||||
|
timetrace_rand[[2]]=seq_len(length(timetrace_rand[[1]]))
|
||||||
|
timetrace_graph[[2]]=seq_len(length(timetrace_graph[[1]]))
|
||||||
|
names(timetrace)[1] <- "timetrace"
|
||||||
|
names(timetrace)[2] <- "iter"
|
||||||
|
names(timetrace_afl)[1] <- "timetrace"
|
||||||
|
names(timetrace_afl)[2] <- "iter"
|
||||||
|
names(timetrace_rand)[1] <- "timetrace"
|
||||||
|
names(timetrace_rand)[2] <- "iter"
|
||||||
|
names(timetrace_graph)[1] <- "timetrace"
|
||||||
|
names(timetrace_graph)[2] <- "iter"
|
||||||
|
|
||||||
|
png(file=filename_1)
|
||||||
|
# pdf(file=filename_1,width=8, height=8)
|
||||||
|
plot(timetrace[[2]],timetrace[[1]], col=myolors[1], xlab="iters", ylab="wcet", pch='.')
|
||||||
|
points(timetrace_afl[[2]],timetrace_afl[[1]], col=myolors[2], pch='.')
|
||||||
|
points(timetrace_rand[[2]],timetrace_rand[[1]], col=myolors[3], pch='.')
|
||||||
|
points(timetrace_graph[[2]],timetrace_graph[[1]], col=myolors[4], pch='.')
|
||||||
|
abline(lm(timetrace ~ iter, data=timetrace),col=myolors[1])
|
||||||
|
abline(lm(timetrace ~ iter, data=timetrace_afl),col=myolors[2])
|
||||||
|
abline(lm(timetrace ~ iter, data=timetrace_rand),col=myolors[3])
|
||||||
|
dev.off()
|
||||||
|
|
||||||
|
png(file=filename_3)
|
||||||
|
gf_histogram(~ timetrace,data=timetrace, fill=myolors[1]) %>%
|
||||||
|
gf_histogram(~ timetrace,data=timetrace_afl, fill=myolors[2]) %>%
|
||||||
|
gf_histogram(~ timetrace,data=timetrace_rand, fill=myolors[3]) %>%
|
||||||
|
gf_histogram(~ timetrace,data=timetrace_graph, fill=myolors[4])
|
||||||
|
dev.off()
|
||||||
|
|
||||||
|
# Takes a flat list
|
||||||
|
trace2maxline <- function(tr) {
|
||||||
|
maxline = tr
|
||||||
|
for (var in seq_len(length(maxline))[2:length(maxline)]) {
|
||||||
|
maxline[var] = max(maxline[var],maxline[var-1])
|
||||||
|
}
|
||||||
|
#plot(seq_len(length(maxline)),maxline,"l",xlab="Index",ylab="WOET")
|
||||||
|
return(maxline)
|
||||||
|
}
|
||||||
|
timetrace[[1]] <- trace2maxline(timetrace[[1]])
|
||||||
|
timetrace_afl[[1]] <- trace2maxline(timetrace_afl[[1]])
|
||||||
|
timetrace_rand[[1]] <- trace2maxline(timetrace_rand[[1]])
|
||||||
|
timetrace_graph[[1]] <- trace2maxline(timetrace_graph[[1]])
|
||||||
|
|
||||||
|
png(file=filename_2)
|
||||||
|
plot(timetrace[[2]],timetrace[[1]], col=myolors[1], xlab="iters", ylab="wcet", pch='.')
|
||||||
|
points(timetrace_afl[[2]],timetrace_afl[[1]], col=myolors[2], pch='.')
|
||||||
|
points(timetrace_rand[[2]],timetrace_rand[[1]], col=myolors[3], pch='.')
|
||||||
|
points(timetrace_graph[[2]],timetrace_graph[[1]], col=myolors[4], pch='.')
|
||||||
|
#abline(lm(timetrace ~ iter, data=timetrace),col=myolors[1])
|
||||||
|
#abline(lm(timetrace ~ iter, data=timetrace_afl),col=myolors[2])
|
||||||
|
#abline(lm(timetrace ~ iter, data=timetrace_rand),col=myolors[3])
|
||||||
|
dev.off()
|
327
fuzzers/FRET/benchmark/plot_multi.r
Normal file
327
fuzzers/FRET/benchmark/plot_multi.r
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
library("mosaic")
|
||||||
|
library("dplyr")
|
||||||
|
library("foreach")
|
||||||
|
library("doParallel")
|
||||||
|
|
||||||
|
#setup parallel backend to use many processors
|
||||||
|
cores=detectCores()
|
||||||
|
cl <- makeCluster(cores[1]-1) #not to overload your computer
|
||||||
|
registerDoParallel(cl)
|
||||||
|
|
||||||
|
args = commandArgs(trailingOnly=TRUE)
|
||||||
|
|
||||||
|
if (length(args)==0) {
|
||||||
|
runtype="timedump_253048_1873f6_all/timedump"
|
||||||
|
target="waters_int"
|
||||||
|
outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/"
|
||||||
|
#MY_SELECTION <- c('state', 'afl', 'graph', 'random')
|
||||||
|
SAVE_FILE=TRUE
|
||||||
|
} else {
|
||||||
|
runtype=args[1]
|
||||||
|
target=args[2]
|
||||||
|
outputpath=args[3]
|
||||||
|
MY_SELECTION <- args[4:length(args)]
|
||||||
|
SAVE_FILE=TRUE
|
||||||
|
}
|
||||||
|
worst_cases <- list(waters=0, waters_int=0, tmr=405669, micro_longint=0)
|
||||||
|
worst_case <- worst_cases[[target]]
|
||||||
|
if (is.null(worst_case)) {
|
||||||
|
worst_case = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#MY_COLORS=c("green","blue","red", "orange", "pink", "black")
|
||||||
|
MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown")
|
||||||
|
BENCHDIR=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s",runtype)
|
||||||
|
BASENAMES=Filter(function(x) x!="" && substr(x,1,1)!='.',list.dirs(BENCHDIR,full.names=FALSE))
|
||||||
|
PATTERNS="%s.[0-9]*$"
|
||||||
|
#RIBBON='sd'
|
||||||
|
#RIBBON='span'
|
||||||
|
RIBBON='both'
|
||||||
|
DRAW_WC = worst_case > 0
|
||||||
|
LEGEND_POS="topright"
|
||||||
|
#LEGEND_POS="bottomright"
|
||||||
|
CONTINUE_LINE_TO_END=FALSE
|
||||||
|
|
||||||
|
# https://www.r-bloggers.com/2013/04/how-to-change-the-alpha-value-of-colours-in-r/
|
||||||
|
alpha <- function(col, alpha=1){
|
||||||
|
if(missing(col))
|
||||||
|
stop("Please provide a vector of colours.")
|
||||||
|
apply(sapply(col, col2rgb)/255, 2,
|
||||||
|
function(x)
|
||||||
|
rgb(x[1], x[2], x[3], alpha=alpha))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Trimm a list of data frames to common length
|
||||||
|
trim_data <- function(input,len=NULL) {
|
||||||
|
if (is.null(len)) {
|
||||||
|
len <- min(sapply(input, function(v) dim(v)[1]))
|
||||||
|
}
|
||||||
|
return(lapply(input, function(d) slice_head(d,n=len)))
|
||||||
|
}
|
||||||
|
|
||||||
|
length_of_data <- function(input) {
|
||||||
|
min(sapply(input, function(v) dim(v)[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Takes a flat list
|
||||||
|
trace2maxline <- function(tr) {
|
||||||
|
maxline = tr
|
||||||
|
for (var in seq_len(length(maxline))[2:length(maxline)]) {
|
||||||
|
#if (maxline[var]>1000000000) {
|
||||||
|
# maxline[var]=maxline[var-1]
|
||||||
|
#} else {
|
||||||
|
maxline[var] = max(maxline[var],maxline[var-1])
|
||||||
|
#}
|
||||||
|
}
|
||||||
|
#plot(seq_len(length(maxline)),maxline,"l",xlab="Index",ylab="WOET")
|
||||||
|
return(maxline)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Take a list of data frames, output same form but maxlines
|
||||||
|
data2maxlines <- function(tr) {
|
||||||
|
min_length <- min(sapply(tr, function(v) dim(v)[1]))
|
||||||
|
maxline <- tr
|
||||||
|
for (var in seq_len(length(tr))) {
|
||||||
|
maxline[[var]][[1]]=trace2maxline(tr[[var]][[1]])
|
||||||
|
}
|
||||||
|
return(maxline)
|
||||||
|
}
|
||||||
|
# Take a multi-column data frame, output same form but maxlines
|
||||||
|
frame2maxlines <- function(tr) {
|
||||||
|
for (var in seq_len(length(tr))) {
|
||||||
|
tr[[var]]=trace2maxline(tr[[var]])
|
||||||
|
}
|
||||||
|
return(tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
trace2maxpoints <- function(tr) {
|
||||||
|
minval = tr[1,1]
|
||||||
|
collect = tr[1,]
|
||||||
|
for (i in seq_len(dim(tr)[1])) {
|
||||||
|
if (minval < tr[i,1]) {
|
||||||
|
collect = rbind(collect,tr[i,])
|
||||||
|
minval = tr[i,1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp = tr[dim(tr)[1],]
|
||||||
|
tmp[1] = minval[1]
|
||||||
|
collect = rbind(collect,tmp)
|
||||||
|
return(collect)
|
||||||
|
}
|
||||||
|
|
||||||
|
sample_maxpoints <- function(tr,po) {
|
||||||
|
index = 1
|
||||||
|
collect=NULL
|
||||||
|
endpoint = dim(tr)[1]
|
||||||
|
for (p in po) {
|
||||||
|
if (p<=tr[1,2]) {
|
||||||
|
tmp = tr[index,]
|
||||||
|
tmp[2] = p
|
||||||
|
collect = rbind(collect, tmp)
|
||||||
|
} else if (p>=tr[endpoint,2]) {
|
||||||
|
tmp = tr[endpoint,]
|
||||||
|
tmp[2] = p
|
||||||
|
collect = rbind(collect, tmp)
|
||||||
|
} else {
|
||||||
|
for (i in seq(index,endpoint)-1) {
|
||||||
|
if (p >= tr[i,2] && p<tr[i+1,2]) {
|
||||||
|
tmp = tr[i,]
|
||||||
|
tmp[2] = p
|
||||||
|
collect = rbind(collect, tmp)
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(collect)
|
||||||
|
}
|
||||||
|
|
||||||
|
#https://www.r-bloggers.com/2012/01/parallel-r-loops-for-windows-and-linux/
|
||||||
|
all_runtypetables <- foreach (bn=BASENAMES) %do% {
|
||||||
|
runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern=sprintf(PATTERNS,target),full.names = TRUE)
|
||||||
|
if (length(runtypefiles) > 0) {
|
||||||
|
runtypetables_reduced <- foreach(i=seq_len(length(runtypefiles))) %dopar% {
|
||||||
|
rtable = read.csv(runtypefiles[[i]], col.names=c(sprintf("%s%d",bn,i),sprintf("times%d",i)))
|
||||||
|
trace2maxpoints(rtable)
|
||||||
|
}
|
||||||
|
#runtypetables <- lapply(seq_len(length(runtypefiles)),
|
||||||
|
# function(i)read.csv(runtypefiles[[i]], col.names=c(sprintf("%s%d",bn,i),sprintf("times%d",i))))
|
||||||
|
#runtypetables_reduced <- lapply(runtypetables, trace2maxpoints)
|
||||||
|
runtypetables_reduced
|
||||||
|
#all_runtypetables = c(all_runtypetables, list(runtypetables_reduced))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all_runtypetables = all_runtypetables[lapply(all_runtypetables, length) > 0]
|
||||||
|
all_min_points = foreach(rtt=all_runtypetables,.combine = cbind) %do% {
|
||||||
|
bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1)
|
||||||
|
ret = data.frame(min(unlist(lapply(rtt, function(v) v[dim(v)[1],2]))))
|
||||||
|
names(ret)[1] = bn
|
||||||
|
ret/(3600 * 1000)
|
||||||
|
}
|
||||||
|
all_max_points = foreach(rtt=all_runtypetables,.combine = cbind) %do% {
|
||||||
|
bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1)
|
||||||
|
ret = data.frame(max(unlist(lapply(rtt, function(v) v[dim(v)[1],2]))))
|
||||||
|
names(ret)[1] = bn
|
||||||
|
ret/(3600 * 1000)
|
||||||
|
}
|
||||||
|
all_points = sort(unique(Reduce(c, lapply(all_runtypetables, function(v) Reduce(c, lapply(v, function(w) w[[2]]))))))
|
||||||
|
all_maxlines <- foreach (rtt=all_runtypetables) %do% {
|
||||||
|
bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1)
|
||||||
|
runtypetables_sampled = foreach(v=rtt) %dopar% {
|
||||||
|
sample_maxpoints(v, all_points)[1]
|
||||||
|
}
|
||||||
|
#runtypetables_sampled = lapply(rtt, function(v) sample_maxpoints(v, all_points)[1])
|
||||||
|
tmp_frame <- Reduce(cbind, runtypetables_sampled)
|
||||||
|
statframe <- data.frame(rowMeans(tmp_frame),apply(tmp_frame, 1, sd),apply(tmp_frame, 1, min),apply(tmp_frame, 1, max), apply(tmp_frame, 1, median))
|
||||||
|
names(statframe) <- c(bn, sprintf("%s_sd",bn), sprintf("%s_min",bn), sprintf("%s_max",bn), sprintf("%s_med",bn))
|
||||||
|
#statframe[sprintf("%s_times",bn)] = all_points
|
||||||
|
round(statframe)
|
||||||
|
#all_maxlines = c(all_maxlines, list(round(statframe)))
|
||||||
|
}
|
||||||
|
one_frame<-data.frame(all_maxlines)
|
||||||
|
one_frame[length(one_frame)+1] <- all_points/(3600 * 1000)
|
||||||
|
names(one_frame)[length(one_frame)] <- 'time'
|
||||||
|
|
||||||
|
typenames = names(one_frame)[which(names(one_frame) != 'time')]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_sd"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_med"))]
|
||||||
|
ylow=min(one_frame[typenames])
|
||||||
|
yhigh=max(one_frame[typenames],worst_case)
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_min"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_max"))]
|
||||||
|
|
||||||
|
ml2lines <- function(ml,lim) {
|
||||||
|
lines = NULL
|
||||||
|
last = 0
|
||||||
|
for (i in seq_len(dim(ml)[1])) {
|
||||||
|
if (!CONTINUE_LINE_TO_END && lim<ml[i,2]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lines = rbind(lines, cbind(X=last, Y=ml[i,1]))
|
||||||
|
lines = rbind(lines, cbind(X=ml[i,2], Y=ml[i,1]))
|
||||||
|
last = ml[i,2]
|
||||||
|
}
|
||||||
|
return(lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
plotting <- function(selection, filename, MY_COLORS_) {
|
||||||
|
# filter out names of iters and sd cols
|
||||||
|
typenames = names(one_frame)[which(names(one_frame) != 'times')]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_sd"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_med"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_min"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_max"))]
|
||||||
|
typenames = selection[which(selection %in% typenames)]
|
||||||
|
if (length(typenames) == 0) {return()}
|
||||||
|
|
||||||
|
h_ = 500
|
||||||
|
w_ = h_*4/3
|
||||||
|
|
||||||
|
if (SAVE_FILE) {png(file=sprintf("%s%s_%s.png",outputpath,target,filename), width=w_, height=h_)}
|
||||||
|
par(mar=c(4,4,1,1))
|
||||||
|
par(oma=c(0,0,0,0))
|
||||||
|
|
||||||
|
plot(c(1,max(one_frame['time'])),c(ylow,yhigh), col='white', xlab="Time [h]", ylab="WORT [insn]", pch='.')
|
||||||
|
|
||||||
|
for (t in seq_len(length(typenames))) {
|
||||||
|
#proj = one_frame[seq(1, dim(one_frame)[1], by=max(1, length(one_frame[[1]])/(10*w_))),]
|
||||||
|
#points(proj[c('iters',typenames[t])], col=MY_COLORS_[t], pch='.')
|
||||||
|
avglines = ml2lines(one_frame[c(typenames[t],'time')],all_max_points[typenames[t]])
|
||||||
|
#lines(avglines, col=MY_COLORS_[t])
|
||||||
|
medlines = ml2lines(one_frame[c(sprintf("%s_med",typenames[t]),'time')],all_max_points[typenames[t]])
|
||||||
|
lines(medlines, col=MY_COLORS_[t], lty='solid')
|
||||||
|
milines = NULL
|
||||||
|
malines = NULL
|
||||||
|
milines = ml2lines(one_frame[c(sprintf("%s_min",typenames[t]),'time')],all_max_points[typenames[t]])
|
||||||
|
malines = ml2lines(one_frame[c(sprintf("%s_max",typenames[t]),'time')],all_max_points[typenames[t]])
|
||||||
|
if (exists("RIBBON") && ( RIBBON=='max' )) {
|
||||||
|
#lines(milines, col=MY_COLORS_[t], lty='dashed')
|
||||||
|
lines(malines, col=MY_COLORS_[t], lty='dashed')
|
||||||
|
#points(proj[c('iters',sprintf("%s_min",typenames[t]))], col=MY_COLORS_[t], pch='.')
|
||||||
|
#points(proj[c('iters',sprintf("%s_max",typenames[t]))], col=MY_COLORS_[t], pch='.')
|
||||||
|
}
|
||||||
|
if (exists("RIBBON") && RIBBON != '') {
|
||||||
|
for (i in seq_len(dim(avglines)[1]-1)) {
|
||||||
|
if (RIBBON=='both') {
|
||||||
|
# draw boxes
|
||||||
|
x_l <- milines[i,][['X']]
|
||||||
|
x_r <- milines[i+1,][['X']]
|
||||||
|
y_l <- milines[i,][['Y']]
|
||||||
|
y_h <- malines[i,][['Y']]
|
||||||
|
rect(x_l, y_l, x_r, y_h, col=alpha(MY_COLORS_[t], alpha=0.1), lwd=0)
|
||||||
|
}
|
||||||
|
if (FALSE && RIBBON=='span') {
|
||||||
|
# draw boxes
|
||||||
|
x_l <- milines[i,][['X']]
|
||||||
|
x_r <- milines[i+1,][['X']]
|
||||||
|
y_l <- milines[i,][['Y']]
|
||||||
|
y_h <- malines[i,][['Y']]
|
||||||
|
rect(x_l, y_l, x_r, y_h, col=alpha(MY_COLORS_[t], alpha=0.1), lwd=0)
|
||||||
|
}
|
||||||
|
#if (FALSE && RIBBON=='both' || RIBBON=='sd') {
|
||||||
|
# # draw sd
|
||||||
|
# x_l <- avglines[i,][['X']]
|
||||||
|
# x_r <- avglines[i+1,][['X']]
|
||||||
|
# y_l <- avglines[i,][['Y']]-one_frame[ceiling(i/2),][[sprintf("%s_sd",typenames[t])]]
|
||||||
|
# y_h <- avglines[i,][['Y']]+one_frame[ceiling(i/2),][[sprintf("%s_sd",typenames[t])]]
|
||||||
|
# if (x_r != x_l) {
|
||||||
|
# rect(x_l, y_l, x_r, y_h, col=alpha(MY_COLORS_[t], alpha=0.1), lwd=0)
|
||||||
|
# }
|
||||||
|
#}
|
||||||
|
#sd_ <- row[sprintf("%s_sd",typenames[t])][[1]]
|
||||||
|
#min_ <- row[sprintf("%s_min",typenames[t])][[1]]
|
||||||
|
#max_ <- row[sprintf("%s_max",typenames[t])][[1]]
|
||||||
|
#if (exists("RIBBON")) {
|
||||||
|
# switch (RIBBON,
|
||||||
|
# 'sd' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)),
|
||||||
|
# 'both' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)),
|
||||||
|
# 'span' = #arrows(x_, min_, x_, max_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03))
|
||||||
|
# )
|
||||||
|
#}
|
||||||
|
##arrows(x_, y_-sd_, x_, y_+sd_, length=0.05, angle=90, code=3, col=alpha(MY_COLORS[t], alpha=0.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leglines=typenames
|
||||||
|
if (DRAW_WC) {
|
||||||
|
lines(c(0,length(one_frame[[1]])),y=c(worst_case,worst_case), lty='dotted')
|
||||||
|
leglines=c(typenames, 'worst observed')
|
||||||
|
}
|
||||||
|
legend(LEGEND_POS, legend=leglines,#"topleft"
|
||||||
|
col=c(MY_COLORS_[1:length(typenames)],"black"),
|
||||||
|
lty=c(rep("solid",length(typenames)),"dotted"))
|
||||||
|
|
||||||
|
if (SAVE_FILE) {dev.off()}
|
||||||
|
}
|
||||||
|
stopCluster(cl)
|
||||||
|
|
||||||
|
par(mar=c(3.8,3.8,0,0))
|
||||||
|
par(oma=c(0,0,0,0))
|
||||||
|
|
||||||
|
#RIBBON='both'
|
||||||
|
#MY_SELECTION = c('state_int','generation100_int')
|
||||||
|
#MY_SELECTION = c('state_int','frafl_int')
|
||||||
|
|
||||||
|
if (exists("MY_SELECTION")) {
|
||||||
|
plotting(MY_SELECTION, 'custom', MY_COLORS[c(1,2)])
|
||||||
|
} else {
|
||||||
|
# MY_SELECTION=c('state', 'afl', 'random', 'feedlongest', 'feedgeneration', 'feedgeneration10')
|
||||||
|
#MY_SELECTION=c('state_int', 'afl_int', 'random_int', 'feedlongest_int', 'feedgeneration_int', 'feedgeneration10_int')
|
||||||
|
#MY_SELECTION=c('state', 'frAFL', 'statenohash', 'feedgeneration10')
|
||||||
|
#MY_SELECTION=c('state_int', 'frAFL_int', 'statenohash_int', 'feedgeneration10_int')
|
||||||
|
MY_SELECTION=typenames
|
||||||
|
RIBBON='both'
|
||||||
|
for (i in seq_len(length(MY_SELECTION))) {
|
||||||
|
n <- MY_SELECTION[i]
|
||||||
|
plotting(c(n), n, c(MY_COLORS[i]))
|
||||||
|
}
|
||||||
|
RIBBON='max'
|
||||||
|
plotting(MY_SELECTION,'all', MY_COLORS)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (t in seq_len(length(typenames))) {
|
||||||
|
li = one_frame[dim(one_frame)[1],]
|
||||||
|
pear = (li[[typenames[[t]]]]-li[[sprintf("%s_med",typenames[[t]])]])/li[[sprintf("%s_sd",typenames[[t]])]]
|
||||||
|
print(sprintf("%s pearson: %g",typenames[[t]],pear))
|
||||||
|
}
|
24
fuzzers/FRET/benchmark/target_symbols.csv
Normal file
24
fuzzers/FRET/benchmark/target_symbols.csv
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
kernel,main_function,input_symbol,input_size,return_function
|
||||||
|
mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return
|
||||||
|
audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return
|
||||||
|
epic,epic_main,epic_image,4096,epic_return
|
||||||
|
dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return
|
||||||
|
fft,fft_main,fft_twidtable,2046,fft_return
|
||||||
|
bsort,bsort_main,bsort_Array,400,bsort_return
|
||||||
|
insertsort,insertsort_main,insertsort_a,400,insertsort_return
|
||||||
|
g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return
|
||||||
|
rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return
|
||||||
|
rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return
|
||||||
|
huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return
|
||||||
|
huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return
|
||||||
|
gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return
|
||||||
|
tmr,main,FUZZ_INPUT,32,trigger_Qemu_break
|
||||||
|
tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break
|
||||||
|
lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break
|
||||||
|
waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
|
||||||
|
watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
|
||||||
|
waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
|
||||||
|
watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
|
||||||
|
micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break
|
||||||
|
micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break
|
||||||
|
micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break
|
|
2
fuzzers/FRET/example/build.sh
Executable file
2
fuzzers/FRET/example/build.sh
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
arm-none-eabi-gcc -ggdb -ffreestanding -nostartfiles -lgcc -T mps2_m3.ld -mcpu=cortex-m3 main.c startup.c -o example.elf
|
38
fuzzers/FRET/example/main.c
Normal file
38
fuzzers/FRET/example/main.c
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
int BREAKPOINT() {
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int LLVMFuzzerTestOneInput(unsigned int* Data, unsigned int Size) {
|
||||||
|
//if (Data[3] == 0) {while(1){}} // cause a timeout
|
||||||
|
for (int i=0; i<Size; i++) {
|
||||||
|
// if (Data[i] > 0xFFd0 && Data[i] < 0xFFFF) {return 1;} // cause qemu to crash
|
||||||
|
for (int j=i+1; j<Size; j++) {
|
||||||
|
if (Data[j] == 0) {continue;}
|
||||||
|
if (Data[j]>Data[i]) {
|
||||||
|
int tmp = Data[i];
|
||||||
|
Data[i]=Data[j];
|
||||||
|
Data[j]=tmp;
|
||||||
|
if (Data[i] <= 100) {j--;}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return BREAKPOINT();
|
||||||
|
}
|
||||||
|
unsigned int FUZZ_INPUT[] = {
|
||||||
|
101,201,700,230,860,
|
||||||
|
234,980,200,340,678,
|
||||||
|
230,134,900,236,900,
|
||||||
|
123,800,123,658,607,
|
||||||
|
246,804,567,568,207,
|
||||||
|
407,246,678,457,892,
|
||||||
|
834,456,878,246,699,
|
||||||
|
854,234,844,290,125,
|
||||||
|
324,560,852,928,910,
|
||||||
|
790,853,345,234,586,
|
||||||
|
};
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
LLVMFuzzerTestOneInput(FUZZ_INPUT, 50);
|
||||||
|
}
|
143
fuzzers/FRET/example/mps2_m3.ld
Normal file
143
fuzzers/FRET/example/mps2_m3.ld
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS V202112.00
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
MEMORY
|
||||||
|
{
|
||||||
|
RAM (xrw) : ORIGIN = 0x00000000, LENGTH = 4M
|
||||||
|
/* Originally */
|
||||||
|
/* FLASH (xr) : ORIGIN = 0x00000000, LENGTH = 4M */
|
||||||
|
/* RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 4M */
|
||||||
|
}
|
||||||
|
ENTRY(Reset_Handler)
|
||||||
|
|
||||||
|
_Min_Heap_Size = 0x300000 ; /* Required amount of heap. */
|
||||||
|
_Min_Stack_Size = 0x4000 ; /* Required amount of stack. */
|
||||||
|
M_VECTOR_RAM_SIZE = (16 + 48) * 4;
|
||||||
|
_estack = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
|
|
||||||
|
SECTIONS
|
||||||
|
{
|
||||||
|
|
||||||
|
.isr_vector :
|
||||||
|
{
|
||||||
|
__vector_table = .;
|
||||||
|
KEEP(*(.isr_vector))
|
||||||
|
. = ALIGN(4);
|
||||||
|
} > RAM /* FLASH */
|
||||||
|
|
||||||
|
.text :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.text*)
|
||||||
|
KEEP (*(.init))
|
||||||
|
KEEP (*(.fini))
|
||||||
|
KEEP(*(.eh_frame))
|
||||||
|
*(.rodata*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_etext = .;
|
||||||
|
} > RAM /* FLASH */
|
||||||
|
|
||||||
|
.ARM.extab :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
*(.ARM.extab* .gnu.linkonce.armextab.*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >RAM /* FLASH */
|
||||||
|
|
||||||
|
.ARM :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
__exidx_start = .;
|
||||||
|
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
|
||||||
|
__exidx_end = .;
|
||||||
|
. = ALIGN(4);
|
||||||
|
} >RAM /* FLASH */
|
||||||
|
|
||||||
|
.interrupts_ram :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
__VECTOR_RAM__ = .;
|
||||||
|
__interrupts_ram_start__ = .;
|
||||||
|
. += M_VECTOR_RAM_SIZE;
|
||||||
|
. = ALIGN(4);
|
||||||
|
__interrupts_ram_end = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
_sidata = LOADADDR(.data);
|
||||||
|
|
||||||
|
.data : /* AT ( _sidata ) */
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sdata = .;
|
||||||
|
*(.data*)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_edata = .;
|
||||||
|
} > RAM /* RAM AT > FLASH */
|
||||||
|
|
||||||
|
.uninitialized (NOLOAD):
|
||||||
|
{
|
||||||
|
. = ALIGN(32);
|
||||||
|
__uninitialized_start = .;
|
||||||
|
*(.uninitialized)
|
||||||
|
KEEP(*(.keep.uninitialized))
|
||||||
|
. = ALIGN(32);
|
||||||
|
__uninitialized_end = .;
|
||||||
|
} > RAM
|
||||||
|
|
||||||
|
.bss :
|
||||||
|
{
|
||||||
|
. = ALIGN(4);
|
||||||
|
_sbss = .;
|
||||||
|
__bss_start__ = _sbss;
|
||||||
|
*(.bss*)
|
||||||
|
*(COMMON)
|
||||||
|
. = ALIGN(4);
|
||||||
|
_ebss = .;
|
||||||
|
__bss_end__ = _ebss;
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
.heap :
|
||||||
|
{
|
||||||
|
. = ALIGN(8);
|
||||||
|
PROVIDE ( end = . );
|
||||||
|
PROVIDE ( _end = . );
|
||||||
|
_heap_bottom = .;
|
||||||
|
. = . + _Min_Heap_Size;
|
||||||
|
_heap_top = .;
|
||||||
|
. = . + _Min_Stack_Size;
|
||||||
|
. = ALIGN(8);
|
||||||
|
} >RAM
|
||||||
|
|
||||||
|
/* Set stack top to end of RAM, and stack limit move down by
|
||||||
|
* size of stack_dummy section */
|
||||||
|
__StackTop = ORIGIN(RAM) + LENGTH(RAM);
|
||||||
|
__StackLimit = __StackTop - _Min_Stack_Size;
|
||||||
|
PROVIDE(__stack = __StackTop);
|
||||||
|
|
||||||
|
/* Check if data + heap + stack exceeds RAM limit */
|
||||||
|
ASSERT(__StackLimit >= _heap_top, "region RAM overflowed with stack")
|
||||||
|
}
|
||||||
|
|
114
fuzzers/FRET/example/startup.c
Normal file
114
fuzzers/FRET/example/startup.c
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* FreeRTOS V202112.00
|
||||||
|
* Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
|
* this software and associated documentation files (the "Software"), to deal in
|
||||||
|
* the Software without restriction, including without limitation the rights to
|
||||||
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||||
|
* the Software, and to permit persons to whom the Software is furnished to do so,
|
||||||
|
* subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||||
|
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*
|
||||||
|
* https://www.FreeRTOS.org
|
||||||
|
* https://github.com/FreeRTOS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef unsigned int uint32_t;
|
||||||
|
|
||||||
|
extern int main();
|
||||||
|
|
||||||
|
extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss;
|
||||||
|
|
||||||
|
/* Prevent optimization so gcc does not replace code with memcpy */
|
||||||
|
__attribute__( ( optimize( "O0" ) ) )
|
||||||
|
__attribute__( ( naked ) )
|
||||||
|
void Reset_Handler( void )
|
||||||
|
{
|
||||||
|
/* set stack pointer */
|
||||||
|
__asm volatile ( "ldr r0, =_estack" );
|
||||||
|
__asm volatile ( "mov sp, r0" );
|
||||||
|
|
||||||
|
/* copy .data section from flash to RAM */
|
||||||
|
// Not needed for this example, see linker script
|
||||||
|
// for( uint32_t * src = &_sidata, * dest = &_sdata; dest < &_edata; )
|
||||||
|
// {
|
||||||
|
// *dest++ = *src++;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/* zero out .bss section */
|
||||||
|
for( uint32_t * dest = &_sbss; dest < &_ebss; )
|
||||||
|
{
|
||||||
|
*dest++ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* jump to board initialisation */
|
||||||
|
void _start( void );
|
||||||
|
_start();
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint32_t * isr_vector[] __attribute__( ( section( ".isr_vector" ) ) ) =
|
||||||
|
{
|
||||||
|
( uint32_t * ) &_estack,
|
||||||
|
( uint32_t * ) &Reset_Handler, /* Reset -15 */
|
||||||
|
0, /* NMI_Handler -14 */
|
||||||
|
0, /* HardFault_Handler -13 */
|
||||||
|
0, /* MemManage_Handler -12 */
|
||||||
|
0, /* BusFault_Handler -11 */
|
||||||
|
0, /* UsageFault_Handler -10 */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* reserved -6 */
|
||||||
|
0, /* SVC_Handler -5 */
|
||||||
|
0, /* DebugMon_Handler -4 */
|
||||||
|
0, /* reserved */
|
||||||
|
0, /* PendSV handler -2 */
|
||||||
|
0, /* SysTick_Handler -1 */
|
||||||
|
0, /* uart0 receive 0 */
|
||||||
|
0, /* uart0 transmit */
|
||||||
|
0, /* uart1 receive */
|
||||||
|
0, /* uart1 transmit */
|
||||||
|
0, /* uart 2 receive */
|
||||||
|
0, /* uart 2 transmit */
|
||||||
|
0, /* GPIO 0 combined interrupt */
|
||||||
|
0, /* GPIO 2 combined interrupt */
|
||||||
|
0, /* Timer 0 */
|
||||||
|
0, /* Timer 1 */
|
||||||
|
0, /* Dial Timer */
|
||||||
|
0, /* SPI0 SPI1 */
|
||||||
|
0, /* uart overflow 1, 2,3 */
|
||||||
|
0, /* Ethernet 13 */
|
||||||
|
};
|
||||||
|
|
||||||
|
__attribute__( ( naked ) ) void exit(__attribute__((unused)) int status )
|
||||||
|
{
|
||||||
|
/* Force qemu to exit using ARM Semihosting */
|
||||||
|
__asm volatile (
|
||||||
|
"mov r1, r0\n"
|
||||||
|
"cmp r1, #0\n"
|
||||||
|
"bne .notclean\n"
|
||||||
|
"ldr r1, =0x20026\n" /* ADP_Stopped_ApplicationExit, a clean exit */
|
||||||
|
".notclean:\n"
|
||||||
|
"movs r0, #0x18\n" /* SYS_EXIT */
|
||||||
|
"bkpt 0xab\n"
|
||||||
|
"end: b end\n"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _start( void )
|
||||||
|
{
|
||||||
|
main( );
|
||||||
|
exit( 0 );
|
||||||
|
}
|
||||||
|
|
25
fuzzers/FRET/fuzzer.sh
Executable file
25
fuzzers/FRET/fuzzer.sh
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
|
||||||
|
cd "$parent_path"
|
||||||
|
|
||||||
|
[ -n "$1" -a "$1" != "+" -a -z "$KERNEL" ] && export KERNEL="$1"
|
||||||
|
[ -n "$2" -a "$2" != "+" -a -z "$FUZZ_MAIN" ] && export FUZZ_MAIN="$2"
|
||||||
|
[ -n "$3" -a "$3" != "+" -a -z "$FUZZ_INPUT" ] && export FUZZ_INPUT="$3"
|
||||||
|
[ -n "$4" -a "$4" != "+" -a -z "$FUZZ_INPUT_LEN" ] && export FUZZ_INPUT_LEN="$4"
|
||||||
|
[ -n "$5" -a "$5" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$5"
|
||||||
|
[ -n "$6" -a "$6" != "+" -a -z "$FUZZ_ITERS" ] && export FUZZ_ITERS="$6"
|
||||||
|
[ -n "$7" -a "$7" != "+" -a -z "$TIME_DUMP" ] && export TIME_DUMP="$7"
|
||||||
|
[ -n "$8" -a "$8" != "+" -a -z "$CASE_DUMP" ] && export CASE_DUMP="$8"
|
||||||
|
[ -n "$9" -a "$9" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$9"
|
||||||
|
[ -n "${10}" -a "${10}" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="${10}"
|
||||||
|
[ -n "${11}" -a "${11}" != "+" -a -z "$TRACE_DUMP" ] && export TRACE_DUMP="${11}"
|
||||||
|
|
||||||
|
[ -z "$FUZZER" ] && export FUZZER=target/debug/fret
|
||||||
|
set +e
|
||||||
|
$FUZZER -icount shift=4,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native -snapshot -drive if=none,format=qcow2,file=dummy.qcow2
|
||||||
|
if [ "$exitcode" = "101" ]
|
||||||
|
then
|
||||||
|
exit 101
|
||||||
|
else
|
||||||
|
exit 0
|
||||||
|
fi
|
344
fuzzers/FRET/src/clock.rs
Normal file
344
fuzzers/FRET/src/clock.rs
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
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, impl_serdeany,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{cell::UnsafeCell, cmp::max, env, fs::OpenOptions, io::Write, time::Instant};
|
||||||
|
use libafl::bolts::tuples::Named;
|
||||||
|
|
||||||
|
use libafl_qemu::{
|
||||||
|
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;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH;
|
||||||
|
|
||||||
|
//========== Metadata
|
||||||
|
#[derive(Debug, SerdeAny, Serialize, Deserialize)]
|
||||||
|
pub struct QemuIcountMetadata {
|
||||||
|
runtime: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A piece of metadata tracking all icounts
|
||||||
|
#[derive(Debug, SerdeAny, Serialize, Deserialize)]
|
||||||
|
pub struct IcHist (pub Vec<(u64, u128)>, pub (u64,u128));
|
||||||
|
|
||||||
|
//========== Observer
|
||||||
|
|
||||||
|
/// A simple observer, just overlooking the runtime of the target.
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct QemuClockObserver {
|
||||||
|
name: String,
|
||||||
|
start_tick: u64,
|
||||||
|
end_tick: 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(),
|
||||||
|
start_tick: 0,
|
||||||
|
end_tick: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the runtime for the last execution of this target.
|
||||||
|
#[must_use]
|
||||||
|
pub fn last_runtime(&self) -> u64 {
|
||||||
|
self.end_tick - self.start_tick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Observer<S> for QemuClockObserver
|
||||||
|
where
|
||||||
|
S: UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
|
// Only remember the pre-run ticks if presistent mode ist used
|
||||||
|
#[cfg(not(feature = "snapshot_restore"))]
|
||||||
|
unsafe {
|
||||||
|
self.start_tick=emu::icount_get_raw();
|
||||||
|
self.end_tick=self.start_tick;
|
||||||
|
}
|
||||||
|
// unsafe {
|
||||||
|
// println!("clock pre {}",emu::icount_get_raw());
|
||||||
|
// }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> {
|
||||||
|
unsafe { self.end_tick = emu::icount_get_raw() };
|
||||||
|
// println!("clock post {}", self.end_tick);
|
||||||
|
// println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick);
|
||||||
|
let metadata =_state.metadata_mut();
|
||||||
|
let hist = metadata.get_mut::<IcHist>();
|
||||||
|
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
|
||||||
|
match hist {
|
||||||
|
None => {
|
||||||
|
metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)],
|
||||||
|
(self.end_tick - self.start_tick, timestamp)));
|
||||||
|
}
|
||||||
|
Some(v) => {
|
||||||
|
v.0.push((self.end_tick - self.start_tick, timestamp));
|
||||||
|
if (v.1.0 < self.end_tick-self.start_tick) {
|
||||||
|
v.1 = (self.end_tick - self.start_tick, timestamp);
|
||||||
|
}
|
||||||
|
if v.0.len() >= 100 {
|
||||||
|
if let Ok(td) = env::var("TIME_DUMP") {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(td).expect("Could not open timedump");
|
||||||
|
let newv : Vec<(u64, u128)> = Vec::with_capacity(100);
|
||||||
|
for i in std::mem::replace(&mut v.0, newv).into_iter() {
|
||||||
|
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we don't write out values we don't need to remember them at all
|
||||||
|
v.0.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for QemuClockObserver {
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for QemuClockObserver {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
name: String::from("clock"),
|
||||||
|
start_tick: 0,
|
||||||
|
end_tick: 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).
|
||||||
|
/// It decides, if the given [`QemuClockObserver`] value of a run is interesting.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct ClockTimeFeedback {
|
||||||
|
exec_time: Option<Duration>,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for ClockTimeFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor + HasMetadata,
|
||||||
|
{
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
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(Duration::from_nanos(observer.last_runtime() << 4)); // Assume a somewhat realistic multiplier of clock, it does not matter
|
||||||
|
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() = self.exec_time;
|
||||||
|
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 ClockTimeFeedback {
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
self.name.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockTimeFeedback {
|
||||||
|
/// Creates a new [`ClockFeedback`], deciding if the value of a [`QemuClockObserver`] 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 [`QemuClockObserver`] 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")
|
||||||
|
}
|
||||||
|
}
|
715
fuzzers/FRET/src/fuzzer.rs
Normal file
715
fuzzers/FRET/src/fuzzer.rs
Normal file
@ -0,0 +1,715 @@
|
|||||||
|
//! A fuzzer using qemu in systemmode for binary-only coverage of kernels
|
||||||
|
//!
|
||||||
|
use core::time::Duration;
|
||||||
|
use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
bolts::{
|
||||||
|
core_affinity::Cores,
|
||||||
|
current_nanos,
|
||||||
|
launcher::Launcher,
|
||||||
|
rands::StdRand,
|
||||||
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
|
tuples::tuple_list,
|
||||||
|
AsSlice,
|
||||||
|
},
|
||||||
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
|
events::EventConfig,
|
||||||
|
executors::{ExitKind, TimeoutExecutor},
|
||||||
|
feedback_or,
|
||||||
|
feedback_or_fast,
|
||||||
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback},
|
||||||
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
monitors::MultiMonitor,
|
||||||
|
observers::{VariableMapObserver},
|
||||||
|
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||||
|
state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata},
|
||||||
|
Error,
|
||||||
|
prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver}, Evaluator, stages::StdMutationalStage,
|
||||||
|
};
|
||||||
|
use libafl_qemu::{
|
||||||
|
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor,
|
||||||
|
QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr,
|
||||||
|
emu::libafl_qemu_set_native_breakpoint, emu::libafl_qemu_remove_native_breakpoint,
|
||||||
|
};
|
||||||
|
use rand::{SeedableRng, StdRng, Rng};
|
||||||
|
use crate::{
|
||||||
|
clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist, FUZZ_START_TIMESTAMP},
|
||||||
|
qemustate::QemuStateRestoreHelper,
|
||||||
|
systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback},
|
||||||
|
mutational::MyStateStage,
|
||||||
|
mutational::{MINIMUM_INTER_ARRIVAL_TIME},
|
||||||
|
};
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
pub static mut RNG_SEED: u64 = 1;
|
||||||
|
|
||||||
|
pub static mut LIMIT : u32 = u32::MAX;
|
||||||
|
|
||||||
|
pub const MAX_NUM_INTERRUPT: usize = 32;
|
||||||
|
pub const DO_NUM_INTERRUPT: usize = 32;
|
||||||
|
pub static mut MAX_INPUT_SIZE: usize = 32;
|
||||||
|
/// Read ELF program headers to resolve physical load addresses.
|
||||||
|
fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
|
||||||
|
let ret;
|
||||||
|
for i in &tab.goblin().program_headers {
|
||||||
|
if i.vm_range().contains(&vaddr.try_into().unwrap()) {
|
||||||
|
ret = vaddr - TryInto::<GuestPhysAddr>::try_into(i.p_vaddr).unwrap()
|
||||||
|
+ TryInto::<GuestPhysAddr>::try_into(i.p_paddr).unwrap();
|
||||||
|
return ret - (ret % 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return vaddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
static mut libafl_interrupt_offsets : [u32; 32];
|
||||||
|
static mut libafl_num_interrupts : usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn fuzz() {
|
||||||
|
unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();}
|
||||||
|
let mut starttime = std::time::Instant::now();
|
||||||
|
if let Ok(s) = env::var("FUZZ_SIZE") {
|
||||||
|
str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number");
|
||||||
|
};
|
||||||
|
// Hardcoded parameters
|
||||||
|
let timeout = Duration::from_secs(10);
|
||||||
|
let broker_port = 1337;
|
||||||
|
let cores = Cores::from_cmdline("1").unwrap();
|
||||||
|
let corpus_dirs = [PathBuf::from("./corpus")];
|
||||||
|
let objective_dir = PathBuf::from("./crashes");
|
||||||
|
|
||||||
|
let mut elf_buffer = Vec::new();
|
||||||
|
let elf = EasyElf::from_file(
|
||||||
|
env::var("KERNEL").expect("KERNEL env not set"),
|
||||||
|
&mut elf_buffer,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
// the main address where the fuzzer starts
|
||||||
|
// if this is set for freeRTOS it has an influence on where the data will have to be written,
|
||||||
|
// since the startup routine copies the data segemnt to it's virtual address
|
||||||
|
let main_addr = elf
|
||||||
|
.resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()), 0);
|
||||||
|
if let Some(main_addr) = main_addr {
|
||||||
|
println!("main address = {:#x}", main_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_addr = elf
|
||||||
|
.resolve_symbol(
|
||||||
|
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("Symbol or env FUZZ_INPUT not found") as GuestPhysAddr;
|
||||||
|
let input_addr = virt2phys(input_addr,&elf) as GuestPhysAddr;
|
||||||
|
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
||||||
|
|
||||||
|
let test_length_ptr = elf
|
||||||
|
.resolve_symbol("FUZZ_LENGTH", 0).map(|x| x as GuestPhysAddr);
|
||||||
|
let test_length_ptr = Option::map_or(test_length_ptr, None, |x| Some(virt2phys(x,&elf)));
|
||||||
|
|
||||||
|
let input_counter_ptr = elf
|
||||||
|
.resolve_symbol(&env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), 0)
|
||||||
|
.map(|x| x as GuestPhysAddr);
|
||||||
|
let input_counter_ptr = Option::map_or(input_counter_ptr, None, |x| Some(virt2phys(x,&elf)));
|
||||||
|
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
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);
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
println!("TCB pointer at {:#x}", curr_tcb_pointer);
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let task_queue_addr = elf
|
||||||
|
.resolve_symbol("pxReadyTasksLists", 0)
|
||||||
|
.expect("Symbol pxReadyTasksLists not found");
|
||||||
|
// let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin());
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
println!("Task Queue at {:#x}", task_queue_addr);
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let svh = elf
|
||||||
|
.resolve_symbol("xPortPendSVHandler", 0)
|
||||||
|
.expect("Symbol xPortPendSVHandler not found");
|
||||||
|
// let svh=virt2phys(svh, &elf);
|
||||||
|
// let svh = elf
|
||||||
|
// .resolve_symbol("vPortEnterCritical", 0)
|
||||||
|
// .expect("Symbol vPortEnterCritical not found");
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let app_start = elf
|
||||||
|
.resolve_symbol("__APP_CODE_START__", 0)
|
||||||
|
.expect("Symbol __APP_CODE_START__ not found");
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let app_end = elf
|
||||||
|
.resolve_symbol("__APP_CODE_END__", 0)
|
||||||
|
.expect("Symbol __APP_CODE_END__ not found");
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let app_range = app_start..app_end;
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
dbg!(app_range.clone());
|
||||||
|
|
||||||
|
let breakpoint = elf
|
||||||
|
.resolve_symbol(
|
||||||
|
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("Symbol or env BREAKPOINT not found");
|
||||||
|
println!("Breakpoint address = {:#x}", breakpoint);
|
||||||
|
unsafe {
|
||||||
|
libafl_num_interrupts = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") {
|
||||||
|
unsafe {MAX_INPUT_SIZE = str::parse::<usize>(&input_len).expect("FUZZ_INPUT_LEN was not a number");}
|
||||||
|
}
|
||||||
|
unsafe {dbg!(MAX_INPUT_SIZE);}
|
||||||
|
|
||||||
|
if let Ok(seed) = env::var("SEED_RANDOM") {
|
||||||
|
unsafe {RNG_SEED = str::parse::<u64>(&seed).expect("SEED_RANDOM must be an integer.");}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut run_client = |state: Option<_>, mut mgr, _core_id| {
|
||||||
|
// Initialize QEMU
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
|
let emu = Emulator::new(&args, &env);
|
||||||
|
|
||||||
|
if let Some(main_addr) = main_addr {
|
||||||
|
unsafe {
|
||||||
|
libafl_qemu_set_native_breakpoint(main_addr);
|
||||||
|
emu.run();
|
||||||
|
libafl_qemu_remove_native_breakpoint(main_addr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
unsafe {
|
||||||
|
#[cfg(feature = "fuzz_int")]
|
||||||
|
{
|
||||||
|
let mut start_tick : u32 = 0;
|
||||||
|
for i in 0..DO_NUM_INTERRUPT {
|
||||||
|
let mut t : [u8; 4] = [0,0,0,0];
|
||||||
|
if len > (i+1)*4 {
|
||||||
|
for j in 0 as usize..4 as usize {
|
||||||
|
t[j]=buf[i*4+j];
|
||||||
|
}
|
||||||
|
if i == 0 || true {
|
||||||
|
unsafe {start_tick = u32::from_le_bytes(t) % LIMIT;}
|
||||||
|
} else {
|
||||||
|
start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t)));
|
||||||
|
}
|
||||||
|
libafl_interrupt_offsets[i] = start_tick;
|
||||||
|
libafl_num_interrupts = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if buf.len() > libafl_num_interrupts*4 {
|
||||||
|
buf = &buf[libafl_num_interrupts*4..];
|
||||||
|
len = buf.len();
|
||||||
|
}
|
||||||
|
// println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec());
|
||||||
|
}
|
||||||
|
if len > MAX_INPUT_SIZE {
|
||||||
|
buf = &buf[0..MAX_INPUT_SIZE];
|
||||||
|
len = MAX_INPUT_SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
emu.write_phys_mem(input_addr, buf);
|
||||||
|
if let Some(s) = test_length_ptr {
|
||||||
|
emu.write_phys_mem(s as u64, &len.to_le_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
emu.run();
|
||||||
|
|
||||||
|
// If the execution stops at any point other then the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash
|
||||||
|
let mut pcs = (0..emu.num_cpus())
|
||||||
|
.map(|i| emu.cpu_from_index(i))
|
||||||
|
.map(|cpu| -> Result<u32, String> { cpu.read_reg(Regs::Pc) });
|
||||||
|
match pcs
|
||||||
|
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
|
||||||
|
{
|
||||||
|
Some(_) => ExitKind::Ok,
|
||||||
|
None => ExitKind::Crash,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 = VariableMapObserver::new("edges", edges, edges_counter);
|
||||||
|
#[cfg(feature = "observer_hitcounts")]
|
||||||
|
let edges_observer = HitcountsMapObserver::new(edges_observer);
|
||||||
|
|
||||||
|
// Create an observation channel to keep track of the execution time
|
||||||
|
let clock_time_observer = QemuClockObserver::new("clocktime");
|
||||||
|
|
||||||
|
let systemstate_observer = QemuSystemStateObserver::new();
|
||||||
|
|
||||||
|
// Feedback to rate the interestingness of an input
|
||||||
|
// This one is composed by two Feedbacks in OR
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
// Time feedback, this one does not need a feedback state
|
||||||
|
ClockTimeFeedback::new_with_observer(&clock_time_observer)
|
||||||
|
);
|
||||||
|
#[cfg(feature = "feed_genetic")]
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
feedback,
|
||||||
|
AlwaysTrueFeedback::new()
|
||||||
|
);
|
||||||
|
#[cfg(feature = "feed_afl")]
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
feedback,
|
||||||
|
// New maximization map feedback linked to the edges observer and the feedback state
|
||||||
|
MaxMapFeedback::new_tracking(&edges_observer, true, true)
|
||||||
|
);
|
||||||
|
#[cfg(feature = "feed_longest")]
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
// afl feedback needs to be activated first for MapIndexesMetadata
|
||||||
|
feedback,
|
||||||
|
// Feedback to reward any input which increses the execution time
|
||||||
|
ExecTimeIncFeedback::new()
|
||||||
|
);
|
||||||
|
#[cfg(all(feature = "systemstate",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))]
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
feedback,
|
||||||
|
DumpSystraceFeedback::with_dump(env::var("TRACE_DUMP").ok().map(PathBuf::from))
|
||||||
|
);
|
||||||
|
#[cfg(feature = "feed_systemtrace")]
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
feedback,
|
||||||
|
// AlwaysTrueFeedback::new(),
|
||||||
|
NovelSystemStateFeedback::default()
|
||||||
|
);
|
||||||
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
|
let mut feedback = feedback_or!(
|
||||||
|
feedback,
|
||||||
|
SysMapFeedback::default()
|
||||||
|
);
|
||||||
|
|
||||||
|
// A feedback to choose if an input is a solution or not
|
||||||
|
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
|
|
||||||
|
// If not restarting, create a State from scratch
|
||||||
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
StdState::new(
|
||||||
|
// RNG
|
||||||
|
unsafe {StdRand::with_seed(RNG_SEED) },
|
||||||
|
// 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
|
||||||
|
OnDiskCorpus::new(objective_dir.clone()).unwrap(),
|
||||||
|
// States of the feedbacks.
|
||||||
|
// The feedbacks can report the data that should persist in the State.
|
||||||
|
&mut feedback,
|
||||||
|
// Same for objective feedbacks
|
||||||
|
&mut objective,
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
|
#[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace", feature = "feed_genetic")))]
|
||||||
|
let scheduler = QueueScheduler::new();
|
||||||
|
#[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))]
|
||||||
|
let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new());
|
||||||
|
#[cfg(feature = "feed_systemtrace")]
|
||||||
|
let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()));
|
||||||
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
|
let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new());
|
||||||
|
#[cfg(feature = "feed_genetic")]
|
||||||
|
let scheduler = GenerationScheduler::new();
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
#[cfg(not(feature = "systemstate"))]
|
||||||
|
let qhelpers = tuple_list!(
|
||||||
|
QemuEdgeCoverageHelper::default(),
|
||||||
|
QemuStateRestoreHelper::new()
|
||||||
|
);
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let qhelpers = tuple_list!(
|
||||||
|
QemuEdgeCoverageHelper::default(),
|
||||||
|
QemuStateRestoreHelper::new(),
|
||||||
|
QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range.clone())
|
||||||
|
);
|
||||||
|
let mut hooks = QemuHooks::new(&emu,qhelpers);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "systemstate"))]
|
||||||
|
let observer_list = tuple_list!(edges_observer, clock_time_observer);
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let observer_list = tuple_list!(edges_observer, clock_time_observer, systemstate_observer);
|
||||||
|
|
||||||
|
// Create a QEMU in-process executor
|
||||||
|
let executor = QemuExecutor::new(
|
||||||
|
&mut hooks,
|
||||||
|
&mut harness,
|
||||||
|
observer_list,
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
)
|
||||||
|
.expect("Failed to create QemuExecutor");
|
||||||
|
|
||||||
|
// Wrap the executor to keep track of the timeout
|
||||||
|
let mut executor = TimeoutExecutor::new(executor, timeout);
|
||||||
|
|
||||||
|
let mutations = havoc_mutations();
|
||||||
|
// Setup an havoc mutator with a mutational stage
|
||||||
|
let mutator = StdScheduledMutator::new(mutations);
|
||||||
|
// #[cfg(not(all(feature = "feed_systemtrace", feature = "fuzz_int")))]
|
||||||
|
// let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
// #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))]
|
||||||
|
#[cfg(feature = "fuzz_int")]
|
||||||
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator),MyStateStage::new());
|
||||||
|
#[cfg(not(feature = "fuzz_int"))]
|
||||||
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
|
||||||
|
if env::var("DO_SHOWMAP").is_ok() {
|
||||||
|
let s = &env::var("DO_SHOWMAP").unwrap();
|
||||||
|
let show_input = if s=="-" {
|
||||||
|
let mut buf = Vec::<u8>::new();
|
||||||
|
std::io::stdin().read_to_end(&mut buf).expect("Could not read Stdin");
|
||||||
|
buf
|
||||||
|
} else if s=="$" {
|
||||||
|
env::var("SHOWMAP_TEXTINPUT").expect("SHOWMAP_TEXTINPUT not set").as_bytes().to_owned()
|
||||||
|
} else {
|
||||||
|
fs::read(s).expect("Input file for DO_SHOWMAP can not be read")
|
||||||
|
};
|
||||||
|
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input))
|
||||||
|
.unwrap();
|
||||||
|
if let Ok(td) = env::var("TIME_DUMP") {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(td).expect("Could not open timedump");
|
||||||
|
if let Some(ichist) = state.metadata_mut().get_mut::<IcHist>() {
|
||||||
|
for i in ichist.0.drain(..) {
|
||||||
|
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Ok(_) = env::var("SEED_RANDOM") {
|
||||||
|
unsafe {
|
||||||
|
let mut rng = StdRng::seed_from_u64(RNG_SEED);
|
||||||
|
for i in 0..100 {
|
||||||
|
let inp = BytesInput::new(vec![rng.gen::<u8>(); MAX_INPUT_SIZE]);
|
||||||
|
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if let Ok(sf) = env::var("SEED_DIR") {
|
||||||
|
state
|
||||||
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &[PathBuf::from(&sf)])
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
println!("Failed to load initial corpus at {:?}", &corpus_dirs);
|
||||||
|
process::exit(0);
|
||||||
|
});
|
||||||
|
println!("We imported {} inputs from seedfile.", state.corpus().count());
|
||||||
|
} else if state.corpus().count() < 1 {
|
||||||
|
state
|
||||||
|
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
|
||||||
|
.unwrap_or_else(|_| {
|
||||||
|
println!("Failed to load initial corpus at {:?}", &corpus_dirs);
|
||||||
|
process::exit(0);
|
||||||
|
});
|
||||||
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
|
}
|
||||||
|
|
||||||
|
match env::var("FUZZ_ITERS") {
|
||||||
|
Err(_) => {
|
||||||
|
fuzzer
|
||||||
|
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||||
|
.unwrap();
|
||||||
|
},
|
||||||
|
Ok(t) => {
|
||||||
|
println!("Iterations {}",t);
|
||||||
|
let num = str::parse::<u64>(&t).expect("FUZZ_ITERS was not a number");
|
||||||
|
if let Ok(s) = env::var("FUZZ_RANDOM") { unsafe {
|
||||||
|
if s.contains("watersv2_int") {
|
||||||
|
println!("V2");
|
||||||
|
LIMIT=7000000;
|
||||||
|
} else {
|
||||||
|
println!("V1");
|
||||||
|
LIMIT=5000000;
|
||||||
|
}
|
||||||
|
println!("Random Fuzzing, ignore corpus");
|
||||||
|
// let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE);
|
||||||
|
let target_duration = Duration::from_secs(num);
|
||||||
|
let start_time = std::time::Instant::now();
|
||||||
|
let mut rng = StdRng::seed_from_u64(RNG_SEED);
|
||||||
|
while start_time.elapsed() < target_duration {
|
||||||
|
// let inp = generator.generate(&mut state).unwrap();
|
||||||
|
// libafl's generator is too slow
|
||||||
|
let inp = BytesInput::new(vec![rng.gen::<u8>(); MAX_INPUT_SIZE]);
|
||||||
|
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap();
|
||||||
|
}
|
||||||
|
}} else {
|
||||||
|
// fuzzer
|
||||||
|
// .fuzz_loop_for_duration(&mut stages, &mut executor, &mut state, &mut mgr, Duration::from_secs(num))
|
||||||
|
// .unwrap();
|
||||||
|
fuzzer
|
||||||
|
.fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime.checked_add(Duration::from_secs(num)).unwrap())
|
||||||
|
.unwrap();
|
||||||
|
#[cfg(feature = "run_until_saturation")]
|
||||||
|
{
|
||||||
|
{
|
||||||
|
let mut dumper = |marker : String| {
|
||||||
|
if let Ok(td) = env::var("TIME_DUMP") {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(td).expect("Could not open timedump");
|
||||||
|
if let Some(ichist) = state.metadata_mut().get_mut::<IcHist>() {
|
||||||
|
for i in ichist.0.drain(..) {
|
||||||
|
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(td) = env::var("CASE_DUMP") {
|
||||||
|
println!("Dumping worst case to {:?}", td);
|
||||||
|
let corpus = state.corpus();
|
||||||
|
let mut worst = Duration::new(0,0);
|
||||||
|
let mut worst_input = None;
|
||||||
|
for i in 0..corpus.count() {
|
||||||
|
let tc = corpus.get(i).expect("Could not get element from corpus").borrow();
|
||||||
|
if worst < tc.exec_time().expect("Testcase missing duration") {
|
||||||
|
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
|
||||||
|
worst = tc.exec_time().expect("Testcase missing duration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match worst_input {
|
||||||
|
Some(wi) => {
|
||||||
|
// let cd = format!("{}.case",&td);
|
||||||
|
let mut cd = td.clone();
|
||||||
|
cd.push_str(&marker);
|
||||||
|
fs::write(&cd,wi).expect("Failed to write worst corpus element");
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
gd.push_str(&format!(".graph{}", marker));
|
||||||
|
if let Some(md) = state.named_metadata_mut().get_mut::<SysGraphFeedbackState>("SysMap") {
|
||||||
|
fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
|
||||||
|
let mut uniq: Vec<usize> = md.map.values().map(|x| x.clone()).collect();
|
||||||
|
uniq.sort();
|
||||||
|
uniq.dedup();
|
||||||
|
gd.push_str(&format!(".{}.toprated{}", uniq.len(), marker));
|
||||||
|
fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
dumper(format!(".iter_{}",t));
|
||||||
|
}
|
||||||
|
println!("Start running until saturation");
|
||||||
|
let mut last = state.metadata().get::<IcHist>().unwrap().1;
|
||||||
|
while SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis() < last.1 + Duration::from_secs(10800).as_millis() {
|
||||||
|
starttime=starttime.checked_add(Duration::from_secs(30)).unwrap();
|
||||||
|
fuzzer
|
||||||
|
.fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime)
|
||||||
|
.unwrap();
|
||||||
|
let after = state.metadata().get::<IcHist>().unwrap().1;
|
||||||
|
if after.0 > last.0 {
|
||||||
|
last=after;
|
||||||
|
}
|
||||||
|
if let Ok(td) = env::var("CASE_DUMP") {
|
||||||
|
println!("Dumping worst case to {:?}", td);
|
||||||
|
let corpus = state.corpus();
|
||||||
|
let mut worst = Duration::new(0,0);
|
||||||
|
let mut worst_input = None;
|
||||||
|
for i in 0..corpus.count() {
|
||||||
|
let tc = corpus.get(i).expect("Could not get element from corpus").borrow();
|
||||||
|
if worst < tc.exec_time().expect("Testcase missing duration") {
|
||||||
|
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
|
||||||
|
worst = tc.exec_time().expect("Testcase missing duration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match worst_input {
|
||||||
|
Some(wi) => {
|
||||||
|
// let cd = format!("{}.case",&td);
|
||||||
|
let cd = td.clone();
|
||||||
|
fs::write(&cd,wi).expect("Failed to write worst corpus element");
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
gd.push_str(".graph" );
|
||||||
|
if let Some(md) = state.named_metadata_mut().get_mut::<SysGraphFeedbackState>("SysMap") {
|
||||||
|
fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
|
||||||
|
let mut uniq: Vec<usize> = md.map.values().map(|x| x.clone()).collect();
|
||||||
|
uniq.sort();
|
||||||
|
uniq.dedup();
|
||||||
|
gd.push_str(&format!(".{}.toprated", uniq.len()));
|
||||||
|
fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(td) = env::var("TIME_DUMP") {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(td).expect("Could not open timedump");
|
||||||
|
if let Some(ichist) = state.metadata_mut().get_mut::<IcHist>() {
|
||||||
|
for i in ichist.0.drain(..) {
|
||||||
|
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(td) = env::var("CASE_DUMP") {
|
||||||
|
println!("Dumping worst case to {:?}", td);
|
||||||
|
let corpus = state.corpus();
|
||||||
|
let mut worst = Duration::new(0,0);
|
||||||
|
let mut worst_input = None;
|
||||||
|
for i in 0..corpus.count() {
|
||||||
|
let tc = corpus.get(i).expect("Could not get element from corpus").borrow();
|
||||||
|
if worst < tc.exec_time().expect("Testcase missing duration") {
|
||||||
|
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
|
||||||
|
worst = tc.exec_time().expect("Testcase missing duration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match worst_input {
|
||||||
|
Some(wi) => {
|
||||||
|
// let cd = format!("{}.case",&td);
|
||||||
|
let cd = td.clone();
|
||||||
|
fs::write(&cd,wi).expect("Failed to write worst corpus element");
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
gd.push_str(".graph");
|
||||||
|
if let Some(md) = state.named_metadata_mut().get_mut::<SysGraphFeedbackState>("SysMap") {
|
||||||
|
fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
|
||||||
|
let mut uniq: Vec<usize> = md.map.values().map(|x| x.clone()).collect();
|
||||||
|
uniq.sort();
|
||||||
|
uniq.dedup();
|
||||||
|
gd.push_str(&format!(".{}.toprated", uniq.len()));
|
||||||
|
fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "singlecore"))]
|
||||||
|
return Ok(());
|
||||||
|
};
|
||||||
|
|
||||||
|
// Special case where no fuzzing happens, but standard input is dumped
|
||||||
|
if let Ok(input_dump) = env::var("DUMP_SEED") {
|
||||||
|
// Initialize QEMU
|
||||||
|
let args: Vec<String> = env::args().collect();
|
||||||
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
|
let emu = Emulator::new(&args, &env);
|
||||||
|
|
||||||
|
if let Some(main_addr) = main_addr {
|
||||||
|
unsafe { libafl_qemu_set_native_breakpoint(main_addr); }// BREAKPOINT
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
emu.run();
|
||||||
|
|
||||||
|
let mut buf = [0u8].repeat(MAX_INPUT_SIZE);
|
||||||
|
emu.read_phys_mem(input_addr, buf.as_mut_slice());
|
||||||
|
|
||||||
|
let dir = env::var("SEED_DIR").map_or("./corpus".to_string(), |x| x);
|
||||||
|
let filename = if input_dump == "" {"input"} else {&input_dump};
|
||||||
|
println!("Dumping input to: {}/{}",&dir,filename);
|
||||||
|
fs::write(format!("{}/{}",&dir,filename), buf).expect("could not write input dump");
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "singlecore")]
|
||||||
|
{
|
||||||
|
let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||||
|
#[cfg(not(feature = "restarting"))]
|
||||||
|
{
|
||||||
|
let mgr = SimpleEventManager::new(monitor);
|
||||||
|
run_client(None, mgr, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "restarting")]
|
||||||
|
{
|
||||||
|
let mut shmem_provider = StdShMemProvider::new().unwrap();
|
||||||
|
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
|
||||||
|
{
|
||||||
|
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
||||||
|
Ok(res) => res,
|
||||||
|
Err(err) => match err {
|
||||||
|
Error::ShuttingDown => {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
panic!("Failed to setup the restarter: {}", err);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
run_client(state, mgr, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// else -> multicore
|
||||||
|
#[cfg(not(feature = "singlecore"))]
|
||||||
|
{
|
||||||
|
// The shared memory allocator
|
||||||
|
let shmem_provider = StdShMemProvider::new().expect("Failed to init shared memory");
|
||||||
|
|
||||||
|
// The stats reporter for the broker
|
||||||
|
let monitor = MultiMonitor::new(|s| println!("{}", s));
|
||||||
|
|
||||||
|
// Build and run a Launcher
|
||||||
|
match Launcher::builder()
|
||||||
|
.shmem_provider(shmem_provider)
|
||||||
|
.broker_port(broker_port)
|
||||||
|
.configuration(EventConfig::from_build_id())
|
||||||
|
.monitor(monitor)
|
||||||
|
.run_client(&mut run_client)
|
||||||
|
.cores(&cores)
|
||||||
|
// .stdout_file(Some("/dev/null"))
|
||||||
|
.build()
|
||||||
|
.launch()
|
||||||
|
{
|
||||||
|
Ok(()) => (),
|
||||||
|
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
||||||
|
Err(err) => panic!("Failed to run launcher: {:?}", err),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
13
fuzzers/FRET/src/lib.rs
Normal file
13
fuzzers/FRET/src/lib.rs
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
#![feature(is_sorted)]
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod fuzzer;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod clock;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod qemustate;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub mod systemstate;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod mutational;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod worst;
|
24
fuzzers/FRET/src/main.rs
Normal file
24
fuzzers/FRET/src/main.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#![feature(is_sorted)]
|
||||||
|
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod fuzzer;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod clock;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod qemustate;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod systemstate;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod worst;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod mutational;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
pub fn main() {
|
||||||
|
fuzzer::fuzz();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
pub fn main() {
|
||||||
|
panic!("qemu-user and libafl_qemu is only supported on linux!");
|
||||||
|
}
|
240
fuzzers/FRET/src/mutational.rs
Normal file
240
fuzzers/FRET/src/mutational.rs
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
//| The [`MutationalStage`] is the default stage used during fuzzing.
|
||||||
|
//! For the current input, it will perform a range of random mutations, and then run them in the executor.
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use std::cmp::{max, min};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
bolts::rands::Rand,
|
||||||
|
corpus::{Corpus, self},
|
||||||
|
fuzzer::Evaluator,
|
||||||
|
mark_feature_time,
|
||||||
|
stages::{Stage},
|
||||||
|
start_timer,
|
||||||
|
state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata},
|
||||||
|
Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult, Mutator},
|
||||||
|
};
|
||||||
|
use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist};
|
||||||
|
|
||||||
|
pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4);
|
||||||
|
|
||||||
|
//======================= Custom mutator
|
||||||
|
|
||||||
|
/// The default mutational stage
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct MyStateStage<E, EM, Z> {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
phantom: PhantomData<(E, EM, Z)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, Z> MyStateStage<E, EM, Z>
|
||||||
|
where
|
||||||
|
E: UsesState<State = Z::State>,
|
||||||
|
EM: UsesState<State = Z::State>,
|
||||||
|
Z: Evaluator<E, EM>,
|
||||||
|
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { phantom: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, Z> Stage<E, EM, Z> for MyStateStage<E, EM, Z>
|
||||||
|
where
|
||||||
|
E: UsesState<State = Z::State>,
|
||||||
|
EM: UsesState<State = Z::State>,
|
||||||
|
Z: Evaluator<E, EM>,
|
||||||
|
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
|
||||||
|
<Z::State as UsesInput>::Input: HasBytesVec
|
||||||
|
{
|
||||||
|
fn perform(
|
||||||
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
|
executor: &mut E,
|
||||||
|
state: &mut Self::State,
|
||||||
|
manager: &mut EM,
|
||||||
|
corpus_idx: usize,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut _input = state
|
||||||
|
.corpus()
|
||||||
|
.get(corpus_idx)?
|
||||||
|
.borrow_mut().clone();
|
||||||
|
let mut newinput = _input.input_mut().as_mut().unwrap().clone();
|
||||||
|
// let mut tmpinput = _input.input_mut().as_mut().unwrap().clone();
|
||||||
|
let mut do_rerun = false;
|
||||||
|
{
|
||||||
|
// need our own random generator, because borrowing rules
|
||||||
|
let mut myrand = StdRand::new();
|
||||||
|
let mut target_bytes : Vec<u8> = vec![];
|
||||||
|
{
|
||||||
|
let input = _input.input_mut().as_ref().unwrap();
|
||||||
|
let tmp = &mut state.rand_mut();
|
||||||
|
myrand.set_seed(tmp.next());
|
||||||
|
target_bytes = input.bytes().to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
// produce a slice of absolute interrupt times
|
||||||
|
let mut interrupt_offsets : [u32; 32] = [0u32; 32];
|
||||||
|
let mut num_interrupts : usize = 0;
|
||||||
|
{
|
||||||
|
let mut start_tick : u32 = 0;
|
||||||
|
for i in 0..DO_NUM_INTERRUPT {
|
||||||
|
let mut t : [u8; 4] = [0,0,0,0];
|
||||||
|
if target_bytes.len() > (i+1)*4 {
|
||||||
|
for j in 0 as usize..4 as usize {
|
||||||
|
t[j]=target_bytes[i*4+j];
|
||||||
|
}
|
||||||
|
if i == 0 || true {
|
||||||
|
start_tick = u32::from_le_bytes(t);
|
||||||
|
} else {
|
||||||
|
start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t)));
|
||||||
|
}
|
||||||
|
interrupt_offsets[i] = start_tick;
|
||||||
|
num_interrupts = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interrupt_offsets.sort();
|
||||||
|
|
||||||
|
// println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec());
|
||||||
|
// let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT);
|
||||||
|
let mut suffix = target_bytes.split_off(4 * num_interrupts);
|
||||||
|
let mut prefix : Vec<[u8; 4]> = vec![];
|
||||||
|
// let mut suffix : Vec<u8> = vec![];
|
||||||
|
#[cfg(feature = "feed_systemtrace")]
|
||||||
|
{
|
||||||
|
let tmp = _input.metadata().get::<FreeRTOSSystemStateMetadata>();
|
||||||
|
if tmp.is_some() {
|
||||||
|
let trace = tmp.expect("FreeRTOSSystemStateMetadata not found");
|
||||||
|
|
||||||
|
// calculate hits and identify snippets
|
||||||
|
let mut last_m = false;
|
||||||
|
let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler
|
||||||
|
for i in 0..trace.inner.len() {
|
||||||
|
let curr = &trace.inner[i];
|
||||||
|
let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64)));
|
||||||
|
if m {
|
||||||
|
marks.push((curr, i, 1));
|
||||||
|
// println!("1: {}",curr.current_task.task_name);
|
||||||
|
} else if last_m {
|
||||||
|
marks.push((curr, i, 2));
|
||||||
|
// println!("2: {}",curr.current_task.task_name);
|
||||||
|
} else {
|
||||||
|
marks.push((curr, i, 0));
|
||||||
|
}
|
||||||
|
last_m = m;
|
||||||
|
}
|
||||||
|
for i in 0..num_interrupts {
|
||||||
|
// bounds based on minimum inter-arrival time
|
||||||
|
let mut lb = 0;
|
||||||
|
let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32");
|
||||||
|
if i > 0 {
|
||||||
|
lb = u32::saturating_add(interrupt_offsets[i-1],MINIMUM_INTER_ARRIVAL_TIME);
|
||||||
|
}
|
||||||
|
if i < num_interrupts-1 {
|
||||||
|
ub = u32::saturating_sub(interrupt_offsets[i+1],MINIMUM_INTER_ARRIVAL_TIME);
|
||||||
|
}
|
||||||
|
// get old hit and handler
|
||||||
|
let old_hit = marks.iter().filter(
|
||||||
|
|x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick
|
||||||
|
).next();
|
||||||
|
let old_handler = match old_hit {
|
||||||
|
Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 {
|
||||||
|
Some(marks[s.1+1])
|
||||||
|
} else {None},
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
// find reachable alternatives
|
||||||
|
let alternatives : Vec<_> = marks.iter().filter(|x|
|
||||||
|
x.2 != 2 &&
|
||||||
|
(
|
||||||
|
x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick
|
||||||
|
|| x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick )
|
||||||
|
).collect();
|
||||||
|
// in cases there are no alternatives
|
||||||
|
if alternatives.len() == 0 {
|
||||||
|
if old_hit.is_none() {
|
||||||
|
// choose something random
|
||||||
|
let untouched : Vec<_> = marks.iter().filter(
|
||||||
|
|x| x.2 == 0
|
||||||
|
).collect();
|
||||||
|
if untouched.len() > 0 {
|
||||||
|
let tmp = interrupt_offsets[i];
|
||||||
|
let choice = myrand.choose(untouched);
|
||||||
|
interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick)
|
||||||
|
.try_into().expect("tick > u32");
|
||||||
|
do_rerun = true;
|
||||||
|
}
|
||||||
|
// println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// do nothing
|
||||||
|
// println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let replacement = myrand.choose(alternatives);
|
||||||
|
if (old_hit.map_or(false, |x| x == replacement)) {
|
||||||
|
// use the old value
|
||||||
|
// println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) {
|
||||||
|
// move futher back, respect old_handler
|
||||||
|
old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick)
|
||||||
|
} else { 0 };
|
||||||
|
let tmp = interrupt_offsets[i];
|
||||||
|
interrupt_offsets[i] = (myrand.between(replacement.0.start_tick,
|
||||||
|
replacement.0.end_tick) + extra).try_into().expect("ticks > u32");
|
||||||
|
// println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]);
|
||||||
|
do_rerun = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut numbers : Vec<u32> = interrupt_offsets[0..num_interrupts].to_vec();
|
||||||
|
numbers.sort();
|
||||||
|
// println!("Mutator: {:?}", numbers);
|
||||||
|
let mut start : u32 = 0;
|
||||||
|
// for i in 0..numbers.len() {
|
||||||
|
// let tmp = numbers[i];
|
||||||
|
// numbers[i] = numbers[i]-start;
|
||||||
|
// start = tmp;
|
||||||
|
// }
|
||||||
|
for i in 0..numbers.len() {
|
||||||
|
prefix.push(u32::to_le_bytes(numbers[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "feed_systemtrace"))]
|
||||||
|
{
|
||||||
|
let metadata = state.metadata();
|
||||||
|
let hist = metadata.get::<IcHist>().unwrap();
|
||||||
|
let maxtick : u64 = hist.1.0;
|
||||||
|
// let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap();
|
||||||
|
let mut numbers : Vec<u32> = vec![];
|
||||||
|
for i in 0..num_interrupts {
|
||||||
|
prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut n : Vec<u8> = vec![];
|
||||||
|
n = [prefix.concat(), suffix].concat();
|
||||||
|
newinput.bytes_mut().clear();
|
||||||
|
newinput.bytes_mut().append(&mut n);
|
||||||
|
}
|
||||||
|
// InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?;
|
||||||
|
if do_rerun {
|
||||||
|
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, newinput)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, Z> UsesState for MyStateStage<E, EM, Z>
|
||||||
|
where
|
||||||
|
E: UsesState<State = Z::State>,
|
||||||
|
EM: UsesState<State = Z::State>,
|
||||||
|
Z: Evaluator<E, EM>,
|
||||||
|
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
|
||||||
|
{
|
||||||
|
type State = Z::State;
|
||||||
|
}
|
96
fuzzers/FRET/src/qemustate.rs
Normal file
96
fuzzers/FRET/src/qemustate.rs
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
use libafl::prelude::UsesInput;
|
||||||
|
use libafl_qemu::CPUArchState;
|
||||||
|
use libafl_qemu::Emulator;
|
||||||
|
use libafl_qemu::FastSnapshot;
|
||||||
|
use libafl_qemu::QemuExecutor;
|
||||||
|
use libafl_qemu::QemuHelper;
|
||||||
|
use libafl_qemu::QemuHelperTuple;
|
||||||
|
use libafl::{executors::ExitKind, inputs::Input, observers::ObserversTuple, state::HasMetadata};
|
||||||
|
use libafl_qemu::QemuHooks;
|
||||||
|
|
||||||
|
use libafl_qemu::{
|
||||||
|
emu,
|
||||||
|
};
|
||||||
|
// TODO be thread-safe maybe with https://amanieu.github.io/thread_local-rs/thread_local/index.html
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct QemuStateRestoreHelper {
|
||||||
|
has_snapshot: bool,
|
||||||
|
use_snapshot: bool,
|
||||||
|
saved_cpu_states: Vec<CPUArchState>,
|
||||||
|
fastsnap: Option<FastSnapshot>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuStateRestoreHelper {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
has_snapshot: false,
|
||||||
|
use_snapshot: true,
|
||||||
|
saved_cpu_states: vec![],
|
||||||
|
fastsnap: None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for QemuStateRestoreHelper {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> QemuHelper<S> for QemuStateRestoreHelper
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
const HOOKS_DO_SIDE_EFFECTS: bool = true;
|
||||||
|
|
||||||
|
fn init_hooks<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
|
||||||
|
where
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first_exec<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
|
||||||
|
where
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input) {
|
||||||
|
// unsafe { println!("snapshot post {}",emu::icount_get_raw()) };
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_exec(&mut self, emulator: &Emulator, _input: &S::Input) {
|
||||||
|
// only restore in pre-exec, to preserve the post-execution state for inspection
|
||||||
|
#[cfg(feature = "snapshot_restore")]
|
||||||
|
{
|
||||||
|
#[cfg(feature = "snapshot_fast")]
|
||||||
|
match self.fastsnap {
|
||||||
|
Some(s) => emulator.restore_fast_snapshot(s),
|
||||||
|
None => {self.fastsnap = Some(emulator.create_fast_snapshot(true));},
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "snapshot_fast"))]
|
||||||
|
if !self.has_snapshot {
|
||||||
|
emulator.save_snapshot("Start", true);
|
||||||
|
self.has_snapshot = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
emulator.load_snapshot("Start", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "snapshot_restore"))]
|
||||||
|
if !self.has_snapshot {
|
||||||
|
self.saved_cpu_states = (0..emulator.num_cpus())
|
||||||
|
.map(|i| emulator.cpu_from_index(i).save_state())
|
||||||
|
.collect();
|
||||||
|
self.has_snapshot = true;
|
||||||
|
} else {
|
||||||
|
for (i, s) in self.saved_cpu_states.iter().enumerate() {
|
||||||
|
emulator.cpu_from_index(i).restore_state(s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// unsafe { println!("snapshot pre {}",emu::icount_get_raw()) };
|
||||||
|
}
|
||||||
|
}
|
299
fuzzers/FRET/src/systemstate/feedbacks.rs
Normal file
299
fuzzers/FRET/src/systemstate/feedbacks.rs
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
use libafl::SerdeAny;
|
||||||
|
use libafl::bolts::ownedref::OwnedSlice;
|
||||||
|
use libafl::inputs::BytesInput;
|
||||||
|
use libafl::prelude::UsesInput;
|
||||||
|
use libafl::state::HasNamedMetadata;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use crate::clock::QemuClockObserver;
|
||||||
|
use libafl::corpus::Testcase;
|
||||||
|
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::QemuSystemStateObserver;
|
||||||
|
use petgraph::prelude::DiGraph;
|
||||||
|
use petgraph::graph::NodeIndex;
|
||||||
|
use petgraph::Direction;
|
||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
//============================= Feedback
|
||||||
|
|
||||||
|
/// Shared Metadata for a systemstateFeedback
|
||||||
|
#[derive(Debug, Serialize, Deserialize, SerdeAny, Clone, Default)]
|
||||||
|
pub struct SystemStateFeedbackState
|
||||||
|
{
|
||||||
|
known_traces: HashMap<u64,(u64,u64,usize)>, // encounters,ticks,length
|
||||||
|
longest: Vec<RefinedFreeRTOSSystemState>,
|
||||||
|
}
|
||||||
|
impl Named for SystemStateFeedbackState
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"systemstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// impl FeedbackState for systemstateFeedbackState
|
||||||
|
// {
|
||||||
|
// fn reset(&mut self) -> Result<(), Error> {
|
||||||
|
// self.longest.clear();
|
||||||
|
// self.known_traces.clear();
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
/// A Feedback reporting novel System-State Transitions. Depends on [`QemuSystemStateObserver`]
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||||
|
pub struct NovelSystemStateFeedback
|
||||||
|
{
|
||||||
|
last_trace: Option<Vec<RefinedFreeRTOSSystemState>>,
|
||||||
|
// known_traces: HashMap<u64,(u64,usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for NovelSystemStateFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor + HasNamedMetadata,
|
||||||
|
{
|
||||||
|
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::<QemuSystemStateObserver>("systemstate")
|
||||||
|
.expect("QemuSystemStateObserver not found");
|
||||||
|
let clock_observer = observers.match_name::<QemuClockObserver>("clocktime") //TODO not fixed
|
||||||
|
.expect("QemuClockObserver not found");
|
||||||
|
let feedbackstate = match state
|
||||||
|
.named_metadata_mut()
|
||||||
|
.get_mut::<SystemStateFeedbackState>("systemstate") {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
let n=SystemStateFeedbackState::default();
|
||||||
|
state.named_metadata_mut().insert(n, "systemstate");
|
||||||
|
state.named_metadata_mut().get_mut::<SystemStateFeedbackState>("systemstate").unwrap()
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// let feedbackstate = state
|
||||||
|
// .feedback_states_mut()
|
||||||
|
// .match_name_mut::<systemstateFeedbackState>("systemstate")
|
||||||
|
// .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 NovelSystemStateFeedback
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"systemstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//=============================
|
||||||
|
|
||||||
|
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 [`QemuSystemStateObserver`]
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Default)]
|
||||||
|
pub struct HitSystemStateFeedback
|
||||||
|
{
|
||||||
|
target: Option<Vec<String>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for HitSystemStateFeedback
|
||||||
|
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::<QemuSystemStateObserver>("systemstate")
|
||||||
|
.expect("QemuSystemStateObserver not found");
|
||||||
|
// Do Stuff
|
||||||
|
match &self.target {
|
||||||
|
Some(s) => {
|
||||||
|
// #[cfg(debug_assertions)] eprintln!("Hit systemstate Feedback trigger");
|
||||||
|
Ok(match_traces_name(s, &observer.last_run))
|
||||||
|
},
|
||||||
|
None => Ok(false),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for HitSystemStateFeedback
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"hit_systemstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HitSystemStateFeedback {
|
||||||
|
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 [`QemuSystemStateObserver`]
|
||||||
|
#[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::<QemuSystemStateObserver>("systemstate")
|
||||||
|
.expect("QemuSystemStateObserver not found");
|
||||||
|
let names : Vec<String> = observer.last_run.iter().map(|x| x.current_task.task_name.clone()).collect();
|
||||||
|
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!("{:?}\n{:?}",observer.last_run,names);}
|
||||||
|
};
|
||||||
|
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 {
|
||||||
|
"Dumpsystemstate"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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/FRET/src/systemstate/freertos.rs
Normal file
122
fuzzers/FRET/src/systemstate/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::systemstate::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);
|
604
fuzzers/FRET/src/systemstate/graph.rs
Normal file
604
fuzzers/FRET/src/systemstate/graph.rs
Normal file
@ -0,0 +1,604 @@
|
|||||||
|
|
||||||
|
use libafl::SerdeAny;
|
||||||
|
/// 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 libafl::prelude::HasTargetBytes;
|
||||||
|
use libafl::prelude::UsesInput;
|
||||||
|
use libafl::state::HasNamedMetadata;
|
||||||
|
use libafl::state::UsesState;
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use libafl::state::HasCorpus;
|
||||||
|
use libafl::state::HasSolutions;
|
||||||
|
use libafl::state::HasRand;
|
||||||
|
use crate::worst::MaxExecsLenFavFactor;
|
||||||
|
use libafl::schedulers::MinimizerScheduler;
|
||||||
|
use libafl::bolts::HasRefCnt;
|
||||||
|
use libafl::bolts::AsSlice;
|
||||||
|
use libafl::bolts::ownedref::OwnedSlice;
|
||||||
|
use libafl::inputs::BytesInput;
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use crate::clock::QemuClockObserver;
|
||||||
|
use libafl::corpus::Testcase;
|
||||||
|
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::QemuSystemStateObserver;
|
||||||
|
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 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Entry = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
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> =
|
||||||
|
MinimizerScheduler<CS, MaxExecsLenFavFactor<<CS as UsesState>::State>,SysGraphMetadata>;
|
||||||
|
|
||||||
|
//============================= Graph Feedback
|
||||||
|
|
||||||
|
/// Improved System State Graph
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug, Default, SerdeAny)]
|
||||||
|
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 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 [`QemuSystemStateObserver`]
|
||||||
|
#[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<S> Feedback<S> for SysMapFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor + HasNamedMetadata,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
{
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
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::<QemuSystemStateObserver>("systemstate")
|
||||||
|
.expect("QemuSystemStateObserver not found");
|
||||||
|
let feedbackstate = match state
|
||||||
|
.named_metadata_mut()
|
||||||
|
.get_mut::<SysGraphFeedbackState>("SysMap") {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
let n=SysGraphFeedbackState::default();
|
||||||
|
state.named_metadata_mut().insert(n, "SysMap");
|
||||||
|
state.named_metadata_mut().get_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<S::Input>) -> 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: &S::Input) -> 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>,
|
||||||
|
// {
|
||||||
|
// 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>,
|
||||||
|
// {
|
||||||
|
// 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>,
|
||||||
|
// {
|
||||||
|
// 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>,
|
||||||
|
// {
|
||||||
|
// 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>,
|
||||||
|
// {
|
||||||
|
// 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>,
|
||||||
|
// {
|
||||||
|
// fn name(&self) -> &str {
|
||||||
|
// "RandGraphSuffixMutator"
|
||||||
|
// }
|
||||||
|
// }
|
209
fuzzers/FRET/src/systemstate/helpers.rs
Normal file
209
fuzzers/FRET/src/systemstate/helpers.rs
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
use std::cell::UnsafeCell;
|
||||||
|
use std::io::Write;
|
||||||
|
use std::ops::Range;
|
||||||
|
use libafl::prelude::UsesInput;
|
||||||
|
use libafl_qemu::Emulator;
|
||||||
|
use libafl_qemu::GuestAddr;
|
||||||
|
use libafl_qemu::QemuHooks;
|
||||||
|
use libafl_qemu::edges::QemuEdgesMapMetadata;
|
||||||
|
use libafl_qemu::emu;
|
||||||
|
use libafl_qemu::hooks;
|
||||||
|
use crate::systemstate::RawFreeRTOSSystemState;
|
||||||
|
use crate::systemstate::CURRENT_SYSTEMSTATE_VEC;
|
||||||
|
use crate::systemstate::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_qemu::{
|
||||||
|
helper::{QemuHelper, QemuHelperTuple},
|
||||||
|
// edges::SAVED_JUMP,
|
||||||
|
};
|
||||||
|
|
||||||
|
//============================= Struct definitions
|
||||||
|
|
||||||
|
pub static mut INTR_OFFSET : Option<u64> = None;
|
||||||
|
pub static mut INTR_DONE : bool = true;
|
||||||
|
|
||||||
|
// only used when inputs are injected
|
||||||
|
pub static mut NEXT_INPUT : Vec<u8> = Vec::new();
|
||||||
|
|
||||||
|
//============================= Qemu Helper
|
||||||
|
|
||||||
|
/// A Qemu Helper with reads FreeRTOS specific structs from Qemu whenever certain syscalls occur, also inject inputs
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct QemuSystemStateHelper {
|
||||||
|
kerneladdr: u32,
|
||||||
|
tcb_addr: u32,
|
||||||
|
ready_queues: u32,
|
||||||
|
input_counter: Option<u64>,
|
||||||
|
app_range: Range<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuSystemStateHelper {
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(
|
||||||
|
kerneladdr: u32,
|
||||||
|
tcb_addr: u32,
|
||||||
|
ready_queues: u32,
|
||||||
|
input_counter: Option<u64>,
|
||||||
|
app_range: Range<u32>,
|
||||||
|
) -> Self {
|
||||||
|
QemuSystemStateHelper {
|
||||||
|
kerneladdr,
|
||||||
|
tcb_addr: tcb_addr,
|
||||||
|
ready_queues: ready_queues,
|
||||||
|
input_counter: input_counter,
|
||||||
|
app_range,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> QemuHelper<S> for QemuSystemStateHelper
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
fn first_exec<QT>(&self, _hooks: &QemuHooks<'_, QT, S>)
|
||||||
|
where
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
_hooks.instruction(self.kerneladdr, exec_syscall_hook::<QT, S>, false);
|
||||||
|
#[cfg(feature = "trace_abbs")]
|
||||||
|
_hooks.jmps(Some(gen_jmp_is_syscall::<QT, S>), Some(trace_api_call::<QT, S>));
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: refactor duplicate code
|
||||||
|
fn pre_exec(&mut self, _emulator: &Emulator, _input: &S::Input) {
|
||||||
|
unsafe {
|
||||||
|
CURRENT_SYSTEMSTATE_VEC.clear();
|
||||||
|
let p = LAST_API_CALL.with(|x| x.get());
|
||||||
|
*p = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(&mut self, emulator: &Emulator, _input: &S::Input) {
|
||||||
|
trigger_collection(emulator, self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) {
|
||||||
|
let listbytes : u32 = u32::try_from(std::mem::size_of::<freertos::List_t>()).unwrap();
|
||||||
|
let mut systemstate = RawFreeRTOSSystemState::default();
|
||||||
|
unsafe {
|
||||||
|
// TODO: investigate why can_do_io is not set sometimes, as this is just a workaround
|
||||||
|
let c = emulator.cpu_from_index(0);
|
||||||
|
let can_do_io = (*c.raw_ptr()).can_do_io;
|
||||||
|
(*c.raw_ptr()).can_do_io = 1;
|
||||||
|
systemstate.qemu_tick = emu::icount_get_raw();
|
||||||
|
(*c.raw_ptr()).can_do_io = can_do_io;
|
||||||
|
}
|
||||||
|
let mut buf : [u8; 4] = [0,0,0,0];
|
||||||
|
match h.input_counter {
|
||||||
|
Some(s) => unsafe { emulator.read_phys_mem(s, &mut buf); },
|
||||||
|
None => (),
|
||||||
|
};
|
||||||
|
systemstate.input_counter = u32::from_le_bytes(buf);
|
||||||
|
|
||||||
|
let curr_tcb_addr : freertos::void_ptr = freertos::emu_lookup::lookup(emulator, h.tcb_addr);
|
||||||
|
if curr_tcb_addr == 0 {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
systemstate.current_tcb = freertos::emu_lookup::lookup(emulator,curr_tcb_addr);
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
LAST_API_CALL.with(|x|
|
||||||
|
match *x.get() {
|
||||||
|
Some(s) => {
|
||||||
|
systemstate.last_pc = Some(s.0 as u64);
|
||||||
|
},
|
||||||
|
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;
|
||||||
|
systemstate.prio_ready_lists[i] = freertos::emu_lookup::lookup(emulator, target);
|
||||||
|
// println!("List at {}: {:?}",target, systemstate.prio_ready_lists[i]);
|
||||||
|
let mut next_index = systemstate.prio_ready_lists[i].pxIndex;
|
||||||
|
for _j in 0..systemstate.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;
|
||||||
|
systemstate.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);
|
||||||
|
systemstate.dumping_ground.insert(next_item.pvOwner,TCB_struct(next_tcb.clone()));
|
||||||
|
systemstate.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);
|
||||||
|
systemstate.dumping_ground.insert(next_index,List_MiniItem_struct(next_item));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe { CURRENT_SYSTEMSTATE_VEC.push(systemstate); }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exec_syscall_hook<QT, S>(
|
||||||
|
hooks: &mut QemuHooks<'_, QT, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
_pc: u32,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
let emulator = hooks.emulator();
|
||||||
|
let h = hooks.helpers().match_first_type::<QemuSystemStateHelper>().expect("QemuSystemHelper not found in helper tupel");
|
||||||
|
trigger_collection(emulator, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
thread_local!(static LAST_API_CALL : UnsafeCell<Option<(GuestAddr,GuestAddr)>> = UnsafeCell::new(None));
|
||||||
|
|
||||||
|
pub fn gen_jmp_is_syscall<QT, S>(
|
||||||
|
hooks: &mut QemuHooks<'_, QT, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
src: GuestAddr,
|
||||||
|
dest: GuestAddr,
|
||||||
|
) -> Option<u64>
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
if let Some(h) = hooks.helpers().match_first_type::<QemuSystemStateHelper>() {
|
||||||
|
if h.app_range.contains(&src) && !h.app_range.contains(&dest) {
|
||||||
|
// println!("New jmp {:x} {:x}", src, dest);
|
||||||
|
return Some(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn trace_api_call<QT, S>(
|
||||||
|
_hooks: &mut QemuHooks<'_, QT, S>,
|
||||||
|
_state: Option<&mut S>,
|
||||||
|
src: GuestAddr, dest: GuestAddr, id: u64
|
||||||
|
)
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let p = LAST_API_CALL.with(|x| x.get());
|
||||||
|
*p = Some((src,dest));
|
||||||
|
// print!("*");
|
||||||
|
}
|
||||||
|
}
|
167
fuzzers/FRET/src/systemstate/mod.rs
Normal file
167
fuzzers/FRET/src/systemstate/mod.rs
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
//! systemstate 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 schedulers;
|
||||||
|
|
||||||
|
// #[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_SYSTEMSTATE_VEC: Vec<RawFreeRTOSSystemState> = vec![];
|
||||||
|
|
||||||
|
/// A reduced version of freertos::TCB_t
|
||||||
|
#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
|
||||||
|
pub struct RefinedTCB {
|
||||||
|
pub task_name: String,
|
||||||
|
pub priority: u32,
|
||||||
|
pub 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);
|
||||||
|
#[cfg(not(feature = "no_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 {
|
||||||
|
pub start_tick: u64,
|
||||||
|
pub end_tick: u64,
|
||||||
|
last_pc: Option<u64>,
|
||||||
|
input_counter: u32,
|
||||||
|
pub 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 {
|
||||||
|
pub inner: Vec<RefinedFreeRTOSSystemState>,
|
||||||
|
trace_length: usize,
|
||||||
|
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 {trace_length: inner.len(), 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 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()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Entry = usize;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasRefCnt for FreeRTOSSystemStateMetadata {
|
||||||
|
fn refcnt(&self) -> isize {
|
||||||
|
self.tcref
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refcnt_mut(&mut self) -> &mut isize {
|
||||||
|
&mut self.tcref
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
libafl::impl_serdeany!(FreeRTOSSystemStateMetadata);
|
133
fuzzers/FRET/src/systemstate/observers.rs
Normal file
133
fuzzers/FRET/src/systemstate/observers.rs
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
// use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
|
||||||
|
use libafl::prelude::{ExitKind, AsSlice};
|
||||||
|
use libafl::{inputs::HasTargetBytes, prelude::UsesInput};
|
||||||
|
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_SYSTEMSTATE_VEC,
|
||||||
|
RawFreeRTOSSystemState,
|
||||||
|
RefinedTCB,
|
||||||
|
RefinedFreeRTOSSystemState,
|
||||||
|
freertos::{List_t, TCB_t, rtos_struct, rtos_struct::*},
|
||||||
|
};
|
||||||
|
|
||||||
|
//============================= Observer
|
||||||
|
|
||||||
|
/// The Qemusystemstate Observer retrieves the systemstate
|
||||||
|
/// that will get updated by the target.
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Default)]
|
||||||
|
#[allow(clippy::unsafe_derive_deserialize)]
|
||||||
|
pub struct QemuSystemStateObserver
|
||||||
|
{
|
||||||
|
pub last_run: Vec<RefinedFreeRTOSSystemState>,
|
||||||
|
pub last_input: Vec<u8>,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Observer<S> for QemuSystemStateObserver
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
S::Input : HasTargetBytes,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
|
unsafe {CURRENT_SYSTEMSTATE_VEC.clear(); }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> {
|
||||||
|
unsafe {self.last_run = refine_system_states(&mut CURRENT_SYSTEMSTATE_VEC);}
|
||||||
|
self.last_input=_input.target_bytes().as_slice().to_owned();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for QemuSystemStateObserver
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
self.name.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HasLen for QemuSystemStateObserver
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.last_run.len()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuSystemStateObserver {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self{last_run: vec![], last_input: vec![], name: "systemstate".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;
|
||||||
|
}
|
267
fuzzers/FRET/src/systemstate/schedulers.rs
Normal file
267
fuzzers/FRET/src/systemstate/schedulers.rs
Normal file
@ -0,0 +1,267 @@
|
|||||||
|
//! The Minimizer schedulers are a family of corpus schedulers that feed the fuzzer
|
||||||
|
//! with testcases only from a subset of the total corpus.
|
||||||
|
|
||||||
|
use core::{marker::PhantomData};
|
||||||
|
use std::{cmp::{max, min}, mem::swap, borrow::BorrowMut};
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
bolts::{rands::Rand, serdeany::SerdeAny, AsSlice, HasRefCnt},
|
||||||
|
corpus::{Corpus, Testcase},
|
||||||
|
inputs::UsesInput,
|
||||||
|
schedulers::{Scheduler, TestcaseScore, minimizer::DEFAULT_SKIP_NON_FAVORED_PROB },
|
||||||
|
state::{HasCorpus, HasMetadata, HasRand, UsesState, State},
|
||||||
|
Error, SerdeAny, prelude::HasLen,
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::worst::MaxTimeFavFactor;
|
||||||
|
|
||||||
|
use super::FreeRTOSSystemStateMetadata;
|
||||||
|
|
||||||
|
/// A state metadata holding a map of favoreds testcases for each map entry
|
||||||
|
#[derive(Debug, Serialize, Deserialize, SerdeAny, Default)]
|
||||||
|
pub struct LongestTracesMetadata {
|
||||||
|
/// map index -> corpus index
|
||||||
|
pub max_trace_length: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl LongestTracesMetadata {
|
||||||
|
fn new(l : usize) -> Self {
|
||||||
|
Self {max_trace_length: l}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The [`MinimizerScheduler`] employs a genetic algorithm to compute a subset of the
|
||||||
|
/// corpus that exercise all the requested features (e.g. all the coverage seen so far)
|
||||||
|
/// prioritizing [`Testcase`]`s` using [`TestcaseScore`]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct LongestTraceScheduler<CS> {
|
||||||
|
base: CS,
|
||||||
|
skip_non_favored_prob: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CS> UsesState for LongestTraceScheduler<CS>
|
||||||
|
where
|
||||||
|
CS: UsesState,
|
||||||
|
{
|
||||||
|
type State = CS::State;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CS> Scheduler for LongestTraceScheduler<CS>
|
||||||
|
where
|
||||||
|
CS: Scheduler,
|
||||||
|
CS::State: HasCorpus + HasMetadata + HasRand,
|
||||||
|
{
|
||||||
|
/// Add an entry to the corpus and return its index
|
||||||
|
fn on_add(&self, state: &mut CS::State, idx: usize) -> Result<(), Error> {
|
||||||
|
let l = state.corpus()
|
||||||
|
.get(idx)?
|
||||||
|
.borrow()
|
||||||
|
.metadata()
|
||||||
|
.get::<FreeRTOSSystemStateMetadata>().map_or(0, |x| x.trace_length);
|
||||||
|
self.get_update_trace_length(state,l);
|
||||||
|
self.base.on_add(state, idx)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Replaces the testcase at the given idx
|
||||||
|
fn on_replace(
|
||||||
|
&self,
|
||||||
|
state: &mut CS::State,
|
||||||
|
idx: usize,
|
||||||
|
testcase: &Testcase<<CS::State as UsesInput>::Input>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let l = state.corpus()
|
||||||
|
.get(idx)?
|
||||||
|
.borrow()
|
||||||
|
.metadata()
|
||||||
|
.get::<FreeRTOSSystemStateMetadata>().map_or(0, |x| x.trace_length);
|
||||||
|
self.get_update_trace_length(state, l);
|
||||||
|
self.base.on_replace(state, idx, testcase)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Removes an entry from the corpus, returning M if M was present.
|
||||||
|
fn on_remove(
|
||||||
|
&self,
|
||||||
|
state: &mut CS::State,
|
||||||
|
idx: usize,
|
||||||
|
testcase: &Option<Testcase<<CS::State as UsesInput>::Input>>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
self.base.on_remove(state, idx, testcase)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the next entry
|
||||||
|
fn next(&self, state: &mut CS::State) -> Result<usize, Error> {
|
||||||
|
let mut idx = self.base.next(state)?;
|
||||||
|
while {
|
||||||
|
let l = state.corpus()
|
||||||
|
.get(idx)?
|
||||||
|
.borrow()
|
||||||
|
.metadata()
|
||||||
|
.get::<FreeRTOSSystemStateMetadata>().map_or(0, |x| x.trace_length);
|
||||||
|
let m = self.get_update_trace_length(state,l);
|
||||||
|
state.rand_mut().below(m) > l as u64
|
||||||
|
} && state.rand_mut().below(100) < self.skip_non_favored_prob
|
||||||
|
{
|
||||||
|
idx = self.base.next(state)?;
|
||||||
|
}
|
||||||
|
Ok(idx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<CS> LongestTraceScheduler<CS>
|
||||||
|
where
|
||||||
|
CS: Scheduler,
|
||||||
|
CS::State: HasCorpus + HasMetadata + HasRand,
|
||||||
|
{
|
||||||
|
pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 {
|
||||||
|
// Create a new top rated meta if not existing
|
||||||
|
if let Some(td) = state.metadata_mut().get_mut::<LongestTracesMetadata>() {
|
||||||
|
let m = max(td.max_trace_length, par);
|
||||||
|
td.max_trace_length = m;
|
||||||
|
m as u64
|
||||||
|
} else {
|
||||||
|
state.add_metadata(LongestTracesMetadata::new(par));
|
||||||
|
par as u64
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn new(base: CS) -> Self {
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================================
|
||||||
|
|
||||||
|
/// A state metadata holding a map of favoreds testcases for each map entry
|
||||||
|
#[derive(Debug, Serialize, Deserialize, SerdeAny, Default)]
|
||||||
|
pub struct GeneticMetadata {
|
||||||
|
pub current_gen: Vec<(usize, f64)>,
|
||||||
|
pub current_cursor: usize,
|
||||||
|
pub next_gen: Vec<(usize, f64)>,
|
||||||
|
pub gen: usize
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GeneticMetadata {
|
||||||
|
fn new(current_gen: Vec<(usize, f64)>, next_gen: Vec<(usize, f64)>) -> Self {
|
||||||
|
Self {current_gen, current_cursor: 0, next_gen, gen: 0}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct GenerationScheduler<S> {
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
gen_size: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> UsesState for GenerationScheduler<S>
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
type State = S;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Scheduler for GenerationScheduler<S>
|
||||||
|
where
|
||||||
|
S: HasCorpus + HasMetadata,
|
||||||
|
S::Input: HasLen,
|
||||||
|
{
|
||||||
|
/// get first element in current gen,
|
||||||
|
/// if current_gen is empty, swap lists, sort by FavFactor, take top k and return first
|
||||||
|
fn next(&self, state: &mut Self::State) -> Result<usize, Error> {
|
||||||
|
let mut to_remove : Vec<(usize, f64)> = vec![];
|
||||||
|
let mut to_return : usize = 0;
|
||||||
|
let c = state.corpus().count();
|
||||||
|
let gm = state.metadata_mut().get_mut::<GeneticMetadata>().expect("Corpus Scheduler empty");
|
||||||
|
// println!("index: {} curr: {:?} next: {:?} gen: {} corp: {}", gm.current_cursor, gm.current_gen.len(), gm.next_gen.len(), gm.gen,
|
||||||
|
// c);
|
||||||
|
match gm.current_gen.get(gm.current_cursor) {
|
||||||
|
Some(c) => {
|
||||||
|
gm.current_cursor+=1;
|
||||||
|
// println!("normal next: {}", (*c).0);
|
||||||
|
return Ok((*c).0)
|
||||||
|
},
|
||||||
|
None => {
|
||||||
|
swap(&mut to_remove, &mut gm.current_gen);
|
||||||
|
swap(&mut gm.next_gen, &mut gm.current_gen);
|
||||||
|
gm.current_gen.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
|
||||||
|
// gm.current_gen.reverse();
|
||||||
|
if gm.current_gen.len() == 0 {panic!("Corpus is empty");}
|
||||||
|
let d : Vec<(usize, f64)> = gm.current_gen.drain(min(gm.current_gen.len(), self.gen_size)..).collect();
|
||||||
|
to_remove.extend(d);
|
||||||
|
// move all indices to the left, since all other indices will be deleted
|
||||||
|
gm.current_gen.sort_by(|a,b| a.0.cmp(&(*b).0)); // in order of the corpus index
|
||||||
|
for i in 0..gm.current_gen.len() {
|
||||||
|
gm.current_gen[i] = (i, gm.current_gen[i].1);
|
||||||
|
}
|
||||||
|
to_return = gm.current_gen.get(0).unwrap().0;
|
||||||
|
gm.current_cursor=1;
|
||||||
|
gm.gen+=1;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// removing these elements will move all indices left by to_remove.len()
|
||||||
|
to_remove.sort_by(|x,y| x.0.cmp(&(*y).0));
|
||||||
|
to_remove.reverse();
|
||||||
|
for i in to_remove {
|
||||||
|
state.corpus_mut().remove(i.0).unwrap();
|
||||||
|
}
|
||||||
|
// println!("switch next: {to_return}");
|
||||||
|
return Ok(to_return);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add the new input to the next generation
|
||||||
|
fn on_add(
|
||||||
|
&self,
|
||||||
|
state: &mut Self::State,
|
||||||
|
idx: usize
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// println!("On Add {idx}");
|
||||||
|
let mut tc = state.corpus_mut().get(idx).unwrap().borrow_mut().clone();
|
||||||
|
let ff = MaxTimeFavFactor::compute(&mut tc, state).unwrap();
|
||||||
|
if let Some(gm) = state.metadata_mut().get_mut::<GeneticMetadata>() {
|
||||||
|
gm.next_gen.push((idx,ff));
|
||||||
|
} else {
|
||||||
|
state.add_metadata(GeneticMetadata::new(vec![], vec![(idx,ff)]));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn on_replace(
|
||||||
|
&self,
|
||||||
|
_state: &mut Self::State,
|
||||||
|
_idx: usize,
|
||||||
|
_prev: &Testcase<<Self::State as UsesInput>::Input>
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// println!("On Replace {_idx}");
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_remove(
|
||||||
|
&self,
|
||||||
|
state: &mut Self::State,
|
||||||
|
idx: usize,
|
||||||
|
_testcase: &Option<Testcase<<Self::State as UsesInput>::Input>>
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
// println!("On Remove {idx}");
|
||||||
|
if let Some(gm) = state.metadata_mut().get_mut::<GeneticMetadata>() {
|
||||||
|
gm.next_gen = gm.next_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::<Vec<(usize, f64)>>();
|
||||||
|
gm.current_gen = gm.current_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::<Vec<(usize, f64)>>();
|
||||||
|
} else {
|
||||||
|
state.add_metadata(GeneticMetadata::new(vec![], vec![]));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> GenerationScheduler<S>
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
phantom: PhantomData,
|
||||||
|
gen_size: 100,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
381
fuzzers/FRET/src/worst.rs
Normal file
381
fuzzers/FRET/src/worst.rs
Normal file
@ -0,0 +1,381 @@
|
|||||||
|
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::prelude::{UsesInput, AsSlice};
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use libafl::schedulers::{MinimizerScheduler, TestcaseScore};
|
||||||
|
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, HasCorpus, UsesState};
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::clock::QemuClockObserver;
|
||||||
|
use crate::systemstate::FreeRTOSSystemStateMetadata;
|
||||||
|
//=========================== Scheduler
|
||||||
|
|
||||||
|
pub type TimeMaximizerCorpusScheduler<CS> =
|
||||||
|
MinimizerScheduler<CS, MaxTimeFavFactor<<CS as UsesState>::State>, MapIndexesMetadata>;
|
||||||
|
|
||||||
|
/// Multiply the testcase size with the execution time.
|
||||||
|
/// This favors small and quick testcases.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MaxTimeFavFactor<S>
|
||||||
|
where
|
||||||
|
S: HasCorpus + HasMetadata,
|
||||||
|
S::Input: HasLen,
|
||||||
|
{
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> TestcaseScore<S> for MaxTimeFavFactor<S>
|
||||||
|
where
|
||||||
|
S: HasCorpus + HasMetadata,
|
||||||
|
S::Input: HasLen,
|
||||||
|
{
|
||||||
|
fn compute(entry: &mut Testcase<<S as UsesInput>::Input>, state: &S) -> Result<f64, Error> {
|
||||||
|
// TODO maybe enforce entry.exec_time().is_some()
|
||||||
|
let et = entry.exec_time().expect("testcase.exec_time is needed for scheduler");
|
||||||
|
let tns : i64 = et.as_nanos().try_into().expect("failed to convert time");
|
||||||
|
Ok(-tns as f64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type LenTimeMaximizerCorpusScheduler<CS> =
|
||||||
|
MinimizerScheduler<CS, MaxExecsLenFavFactor<<CS as UsesState>::State>, MapIndexesMetadata>;
|
||||||
|
|
||||||
|
pub type TimeStateMaximizerCorpusScheduler<CS> =
|
||||||
|
MinimizerScheduler<CS, MaxTimeFavFactor<<CS as UsesState>::State>, FreeRTOSSystemStateMetadata>;
|
||||||
|
|
||||||
|
/// Multiply the testcase size with the execution time.
|
||||||
|
/// This favors small and quick testcases.
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct MaxExecsLenFavFactor<S>
|
||||||
|
where
|
||||||
|
S: HasCorpus + HasMetadata,
|
||||||
|
S::Input: HasLen,
|
||||||
|
{
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> TestcaseScore<S> for MaxExecsLenFavFactor<S>
|
||||||
|
where
|
||||||
|
S: HasCorpus + HasMetadata,
|
||||||
|
S::Input: HasLen,
|
||||||
|
{
|
||||||
|
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error> {
|
||||||
|
let execs_per_hour = (3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64());
|
||||||
|
let execs_times_length_per_hour = execs_per_hour*entry.cached_len()? as f64;
|
||||||
|
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<S> for SortedFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
{
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
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 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<S> Feedback<S> for ExecTimeReachedFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor,
|
||||||
|
{
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
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");
|
||||||
|
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<S> Feedback<S> for ExecTimeCollectorFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor,
|
||||||
|
{
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
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");
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//===================================================================
|
||||||
|
/// A Feedback which expects a certain minimum execution time
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct ExecTimeIncFeedback
|
||||||
|
{
|
||||||
|
longest_time: u64,
|
||||||
|
last_is_longest: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for ExecTimeIncFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor,
|
||||||
|
{
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
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>("clocktime")
|
||||||
|
.expect("QemuClockObserver not found");
|
||||||
|
if observer.last_runtime() > self.longest_time {
|
||||||
|
self.longest_time = observer.last_runtime();
|
||||||
|
self.last_is_longest = true;
|
||||||
|
Ok(true)
|
||||||
|
} else {
|
||||||
|
self.last_is_longest = false;
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn append_metadata(
|
||||||
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
|
testcase: &mut Testcase<<S as UsesInput>::Input>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
#[cfg(feature = "feed_afl")]
|
||||||
|
if self.last_is_longest {
|
||||||
|
let mim : Option<&mut MapIndexesMetadata>= testcase.metadata_mut().get_mut();
|
||||||
|
// pretend that the longest input alone excercises some non-existing edge, to keep it relevant
|
||||||
|
mim.unwrap().list.push(usize::MAX);
|
||||||
|
};
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for ExecTimeIncFeedback
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"ExecTimeReachedFeedback"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecTimeIncFeedback
|
||||||
|
where
|
||||||
|
{
|
||||||
|
/// Creates a new [`ExecTimeReachedFeedback`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {longest_time: 0, last_is_longest: false}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Noop Feedback which records a list of all execution times
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct AlwaysTrueFeedback
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for AlwaysTrueFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor,
|
||||||
|
{
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
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>,
|
||||||
|
{
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for AlwaysTrueFeedback
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
"AlwaysTrueFeedback"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AlwaysTrueFeedback
|
||||||
|
where
|
||||||
|
{
|
||||||
|
/// Creates a new [`ExecTimeCollectorFeedback`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -82,7 +82,7 @@ windows_alias = "unsupported"
|
|||||||
[tasks.run_unix]
|
[tasks.run_unix]
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script='''
|
script='''
|
||||||
./${FUZZER_NAME} --cores 0 --input ./corpus &
|
./${FUZZER_NAME} --cores 0 --input ./corpus
|
||||||
'''
|
'''
|
||||||
dependencies = [ "fuzzer" ]
|
dependencies = [ "fuzzer" ]
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ windows_alias = "unsupported"
|
|||||||
[tasks.run_unix]
|
[tasks.run_unix]
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script='''
|
script='''
|
||||||
./${FUZZER_NAME} --cores 0 --input ./corpus &
|
./${FUZZER_NAME} --cores 0 --input ./corpus
|
||||||
'''
|
'''
|
||||||
dependencies = [ "fuzzer" ]
|
dependencies = [ "fuzzer" ]
|
||||||
|
|
||||||
|
@ -82,7 +82,7 @@ windows_alias = "unsupported"
|
|||||||
[tasks.run_unix]
|
[tasks.run_unix]
|
||||||
script_runner = "@shell"
|
script_runner = "@shell"
|
||||||
script='''
|
script='''
|
||||||
./${FUZZER_NAME} --cores 0 --input ./corpus &
|
./${FUZZER_NAME} --cores 0 --input ./corpus
|
||||||
'''
|
'''
|
||||||
dependencies = [ "fuzzer" ]
|
dependencies = [ "fuzzer" ]
|
||||||
|
|
||||||
|
@ -9,9 +9,10 @@ default = ["std"]
|
|||||||
std = []
|
std = []
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
incremental = true
|
||||||
codegen-units = 1
|
|
||||||
debug = true
|
debug = true
|
||||||
|
lto = "fat"
|
||||||
|
codegen-units = 1
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../libafl/" }
|
libafl = { path = "../../libafl/" }
|
||||||
|
@ -28,7 +28,6 @@ use libafl::{
|
|||||||
stages::StdMutationalStage,
|
stages::StdMutationalStage,
|
||||||
state::{HasCorpus, StdState},
|
state::{HasCorpus, StdState},
|
||||||
Error,
|
Error,
|
||||||
//prelude::{SimpleMonitor, SimpleEventManager},
|
|
||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor,
|
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor,
|
||||||
@ -88,20 +87,15 @@ pub fn fuzz() {
|
|||||||
}
|
}
|
||||||
emu.remove_breakpoint(main_addr);
|
emu.remove_breakpoint(main_addr);
|
||||||
|
|
||||||
// emu.save_snapshot("start", true);
|
|
||||||
|
|
||||||
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
emu.set_breakpoint(breakpoint); // BREAKPOINT
|
||||||
|
|
||||||
//use libafl_qemu::IntoEnumIterator;
|
// let saved_cpu_states: Vec<_> = (0..emu.num_cpus())
|
||||||
// Save the GPRs
|
// .map(|i| emu.cpu_from_index(i).save_state())
|
||||||
//let mut saved_regs: Vec<u64> = vec![];
|
// .collect();
|
||||||
//for r in Regs::iter() {
|
|
||||||
// saved_regs.push(emu.cpu_from_index(0).read_reg(r).unwrap());
|
|
||||||
//}
|
|
||||||
|
|
||||||
let mut saved_cpu_states: Vec<_> = (0..emu.num_cpus())
|
// emu.save_snapshot("start", true);
|
||||||
.map(|i| emu.cpu_from_index(i).save_state())
|
|
||||||
.collect();
|
let snap = emu.create_fast_snapshot(true);
|
||||||
|
|
||||||
// The wrapped harness function, calling out to the LLVM-style harness
|
// The wrapped harness function, calling out to the LLVM-style harness
|
||||||
let mut harness = |input: &BytesInput| {
|
let mut harness = |input: &BytesInput| {
|
||||||
@ -114,10 +108,6 @@ pub fn fuzz() {
|
|||||||
// len = MAX_INPUT_SIZE;
|
// len = MAX_INPUT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
//for (r, v) in saved_regs.iter().enumerate() {
|
|
||||||
// emu.cpu_from_index(0).write_reg(r as i32, *v).unwrap();
|
|
||||||
//}
|
|
||||||
|
|
||||||
emu.write_phys_mem(input_addr, buf);
|
emu.write_phys_mem(input_addr, buf);
|
||||||
|
|
||||||
emu.run();
|
emu.run();
|
||||||
@ -126,19 +116,24 @@ pub fn fuzz() {
|
|||||||
let mut pcs = (0..emu.num_cpus())
|
let mut pcs = (0..emu.num_cpus())
|
||||||
.map(|i| emu.cpu_from_index(i))
|
.map(|i| emu.cpu_from_index(i))
|
||||||
.map(|cpu| -> Result<u32, String> { cpu.read_reg(Regs::Pc) });
|
.map(|cpu| -> Result<u32, String> { cpu.read_reg(Regs::Pc) });
|
||||||
let ret = match pcs
|
let _ret = match pcs
|
||||||
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
|
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
|
||||||
{
|
{
|
||||||
Some(_) => ExitKind::Ok,
|
Some(_) => ExitKind::Ok,
|
||||||
None => ExitKind::Crash,
|
None => ExitKind::Crash,
|
||||||
};
|
};
|
||||||
|
|
||||||
for (i, s) in saved_cpu_states.iter().enumerate() {
|
// OPTION 1: restore only the CPU state (registers et. al)
|
||||||
emu.cpu_from_index(i).restore_state(s);
|
// for (i, s) in saved_cpu_states.iter().enumerate() {
|
||||||
}
|
// emu.cpu_from_index(i).restore_state(s);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// OPTION 2: restore a slow vanilla QEMU snapshot
|
||||||
// emu.load_snapshot("start", true);
|
// emu.load_snapshot("start", true);
|
||||||
|
|
||||||
|
// OPTION 3: restore a fast devices+mem snapshot
|
||||||
|
emu.restore_fast_snapshot(snap);
|
||||||
|
|
||||||
ret
|
ret
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -161,7 +156,7 @@ pub fn fuzz() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// A feedback to choose if an input is a solution or not
|
// A feedback to choose if an input is a solution or not
|
||||||
let mut objective = CrashFeedback::new(); //feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
|
|
||||||
// If not restarting, create a State from scratch
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
let mut state = state.unwrap_or_else(|| {
|
||||||
|
13
fuzzers/tinyinst_simple/Cargo.toml
Normal file
13
fuzzers/tinyinst_simple/Cargo.toml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
[package]
|
||||||
|
name = "tinyinst_simple"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libafl = { version = "0.8.1", path = "../../libafl", features = ["tui"] }
|
||||||
|
libafl_tinyinst = { version = "0.8.1", path = "../../libafl_tinyinst" }
|
||||||
|
[profile.release]
|
||||||
|
codegen-units = 1
|
||||||
|
opt-level = 3
|
8
fuzzers/tinyinst_simple/Makefile.toml
Normal file
8
fuzzers/tinyinst_simple/Makefile.toml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
[tasks.build_test]
|
||||||
|
command = "cl"
|
||||||
|
args = ["./test/test.c", "-o", "./test/test.exe"]
|
||||||
|
|
||||||
|
[tasks.run]
|
||||||
|
dependencies = ["build_test"]
|
||||||
|
command = "cargo"
|
||||||
|
args = ["run", "--release"]
|
3
fuzzers/tinyinst_simple/README.md
Normal file
3
fuzzers/tinyinst_simple/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Run Instruction
|
||||||
|
1. Open up developer powershell so that you have access to cl (Windows Default Compiler)
|
||||||
|
2. Run `cargo make run` to run the fuzzer
|
62
fuzzers/tinyinst_simple/src/main.rs
Normal file
62
fuzzers/tinyinst_simple/src/main.rs
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
bolts::{
|
||||||
|
rands::{RandomSeed, StdRand},
|
||||||
|
tuples::tuple_list,
|
||||||
|
},
|
||||||
|
corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus, Testcase},
|
||||||
|
events::SimpleEventManager,
|
||||||
|
feedbacks::{CrashFeedback, ListFeedback},
|
||||||
|
inputs::BytesInput,
|
||||||
|
monitors::SimpleMonitor,
|
||||||
|
mutators::{havoc_mutations, StdScheduledMutator},
|
||||||
|
observers::ListObserver,
|
||||||
|
schedulers::RandScheduler,
|
||||||
|
stages::StdMutationalStage,
|
||||||
|
state::StdState,
|
||||||
|
Fuzzer, StdFuzzer,
|
||||||
|
};
|
||||||
|
use libafl_tinyinst::executor::TinyInstExecutor;
|
||||||
|
static mut COVERAGE: Vec<u64> = vec![];
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Tinyinst things
|
||||||
|
let tinyinst_args = vec!["-instrument_module".to_string(), "test.exe".to_string()];
|
||||||
|
|
||||||
|
let args = vec![".\\test\\test.exe".to_string(), "@@".to_string()];
|
||||||
|
|
||||||
|
let observer = ListObserver::new("cov", unsafe { &mut COVERAGE });
|
||||||
|
let mut feedback = ListFeedback::new_with_observer(&observer);
|
||||||
|
|
||||||
|
let input = BytesInput::new(b"bad".to_vec());
|
||||||
|
let rand = StdRand::new();
|
||||||
|
let mut corpus = CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap();
|
||||||
|
corpus
|
||||||
|
.add(Testcase::new(input))
|
||||||
|
.expect("error in adding corpus");
|
||||||
|
let solutions = OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap();
|
||||||
|
|
||||||
|
let mut objective = CrashFeedback::new();
|
||||||
|
let mut state = StdState::new(rand, corpus, solutions, &mut feedback, &mut objective).unwrap();
|
||||||
|
let scheduler = RandScheduler::new();
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
|
|
||||||
|
let monitor = SimpleMonitor::new(|x| println!("{}", x));
|
||||||
|
|
||||||
|
let mut mgr = SimpleEventManager::new(monitor);
|
||||||
|
let mut executor = unsafe {
|
||||||
|
TinyInstExecutor::new(
|
||||||
|
&mut COVERAGE,
|
||||||
|
tinyinst_args,
|
||||||
|
args,
|
||||||
|
5000,
|
||||||
|
tuple_list!(observer),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
fuzzer
|
||||||
|
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||||
|
.expect("error in fuzzing loop");
|
||||||
|
}
|
1
fuzzers/tinyinst_simple/test/crash_input.txt
Normal file
1
fuzzers/tinyinst_simple/test/crash_input.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
bad12
|
1
fuzzers/tinyinst_simple/test/ok_input.txt
Normal file
1
fuzzers/tinyinst_simple/test/ok_input.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
ok
|
36
fuzzers/tinyinst_simple/test/test.c
Normal file
36
fuzzers/tinyinst_simple/test/test.c
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
void pass1(char *buf, int buf_size) {
|
||||||
|
char target[0x10];
|
||||||
|
if (buf[0] == 'b') {
|
||||||
|
if (buf[1] == 'a') {
|
||||||
|
if (buf[2] == 'd') {
|
||||||
|
if (buf[3] == '1') {
|
||||||
|
if (buf[4] == '2') {
|
||||||
|
printf("You got me\n");
|
||||||
|
memcpy(target, buf, 0x1000000);
|
||||||
|
printf("GG\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
FILE *fp;
|
||||||
|
char buf[0x1000];
|
||||||
|
if (argc == 2) {
|
||||||
|
fp = fopen(argv[1], "r");
|
||||||
|
if (fp == NULL) {
|
||||||
|
printf("File not found\n");
|
||||||
|
printf("Received filename %s\n", argv[1]);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fscanf(fp, "%s", buf);
|
||||||
|
|
||||||
|
pass1(buf, sizeof(buf));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
printf("there is nothing\n");
|
||||||
|
}
|
||||||
|
}
|
@ -48,10 +48,7 @@ fn adder_loop(port: u16) -> ! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if current_result != last_result {
|
if current_result != last_result {
|
||||||
println!(
|
println!("Adder handled {msg_counter} messages, reporting {current_result} to broker");
|
||||||
"Adder handled {} messages, reporting {} to broker",
|
|
||||||
msg_counter, current_result
|
|
||||||
);
|
|
||||||
|
|
||||||
client
|
client
|
||||||
.send_buf(_TAG_MATH_RESULT_V1, ¤t_result.to_le_bytes())
|
.send_buf(_TAG_MATH_RESULT_V1, ¤t_result.to_le_bytes())
|
||||||
|
@ -132,8 +132,7 @@ impl Cores {
|
|||||||
|
|
||||||
if cores.is_empty() {
|
if cores.is_empty() {
|
||||||
return Err(Error::illegal_argument(format!(
|
return Err(Error::illegal_argument(format!(
|
||||||
"No cores specified! parsed: {}",
|
"No cores specified! parsed: {args}"
|
||||||
args
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -591,8 +590,7 @@ mod apple {
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::unknown(format!(
|
Err(Error::unknown(format!(
|
||||||
"Failed to set_for_current {:?}",
|
"Failed to set_for_current {result:?}"
|
||||||
result
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -615,8 +613,7 @@ mod apple {
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::unknown(format!(
|
Err(Error::unknown(format!(
|
||||||
"Failed to set_for_current {:?}",
|
"Failed to set_for_current {result:?}"
|
||||||
result
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,7 +165,7 @@ const LLMP_PAGE_HEADER_LEN: usize = size_of::<LlmpPage>();
|
|||||||
|
|
||||||
/// The llmp broker registers a signal handler for cleanups on `SIGINT`.
|
/// The llmp broker registers a signal handler for cleanups on `SIGINT`.
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
static mut GLOBAL_SIGHANDLER_STATE: LlmpBrokerSignalHandler = LlmpBrokerSignalHandler {
|
static mut LLMP_SIGHANDLER_STATE: LlmpShutdownSignalHandler = LlmpShutdownSignalHandler {
|
||||||
shutting_down: false,
|
shutting_down: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -466,9 +466,7 @@ unsafe fn _llmp_page_init<SHM: ShMem>(shmem: &mut SHM, sender_id: ClientId, allo
|
|||||||
if !allow_reinit {
|
if !allow_reinit {
|
||||||
assert!(
|
assert!(
|
||||||
(*page).magic != PAGE_INITIALIZED_MAGIC,
|
(*page).magic != PAGE_INITIALIZED_MAGIC,
|
||||||
"Tried to initialize page {:?} twice (for shmem {:?})",
|
"Tried to initialize page {page:?} twice (for shmem {shmem:?})"
|
||||||
page,
|
|
||||||
shmem
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,10 +639,7 @@ where
|
|||||||
}
|
}
|
||||||
Err(Error::File(e, _)) if e.kind() == ErrorKind::AddrInUse => {
|
Err(Error::File(e, _)) if e.kind() == ErrorKind::AddrInUse => {
|
||||||
// We are the client :)
|
// We are the client :)
|
||||||
println!(
|
println!("We're the client (internal port already bound by broker, {e:#?})");
|
||||||
"We're the client (internal port already bound by broker, {:#?})",
|
|
||||||
e
|
|
||||||
);
|
|
||||||
Ok(LlmpConnection::IsClient {
|
Ok(LlmpConnection::IsClient {
|
||||||
client: LlmpClient::create_attach_to_tcp(shmem_provider, port)?,
|
client: LlmpClient::create_attach_to_tcp(shmem_provider, port)?,
|
||||||
})
|
})
|
||||||
@ -1082,8 +1077,7 @@ where
|
|||||||
let page = self.out_shmems.last_mut().unwrap().page_mut();
|
let page = self.out_shmems.last_mut().unwrap().page_mut();
|
||||||
if msg.is_null() || !llmp_msg_in_page(page, msg) {
|
if msg.is_null() || !llmp_msg_in_page(page, msg) {
|
||||||
return Err(Error::unknown(format!(
|
return Err(Error::unknown(format!(
|
||||||
"Llmp Message {:?} is null or not in current page",
|
"Llmp Message {msg:?} is null or not in current page"
|
||||||
msg
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1189,8 +1183,7 @@ where
|
|||||||
match unsafe { self.alloc_next_if_space(buf_len) } {
|
match unsafe { self.alloc_next_if_space(buf_len) } {
|
||||||
Some(msg) => Ok(msg),
|
Some(msg) => Ok(msg),
|
||||||
None => Err(Error::unknown(format!(
|
None => Err(Error::unknown(format!(
|
||||||
"Error allocating {} bytes in shmap",
|
"Error allocating {buf_len} bytes in shmap"
|
||||||
buf_len
|
|
||||||
))),
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1258,8 +1251,7 @@ where
|
|||||||
|| tag == LLMP_TAG_UNSET
|
|| tag == LLMP_TAG_UNSET
|
||||||
{
|
{
|
||||||
return Err(Error::unknown(format!(
|
return Err(Error::unknown(format!(
|
||||||
"Reserved tag supplied to send_buf ({:#X})",
|
"Reserved tag supplied to send_buf ({tag:#X})"
|
||||||
tag
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1282,8 +1274,7 @@ where
|
|||||||
|| tag == LLMP_TAG_UNSET
|
|| tag == LLMP_TAG_UNSET
|
||||||
{
|
{
|
||||||
return Err(Error::unknown(format!(
|
return Err(Error::unknown(format!(
|
||||||
"Reserved tag supplied to send_buf ({:#X})",
|
"Reserved tag supplied to send_buf ({tag:#X})"
|
||||||
tag
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1742,8 +1733,7 @@ where
|
|||||||
let page_size = self.shmem.as_slice().len() - size_of::<LlmpPage>();
|
let page_size = self.shmem.as_slice().len() - size_of::<LlmpPage>();
|
||||||
if offset > page_size {
|
if offset > page_size {
|
||||||
Err(Error::illegal_argument(format!(
|
Err(Error::illegal_argument(format!(
|
||||||
"Msg offset out of bounds (size: {}, requested offset: {})",
|
"Msg offset out of bounds (size: {page_size}, requested offset: {offset})"
|
||||||
page_size, offset
|
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
unsafe { Ok(((*page).messages.as_mut_ptr() as *mut u8).add(offset) as *mut LlmpMsg) }
|
unsafe { Ok(((*page).messages.as_mut_ptr() as *mut u8).add(offset) as *mut LlmpMsg) }
|
||||||
@ -1770,12 +1760,12 @@ where
|
|||||||
/// A signal handler for the [`LlmpBroker`].
|
/// A signal handler for the [`LlmpBroker`].
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct LlmpBrokerSignalHandler {
|
pub struct LlmpShutdownSignalHandler {
|
||||||
shutting_down: bool,
|
shutting_down: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
impl Handler for LlmpBrokerSignalHandler {
|
impl Handler for LlmpShutdownSignalHandler {
|
||||||
fn handle(&mut self, _signal: Signal, _info: siginfo_t, _context: &mut ucontext_t) {
|
fn handle(&mut self, _signal: Signal, _info: siginfo_t, _context: &mut ucontext_t) {
|
||||||
unsafe {
|
unsafe {
|
||||||
ptr::write_volatile(&mut self.shutting_down, true);
|
ptr::write_volatile(&mut self.shutting_down, true);
|
||||||
@ -1957,7 +1947,7 @@ where
|
|||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
#[allow(clippy::unused_self)]
|
#[allow(clippy::unused_self)]
|
||||||
fn is_shutting_down(&self) -> bool {
|
fn is_shutting_down(&self) -> bool {
|
||||||
unsafe { ptr::read_volatile(&GLOBAL_SIGHANDLER_STATE.shutting_down) }
|
unsafe { ptr::read_volatile(&LLMP_SIGHANDLER_STATE.shutting_down) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Always returns true on platforms, where no shutdown signal handlers are supported
|
/// Always returns true on platforms, where no shutdown signal handlers are supported
|
||||||
@ -1976,7 +1966,7 @@ where
|
|||||||
F: FnMut(ClientId, Tag, Flags, &[u8]) -> Result<LlmpMsgHookResult, Error>,
|
F: FnMut(ClientId, Tag, Flags, &[u8]) -> Result<LlmpMsgHookResult, Error>,
|
||||||
{
|
{
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
if let Err(_e) = unsafe { setup_signal_handler(&mut GLOBAL_SIGHANDLER_STATE) } {
|
if let Err(_e) = unsafe { setup_signal_handler(&mut LLMP_SIGHANDLER_STATE) } {
|
||||||
// We can live without a proper ctrl+c signal handler. Print and ignore.
|
// We can live without a proper ctrl+c signal handler. Print and ignore.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
println!("Failed to setup signal handlers: {_e}");
|
println!("Failed to setup signal handlers: {_e}");
|
||||||
@ -2048,7 +2038,7 @@ where
|
|||||||
/// This function returns the [`ShMemDescription`] the client uses to place incoming messages.
|
/// This function returns the [`ShMemDescription`] the client uses to place incoming messages.
|
||||||
/// The thread exits, when the remote broker disconnects.
|
/// The thread exits, when the remote broker disconnects.
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[allow(clippy::let_and_return)]
|
#[allow(clippy::let_and_return, clippy::too_many_lines)]
|
||||||
fn b2b_thread_on(
|
fn b2b_thread_on(
|
||||||
mut stream: TcpStream,
|
mut stream: TcpStream,
|
||||||
b2b_client_id: ClientId,
|
b2b_client_id: ClientId,
|
||||||
@ -2096,36 +2086,46 @@ where
|
|||||||
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
||||||
println!("B2B: Starting proxy loop :)");
|
println!("B2B: Starting proxy loop :)");
|
||||||
|
|
||||||
|
let peer_address = stream.peer_addr().unwrap();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
// first, forward all data we have.
|
// first, forward all data we have.
|
||||||
while let Some((client_id, tag, flags, payload)) = local_receiver
|
loop {
|
||||||
.recv_buf_with_flags()
|
match local_receiver.recv_buf_with_flags() {
|
||||||
.expect("Error reading from local page!")
|
Ok(None) => break, // no more data to forward
|
||||||
{
|
Ok(Some((client_id, tag, flags, payload))) => {
|
||||||
if client_id == b2b_client_id {
|
if client_id == b2b_client_id {
|
||||||
println!(
|
println!(
|
||||||
"Ignored message we probably sent earlier (same id), TAG: {:x}",
|
"Ignored message we probably sent earlier (same id), TAG: {tag:x}"
|
||||||
tag
|
);
|
||||||
);
|
continue;
|
||||||
continue;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
||||||
println!(
|
println!(
|
||||||
"Fowarding message ({} bytes) via broker2broker connection",
|
"Fowarding message ({} bytes) via broker2broker connection",
|
||||||
payload.len()
|
payload.len()
|
||||||
);
|
);
|
||||||
// We got a new message! Forward...
|
// We got a new message! Forward...
|
||||||
send_tcp_msg(
|
if let Err(e) = send_tcp_msg(
|
||||||
&mut stream,
|
&mut stream,
|
||||||
&TcpRemoteNewMessage {
|
&TcpRemoteNewMessage {
|
||||||
client_id,
|
client_id,
|
||||||
tag,
|
tag,
|
||||||
flags,
|
flags,
|
||||||
payload: payload.to_vec(),
|
payload: payload.to_vec(),
|
||||||
},
|
},
|
||||||
)
|
) {
|
||||||
.expect("Error sending message via broker 2 broker");
|
println!("Got error {e} while trying to forward a message to broker {peer_address}, exiting thread");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Err(Error::ShuttingDown) => {
|
||||||
|
println!("Local broker is shutting down, exiting thread");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Err(e) => panic!("Error reading from local page! {e}"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Then, see if we can receive something.
|
// Then, see if we can receive something.
|
||||||
@ -2134,25 +2134,41 @@ where
|
|||||||
// Forwarding happens between each recv, too, as simplification.
|
// Forwarding happens between each recv, too, as simplification.
|
||||||
// We ignore errors completely as they may be timeout, or stream closings.
|
// We ignore errors completely as they may be timeout, or stream closings.
|
||||||
// Instead, we catch stream close when/if we next try to send.
|
// Instead, we catch stream close when/if we next try to send.
|
||||||
if let Ok(val) = recv_tcp_msg(&mut stream) {
|
match recv_tcp_msg(&mut stream) {
|
||||||
let msg: TcpRemoteNewMessage = val.try_into().expect(
|
Ok(val) => {
|
||||||
"Illegal message received from broker 2 broker connection - shutting down.",
|
let msg: TcpRemoteNewMessage = val.try_into().expect(
|
||||||
);
|
"Illegal message received from broker 2 broker connection - shutting down.",
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
||||||
println!(
|
println!(
|
||||||
"Fowarding incoming message ({} bytes) from broker2broker connection",
|
"Fowarding incoming message ({} bytes) from broker2broker connection",
|
||||||
msg.payload.len()
|
msg.payload.len()
|
||||||
);
|
);
|
||||||
|
|
||||||
// TODO: Could probably optimize this somehow to forward all queued messages between locks... oh well.
|
// TODO: Could probably optimize this somehow to forward all queued messages between locks... oh well.
|
||||||
// Todo: somehow mangle in the other broker id? ClientId?
|
// Todo: somehow mangle in the other broker id? ClientId?
|
||||||
new_sender
|
new_sender
|
||||||
.send_buf_with_flags(msg.tag, msg.flags | LLMP_FLAG_FROM_B2B, &msg.payload)
|
.send_buf_with_flags(
|
||||||
.expect("B2B: Error forwarding message. Exiting.");
|
msg.tag,
|
||||||
} else {
|
msg.flags | LLMP_FLAG_FROM_B2B,
|
||||||
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
&msg.payload,
|
||||||
println!("Received no input, timeout or closed. Looping back up :)");
|
)
|
||||||
|
.expect("B2B: Error forwarding message. Exiting.");
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
if let Error::File(e, _) = e {
|
||||||
|
if e.kind() == ErrorKind::UnexpectedEof {
|
||||||
|
println!(
|
||||||
|
"Broker {peer_address} seems to have disconnected, exiting"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "llmp_debug", feature = "std"))]
|
||||||
|
println!("Received no input, timeout or closed. Looping back up :)");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -2389,8 +2405,7 @@ where
|
|||||||
println!("Error adding client! Ignoring: {e:?}");
|
println!("Error adding client! Ignoring: {e:?}");
|
||||||
#[cfg(not(feature = "std"))]
|
#[cfg(not(feature = "std"))]
|
||||||
return Err(Error::unknown(format!(
|
return Err(Error::unknown(format!(
|
||||||
"Error adding client! PANIC! {:?}",
|
"Error adding client! PANIC! {e:?}"
|
||||||
e
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -391,8 +391,7 @@ pub unsafe fn setup_signal_handler<T: 'static + Handler>(handler: &mut T) -> Res
|
|||||||
// Rust always panics on OOM, so we will, too.
|
// Rust always panics on OOM, so we will, too.
|
||||||
assert!(
|
assert!(
|
||||||
!SIGNAL_STACK_PTR.is_null(),
|
!SIGNAL_STACK_PTR.is_null(),
|
||||||
"Failed to allocate signal stack with {} bytes!",
|
"Failed to allocate signal stack with {SIGNAL_STACK_SIZE} bytes!"
|
||||||
SIGNAL_STACK_SIZE
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let mut ss: stack_t = mem::zeroed();
|
let mut ss: stack_t = mem::zeroed();
|
||||||
|
@ -23,9 +23,14 @@ pub use windows::Win32::{
|
|||||||
|
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
|
|
||||||
//const EXCEPTION_CONTINUE_EXECUTION: c_long = -1;
|
// For VEH
|
||||||
|
const EXCEPTION_CONTINUE_EXECUTION: c_long = -1;
|
||||||
|
|
||||||
|
// For VEH
|
||||||
//const EXCEPTION_CONTINUE_SEARCH: c_long = 0;
|
//const EXCEPTION_CONTINUE_SEARCH: c_long = 0;
|
||||||
const EXCEPTION_EXECUTE_HANDLER: c_long = 1;
|
|
||||||
|
// For SEH
|
||||||
|
//const EXCEPTION_EXECUTE_HANDLER: c_long = 1;
|
||||||
|
|
||||||
// From https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/crt/signal.h
|
// From https://github.com/Alexpux/mingw-w64/blob/master/mingw-w64-headers/crt/signal.h
|
||||||
pub const SIGINT: i32 = 2;
|
pub const SIGINT: i32 = 2;
|
||||||
@ -314,9 +319,9 @@ unsafe fn internal_handle_exception(
|
|||||||
Some(handler_holder) => {
|
Some(handler_holder) => {
|
||||||
let handler = &mut **handler_holder.handler.get();
|
let handler = &mut **handler_holder.handler.get();
|
||||||
handler.handle(exception_code, exception_pointers);
|
handler.handle(exception_code, exception_pointers);
|
||||||
EXCEPTION_EXECUTE_HANDLER
|
EXCEPTION_CONTINUE_EXECUTION
|
||||||
}
|
}
|
||||||
None => EXCEPTION_EXECUTE_HANDLER,
|
None => EXCEPTION_CONTINUE_EXECUTION,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,7 +381,7 @@ pub unsafe fn setup_exception_handler<T: 'static + Handler>(handler: &mut T) ->
|
|||||||
// SetUnhandledFilter does not work with frida since the stack is changed and exception handler is lost with Stalker enabled.
|
// SetUnhandledFilter does not work with frida since the stack is changed and exception handler is lost with Stalker enabled.
|
||||||
// See https://github.com/AFLplusplus/LibAFL/pull/403
|
// See https://github.com/AFLplusplus/LibAFL/pull/403
|
||||||
AddVectoredExceptionHandler(
|
AddVectoredExceptionHandler(
|
||||||
1,
|
0,
|
||||||
Some(core::mem::transmute(handle_exception as *const c_void)),
|
Some(core::mem::transmute(handle_exception as *const c_void)),
|
||||||
);
|
);
|
||||||
Ok(())
|
Ok(())
|
||||||
|
@ -468,8 +468,7 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::unknown(format!(
|
Err(Error::unknown(format!(
|
||||||
"Wrong result read from pipe! Expected 0, got {:?}",
|
"Wrong result read from pipe! Expected 0, got {ret:?}"
|
||||||
ret
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -630,8 +629,7 @@ pub mod unix_shmem {
|
|||||||
if shm_fd == -1 {
|
if shm_fd == -1 {
|
||||||
perror(b"shm_open\0".as_ptr() as *const _);
|
perror(b"shm_open\0".as_ptr() as *const _);
|
||||||
return Err(Error::unknown(format!(
|
return Err(Error::unknown(format!(
|
||||||
"Failed to shm_open map with id {:?}",
|
"Failed to shm_open map with id {shmem_ctr:?}"
|
||||||
shmem_ctr
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -640,8 +638,7 @@ pub mod unix_shmem {
|
|||||||
perror(b"ftruncate\0".as_ptr() as *const _);
|
perror(b"ftruncate\0".as_ptr() as *const _);
|
||||||
shm_unlink(filename_path.as_ptr() as *const _);
|
shm_unlink(filename_path.as_ptr() as *const _);
|
||||||
return Err(Error::unknown(format!(
|
return Err(Error::unknown(format!(
|
||||||
"setup_shm(): ftruncate() failed for map with id {:?}",
|
"setup_shm(): ftruncate() failed for map with id {shmem_ctr:?}"
|
||||||
shmem_ctr
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -659,8 +656,7 @@ pub mod unix_shmem {
|
|||||||
close(shm_fd);
|
close(shm_fd);
|
||||||
shm_unlink(filename_path.as_ptr() as *const _);
|
shm_unlink(filename_path.as_ptr() as *const _);
|
||||||
return Err(Error::unknown(format!(
|
return Err(Error::unknown(format!(
|
||||||
"mmap() failed for map with id {:?}",
|
"mmap() failed for map with id {shmem_ctr:?}"
|
||||||
shmem_ctr
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,8 +687,7 @@ pub mod unix_shmem {
|
|||||||
perror(b"mmap\0".as_ptr() as *const _);
|
perror(b"mmap\0".as_ptr() as *const _);
|
||||||
close(shm_fd);
|
close(shm_fd);
|
||||||
return Err(Error::unknown(format!(
|
return Err(Error::unknown(format!(
|
||||||
"mmap() failed for map with fd {:?}",
|
"mmap() failed for map with fd {shm_fd:?}"
|
||||||
shm_fd
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -997,8 +992,7 @@ pub mod unix_shmem {
|
|||||||
let fd = open(device_path.as_ptr(), O_RDWR);
|
let fd = open(device_path.as_ptr(), O_RDWR);
|
||||||
if fd == -1 {
|
if fd == -1 {
|
||||||
return Err(Error::unknown(format!(
|
return Err(Error::unknown(format!(
|
||||||
"Failed to open the ashmem device at {:?}",
|
"Failed to open the ashmem device at {device_path:?}"
|
||||||
device_path
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +31,11 @@ use crate::bolts::{
|
|||||||
};
|
};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use crate::bolts::{llmp::LlmpConnection, shmem::StdShMemProvider, staterestore::StateRestorer};
|
use crate::bolts::{llmp::LlmpConnection, shmem::StdShMemProvider, staterestore::StateRestorer};
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
use crate::{
|
||||||
|
bolts::os::unix_signals::setup_signal_handler,
|
||||||
|
events::{shutdown_handler, SHUTDOWN_SIGHANDLER_DATA},
|
||||||
|
};
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::{
|
bolts::{
|
||||||
llmp::{self, Flags, LlmpClient, LlmpClientDescription, Tag},
|
llmp::{self, Flags, LlmpClient, LlmpClientDescription, Tag},
|
||||||
@ -393,10 +398,7 @@ where
|
|||||||
executions: _,
|
executions: _,
|
||||||
} => {
|
} => {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
println!(
|
println!("Received new Testcase from {_client_id} ({client_config:?})");
|
||||||
"Received new Testcase from {} ({:?})",
|
|
||||||
_client_id, client_config
|
|
||||||
);
|
|
||||||
|
|
||||||
let _res = if client_config.match_with(&self.configuration)
|
let _res = if client_config.match_with(&self.configuration)
|
||||||
&& observers_buf.is_some()
|
&& observers_buf.is_some()
|
||||||
@ -885,17 +887,42 @@ where
|
|||||||
mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL);
|
mgr.to_env(_ENV_FUZZER_BROKER_CLIENT_INITIAL);
|
||||||
|
|
||||||
// First, create a channel from the current fuzzer to the next to store state between restarts.
|
// First, create a channel from the current fuzzer to the next to store state between restarts.
|
||||||
|
#[cfg(unix)]
|
||||||
|
let mut staterestorer: StateRestorer<SP> =
|
||||||
|
StateRestorer::new(self.shmem_provider.new_shmem(256 * 1024 * 1024)?);
|
||||||
|
|
||||||
|
#[cfg(not(unix))]
|
||||||
let staterestorer: StateRestorer<SP> =
|
let staterestorer: StateRestorer<SP> =
|
||||||
StateRestorer::new(self.shmem_provider.new_shmem(256 * 1024 * 1024)?);
|
StateRestorer::new(self.shmem_provider.new_shmem(256 * 1024 * 1024)?);
|
||||||
// Store the information to a map.
|
// Store the information to a map.
|
||||||
staterestorer.write_to_env(_ENV_FUZZER_SENDER)?;
|
staterestorer.write_to_env(_ENV_FUZZER_SENDER)?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
unsafe {
|
||||||
|
let data = &mut SHUTDOWN_SIGHANDLER_DATA;
|
||||||
|
// Write the pointer to staterestorer so we can release its shmem later
|
||||||
|
core::ptr::write_volatile(
|
||||||
|
&mut data.staterestorer_ptr,
|
||||||
|
&mut staterestorer as *mut _ as *mut std::ffi::c_void,
|
||||||
|
);
|
||||||
|
data.allocator_pid = std::process::id() as usize;
|
||||||
|
data.shutdown_handler = shutdown_handler::<SP> as *const std::ffi::c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We setup signal handlers to clean up shmem segments used by state restorer
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Err(_e) = unsafe { setup_signal_handler(&mut SHUTDOWN_SIGHANDLER_DATA) } {
|
||||||
|
// We can live without a proper ctrl+c signal handler. Print and ignore.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!("Failed to setup signal handlers: {_e}");
|
||||||
|
}
|
||||||
|
|
||||||
let mut ctr: u64 = 0;
|
let mut ctr: u64 = 0;
|
||||||
// Client->parent loop
|
// Client->parent loop
|
||||||
loop {
|
loop {
|
||||||
println!("Spawning next client (id {ctr})");
|
println!("Spawning next client (id {ctr})");
|
||||||
|
|
||||||
// On Unix, we fork
|
// On Unix, we fork (when fork feature is enabled)
|
||||||
#[cfg(all(unix, feature = "fork"))]
|
#[cfg(all(unix, feature = "fork"))]
|
||||||
let child_status = {
|
let child_status = {
|
||||||
self.shmem_provider.pre_fork()?;
|
self.shmem_provider.pre_fork()?;
|
||||||
|
@ -8,6 +8,8 @@ use alloc::{
|
|||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
use core::ffi::c_void;
|
||||||
use core::{fmt, hash::Hasher, marker::PhantomData, time::Duration};
|
use core::{fmt, hash::Hasher, marker::PhantomData, time::Duration};
|
||||||
|
|
||||||
use ahash::AHasher;
|
use ahash::AHasher;
|
||||||
@ -16,6 +18,10 @@ use serde::{Deserialize, Serialize};
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
use crate::bolts::os::unix_signals::{siginfo_t, ucontext_t, Handler, Signal};
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
use crate::bolts::{shmem::ShMemProvider, staterestore::StateRestorer};
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::current_time,
|
bolts::current_time,
|
||||||
executors::ExitKind,
|
executors::ExitKind,
|
||||||
@ -27,6 +33,80 @@ use crate::{
|
|||||||
Error,
|
Error,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Check if ctrl-c is sent with this struct
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
pub static mut SHUTDOWN_SIGHANDLER_DATA: ShutdownSignalData = ShutdownSignalData {
|
||||||
|
allocator_pid: 0,
|
||||||
|
staterestorer_ptr: core::ptr::null_mut(),
|
||||||
|
shutdown_handler: core::ptr::null(),
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A signal handler for releasing `StateRestore` `ShMem`
|
||||||
|
/// This struct holds a pointer to `StateRestore` and clean up the `ShMem` segment used by it.
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ShutdownSignalData {
|
||||||
|
allocator_pid: usize,
|
||||||
|
staterestorer_ptr: *mut c_void,
|
||||||
|
shutdown_handler: *const c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Type for shutdown handler
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
pub type ShutdownFuncPtr =
|
||||||
|
unsafe fn(Signal, siginfo_t, &mut ucontext_t, data: &mut ShutdownSignalData);
|
||||||
|
|
||||||
|
/// Shutdown handler. `SigTerm`, `SigInterrupt`, `SigQuit` call this
|
||||||
|
/// We can't handle SIGKILL in the signal handler, this means that you shouldn't kill your fuzzer with `kill -9` because then the shmem segments are never freed
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This will acceess `data` and write to the global `data.staterestorer_ptr` if it's not null.
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
pub unsafe fn shutdown_handler<SP>(
|
||||||
|
signal: Signal,
|
||||||
|
_info: siginfo_t,
|
||||||
|
_context: &mut ucontext_t,
|
||||||
|
data: &mut ShutdownSignalData,
|
||||||
|
) where
|
||||||
|
SP: ShMemProvider,
|
||||||
|
{
|
||||||
|
println!(
|
||||||
|
"Fuzzer shutdown by Signal: {} Pid: {}",
|
||||||
|
signal,
|
||||||
|
std::process::id()
|
||||||
|
);
|
||||||
|
|
||||||
|
let ptr = data.staterestorer_ptr;
|
||||||
|
if ptr.is_null() || data.allocator_pid != std::process::id() as usize {
|
||||||
|
// Do nothing
|
||||||
|
} else {
|
||||||
|
// The process allocated the staterestorer map must take care of it
|
||||||
|
let sr = (ptr as *mut StateRestorer<SP>).as_mut().unwrap();
|
||||||
|
// println!("{:#?}", sr);
|
||||||
|
std::ptr::drop_in_place(sr);
|
||||||
|
}
|
||||||
|
println!("Bye!");
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
impl Handler for ShutdownSignalData {
|
||||||
|
fn handle(&mut self, signal: Signal, info: siginfo_t, context: &mut ucontext_t) {
|
||||||
|
unsafe {
|
||||||
|
let data = &mut SHUTDOWN_SIGHANDLER_DATA;
|
||||||
|
if !data.shutdown_handler.is_null() {
|
||||||
|
let func: ShutdownFuncPtr = std::mem::transmute(data.shutdown_handler);
|
||||||
|
(func)(signal, info, context, data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signals(&self) -> Vec<Signal> {
|
||||||
|
vec![Signal::SigTerm, Signal::SigInterrupt, Signal::SigQuit]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A per-fuzzer unique `ID`, usually starting with `0` and increasing
|
/// A per-fuzzer unique `ID`, usually starting with `0` and increasing
|
||||||
/// by `1` in multiprocessed `EventManager`s, such as [`self::llmp::LlmpEventManager`].
|
/// by `1` in multiprocessed `EventManager`s, such as [`self::llmp::LlmpEventManager`].
|
||||||
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
|
||||||
|
@ -5,6 +5,10 @@ use alloc::{
|
|||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
use core::ffi::c_void;
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
use core::ptr::write_volatile;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use core::sync::atomic::{compiler_fence, Ordering};
|
use core::sync::atomic::{compiler_fence, Ordering};
|
||||||
use core::{fmt::Debug, marker::PhantomData};
|
use core::{fmt::Debug, marker::PhantomData};
|
||||||
@ -17,6 +21,11 @@ use super::{CustomBufEventResult, CustomBufHandlerFn, HasCustomBufHandlers, Prog
|
|||||||
use crate::bolts::os::startable_self;
|
use crate::bolts::os::startable_self;
|
||||||
#[cfg(all(feature = "std", feature = "fork", unix))]
|
#[cfg(all(feature = "std", feature = "fork", unix))]
|
||||||
use crate::bolts::os::{fork, ForkResult};
|
use crate::bolts::os::{fork, ForkResult};
|
||||||
|
#[cfg(all(unix, feature = "std"))]
|
||||||
|
use crate::{
|
||||||
|
bolts::os::unix_signals::setup_signal_handler,
|
||||||
|
events::{shutdown_handler, SHUTDOWN_SIGHANDLER_DATA},
|
||||||
|
};
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::{shmem::ShMemProvider, staterestore::StateRestorer},
|
bolts::{shmem::ShMemProvider, staterestore::StateRestorer},
|
||||||
@ -283,8 +292,7 @@ where
|
|||||||
Ok(())
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
Err(Error::unknown(format!(
|
Err(Error::unknown(format!(
|
||||||
"Received illegal message that message should not have arrived: {:?}.",
|
"Received illegal message that message should not have arrived: {event:?}."
|
||||||
event
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -436,11 +444,36 @@ where
|
|||||||
// We start ourself as child process to actually fuzz
|
// We start ourself as child process to actually fuzz
|
||||||
let mut staterestorer = if std::env::var(_ENV_FUZZER_SENDER).is_err() {
|
let mut staterestorer = if std::env::var(_ENV_FUZZER_SENDER).is_err() {
|
||||||
// First, create a place to store state in, for restarts.
|
// First, create a place to store state in, for restarts.
|
||||||
|
#[cfg(unix)]
|
||||||
|
let mut staterestorer: StateRestorer<SP> =
|
||||||
|
StateRestorer::new(shmem_provider.new_shmem(256 * 1024 * 1024)?);
|
||||||
|
#[cfg(not(unix))]
|
||||||
let staterestorer: StateRestorer<SP> =
|
let staterestorer: StateRestorer<SP> =
|
||||||
StateRestorer::new(shmem_provider.new_shmem(256 * 1024 * 1024)?);
|
StateRestorer::new(shmem_provider.new_shmem(256 * 1024 * 1024)?);
|
||||||
|
|
||||||
//let staterestorer = { LlmpSender::new(shmem_provider.clone(), 0, false)? };
|
//let staterestorer = { LlmpSender::new(shmem_provider.clone(), 0, false)? };
|
||||||
staterestorer.write_to_env(_ENV_FUZZER_SENDER)?;
|
staterestorer.write_to_env(_ENV_FUZZER_SENDER)?;
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
unsafe {
|
||||||
|
let data = &mut SHUTDOWN_SIGHANDLER_DATA;
|
||||||
|
// Write the pointer to staterestorer so we can release its shmem later
|
||||||
|
write_volatile(
|
||||||
|
&mut data.staterestorer_ptr,
|
||||||
|
&mut staterestorer as *mut _ as *mut c_void,
|
||||||
|
);
|
||||||
|
data.allocator_pid = std::process::id() as usize;
|
||||||
|
data.shutdown_handler = shutdown_handler::<SP> as *const c_void;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We setup signal handlers to clean up shmem segments used by state restorer
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Err(_e) = unsafe { setup_signal_handler(&mut SHUTDOWN_SIGHANDLER_DATA) } {
|
||||||
|
// We can live without a proper ctrl+c signal handler. Print and ignore.
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
println!("Failed to setup signal handlers: {_e}");
|
||||||
|
}
|
||||||
|
|
||||||
let mut ctr: u64 = 0;
|
let mut ctr: u64 = 0;
|
||||||
// Client->parent loop
|
// Client->parent loop
|
||||||
loop {
|
loop {
|
||||||
|
@ -152,7 +152,7 @@ impl CommandConfigurator for StdCommandConfigurator {
|
|||||||
/// Construct a `CommandExecutor` by implementing [`CommandConfigurator`] for a type of your choice and calling [`CommandConfigurator::into_executor`] on it.
|
/// Construct a `CommandExecutor` by implementing [`CommandConfigurator`] for a type of your choice and calling [`CommandConfigurator::into_executor`] on it.
|
||||||
/// Instead, you can use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`].
|
/// Instead, you can use [`CommandExecutor::builder()`] to construct a [`CommandExecutor`] backed by a [`StdCommandConfigurator`].
|
||||||
pub struct CommandExecutor<EM, OT, S, T, Z> {
|
pub struct CommandExecutor<EM, OT, S, T, Z> {
|
||||||
/// The wrapped comand configurer
|
/// The wrapped command configurer
|
||||||
configurer: T,
|
configurer: T,
|
||||||
/// The observers used by this executor
|
/// The observers used by this executor
|
||||||
observers: OT,
|
observers: OT,
|
||||||
@ -526,7 +526,7 @@ impl CommandExecutorBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Builds the `ComandExecutor`
|
/// Builds the `CommandExecutor`
|
||||||
pub fn build<EM, OT, S, Z>(
|
pub fn build<EM, OT, S, Z>(
|
||||||
&self,
|
&self,
|
||||||
observers: OT,
|
observers: OT,
|
||||||
@ -537,7 +537,7 @@ impl CommandExecutorBuilder {
|
|||||||
{
|
{
|
||||||
let Some(program) = &self.program else {
|
let Some(program) = &self.program else {
|
||||||
return Err(Error::illegal_argument(
|
return Err(Error::illegal_argument(
|
||||||
"ComandExecutor::builder: no program set!",
|
"CommandExecutor::builder: no program set!",
|
||||||
));
|
));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -249,8 +249,7 @@ impl Forkserver {
|
|||||||
Ok(_) => (),
|
Ok(_) => (),
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
return Err(Error::illegal_state(format!(
|
return Err(Error::illegal_state(format!(
|
||||||
"Could not spawn the forkserver: {:#?}",
|
"Could not spawn the forkserver: {err:#?}"
|
||||||
err
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -188,6 +188,7 @@ where
|
|||||||
{
|
{
|
||||||
let handlers = InProcessHandlers::new::<Self, EM, OF, Z, H>()?;
|
let handlers = InProcessHandlers::new::<Self, EM, OF, Z, H>()?;
|
||||||
#[cfg(windows)]
|
#[cfg(windows)]
|
||||||
|
// Some initialization necessary for windows.
|
||||||
unsafe {
|
unsafe {
|
||||||
/*
|
/*
|
||||||
See https://github.com/AFLplusplus/LibAFL/pull/403
|
See https://github.com/AFLplusplus/LibAFL/pull/403
|
||||||
@ -844,8 +845,7 @@ mod unix_signal_handler {
|
|||||||
let si_addr = { _info.si_addr() as usize };
|
let si_addr = { _info.si_addr() as usize };
|
||||||
|
|
||||||
eprintln!(
|
eprintln!(
|
||||||
"We crashed at addr 0x{:x}, but are not in the target... Bug in the fuzzer? Exiting.",
|
"We crashed at addr 0x{si_addr:x}, but are not in the target... Bug in the fuzzer? Exiting."
|
||||||
si_addr
|
|
||||||
);
|
);
|
||||||
|
|
||||||
#[cfg(all(feature = "std", unix))]
|
#[cfg(all(feature = "std", unix))]
|
||||||
@ -873,6 +873,149 @@ mod unix_signal_handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Same as `inproc_crash_handler`, but this is called when address sanitizer exits, not from the exception handler
|
||||||
|
#[cfg(all(windows, feature = "std"))]
|
||||||
|
pub mod windows_asan_handler {
|
||||||
|
use alloc::string::String;
|
||||||
|
use core::{
|
||||||
|
ptr,
|
||||||
|
sync::atomic::{compiler_fence, Ordering},
|
||||||
|
};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::io::{stdout, Write};
|
||||||
|
|
||||||
|
use windows::Win32::System::Threading::{
|
||||||
|
EnterCriticalSection, LeaveCriticalSection, RTL_CRITICAL_SECTION,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
corpus::{Corpus, Testcase},
|
||||||
|
events::{Event, EventFirer, EventRestarter},
|
||||||
|
executors::{inprocess::GLOBAL_STATE, Executor, ExitKind, HasObservers},
|
||||||
|
feedbacks::Feedback,
|
||||||
|
fuzzer::HasObjective,
|
||||||
|
inputs::UsesInput,
|
||||||
|
observers::ObserversTuple,
|
||||||
|
state::{HasClientPerfMonitor, HasMetadata, HasSolutions},
|
||||||
|
};
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// ASAN deatch handler
|
||||||
|
pub unsafe extern "C" fn asan_death_handler<E, EM, OF, Z>()
|
||||||
|
where
|
||||||
|
E: Executor<EM, Z> + HasObservers,
|
||||||
|
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
|
||||||
|
OF: Feedback<E::State>,
|
||||||
|
E::State: HasSolutions + HasClientPerfMonitor,
|
||||||
|
Z: HasObjective<Objective = OF, State = E::State>,
|
||||||
|
{
|
||||||
|
let mut data = &mut GLOBAL_STATE;
|
||||||
|
// Have we set a timer_before?
|
||||||
|
if !(data.tp_timer as *mut windows::Win32::System::Threading::TP_TIMER).is_null() {
|
||||||
|
/*
|
||||||
|
We want to prevent the timeout handler being run while the main thread is executing the crash handler
|
||||||
|
Timeout handler runs if it has access to the critical section or data.in_target == 0
|
||||||
|
Writing 0 to the data.in_target makes the timeout handler makes the timeout handler invalid.
|
||||||
|
*/
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
EnterCriticalSection(data.critical as *mut RTL_CRITICAL_SECTION);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
data.in_target = 0;
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
LeaveCriticalSection(data.critical as *mut RTL_CRITICAL_SECTION);
|
||||||
|
compiler_fence(Ordering::SeqCst);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
eprintln!("ASAN detected crash!");
|
||||||
|
if data.current_input_ptr.is_null() {
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
{
|
||||||
|
eprintln!("Double crash\n");
|
||||||
|
eprintln!(
|
||||||
|
"ASAN detected crash but we're not in the target... Bug in the fuzzer? Exiting.",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
{
|
||||||
|
eprintln!("Type QUIT to restart the child");
|
||||||
|
let mut line = String::new();
|
||||||
|
while line.trim() != "QUIT" {
|
||||||
|
std::io::stdin().read_line(&mut line).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO tell the parent to not restart
|
||||||
|
} else {
|
||||||
|
let executor = data.executor_mut::<E>();
|
||||||
|
// reset timer
|
||||||
|
if !data.tp_timer.is_null() {
|
||||||
|
executor.post_run_reset();
|
||||||
|
data.tp_timer = ptr::null_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
let state = data.state_mut::<E::State>();
|
||||||
|
let fuzzer = data.fuzzer_mut::<Z>();
|
||||||
|
let event_mgr = data.event_mgr_mut::<EM>();
|
||||||
|
let observers = executor.observers_mut();
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
eprintln!("Child crashed!");
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
drop(stdout().flush());
|
||||||
|
|
||||||
|
// Make sure we don't crash in the crash handler forever.
|
||||||
|
let input = data.take_current_input::<<E::State as UsesInput>::Input>();
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
eprintln!("Child crashed!");
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
drop(stdout().flush());
|
||||||
|
|
||||||
|
observers
|
||||||
|
.post_exec_all(state, input, &ExitKind::Crash)
|
||||||
|
.expect("Observers post_exec_all failed");
|
||||||
|
|
||||||
|
let interesting = fuzzer
|
||||||
|
.objective_mut()
|
||||||
|
.is_interesting(state, event_mgr, input, observers, &ExitKind::Crash)
|
||||||
|
.expect("In crash handler objective failure.");
|
||||||
|
|
||||||
|
if interesting {
|
||||||
|
let new_input = input.clone();
|
||||||
|
let mut new_testcase = Testcase::new(new_input);
|
||||||
|
new_testcase.add_metadata(ExitKind::Crash);
|
||||||
|
fuzzer
|
||||||
|
.objective_mut()
|
||||||
|
.append_metadata(state, &mut new_testcase)
|
||||||
|
.expect("Failed adding metadata");
|
||||||
|
state
|
||||||
|
.solutions_mut()
|
||||||
|
.add(new_testcase)
|
||||||
|
.expect("In crash handler solutions failure.");
|
||||||
|
event_mgr
|
||||||
|
.fire(
|
||||||
|
state,
|
||||||
|
Event::Objective {
|
||||||
|
objective_size: state.solutions().count(),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
.expect("Could not send crashing input");
|
||||||
|
}
|
||||||
|
|
||||||
|
event_mgr.on_restart(state).unwrap();
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
eprintln!("Waiting for broker...");
|
||||||
|
event_mgr.await_restart_safe();
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
eprintln!("Bye!");
|
||||||
|
}
|
||||||
|
// Don't need to exit, Asan will exit for us
|
||||||
|
// ExitProcess(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(all(windows, feature = "std"))]
|
#[cfg(all(windows, feature = "std"))]
|
||||||
mod windows_exception_handler {
|
mod windows_exception_handler {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -890,7 +1033,9 @@ mod windows_exception_handler {
|
|||||||
panic,
|
panic,
|
||||||
};
|
};
|
||||||
|
|
||||||
use windows::Win32::System::Threading::ExitProcess;
|
use windows::Win32::System::Threading::{
|
||||||
|
EnterCriticalSection, ExitProcess, LeaveCriticalSection, RTL_CRITICAL_SECTION,
|
||||||
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::os::windows_exceptions::{
|
bolts::os::windows_exceptions::{
|
||||||
@ -904,6 +1049,7 @@ mod windows_exception_handler {
|
|||||||
},
|
},
|
||||||
feedbacks::Feedback,
|
feedbacks::Feedback,
|
||||||
fuzzer::HasObjective,
|
fuzzer::HasObjective,
|
||||||
|
inputs::UsesInput,
|
||||||
observers::ObserversTuple,
|
observers::ObserversTuple,
|
||||||
state::{HasClientPerfMonitor, HasMetadata, HasSolutions},
|
state::{HasClientPerfMonitor, HasMetadata, HasSolutions},
|
||||||
};
|
};
|
||||||
@ -935,12 +1081,6 @@ mod windows_exception_handler {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use windows::Win32::System::Threading::{
|
|
||||||
EnterCriticalSection, LeaveCriticalSection, RTL_CRITICAL_SECTION,
|
|
||||||
};
|
|
||||||
|
|
||||||
use crate::inputs::UsesInput;
|
|
||||||
|
|
||||||
/// invokes the `post_exec` hook on all observer in case of panic
|
/// invokes the `post_exec` hook on all observer in case of panic
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub fn setup_panic_hook<E, EM, OF, Z>()
|
pub fn setup_panic_hook<E, EM, OF, Z>()
|
||||||
@ -1030,6 +1170,7 @@ mod windows_exception_handler {
|
|||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Timeout handler for windows
|
||||||
pub unsafe extern "system" fn inproc_timeout_handler<E, EM, OF, Z>(
|
pub unsafe extern "system" fn inproc_timeout_handler<E, EM, OF, Z>(
|
||||||
_p0: *mut u8,
|
_p0: *mut u8,
|
||||||
global_state: *mut c_void,
|
global_state: *mut c_void,
|
||||||
@ -1153,20 +1294,22 @@ mod windows_exception_handler {
|
|||||||
compiler_fence(Ordering::SeqCst);
|
compiler_fence(Ordering::SeqCst);
|
||||||
}
|
}
|
||||||
|
|
||||||
let code = ExceptionCode::try_from(
|
|
||||||
exception_pointers
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.ExceptionRecord
|
|
||||||
.as_mut()
|
|
||||||
.unwrap()
|
|
||||||
.ExceptionCode
|
|
||||||
.0,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
eprintln!("Crashed with {}", code);
|
if let Some(exception_pointers) = exception_pointers.as_mut() {
|
||||||
|
let code = ExceptionCode::try_from(
|
||||||
|
exception_pointers
|
||||||
|
.ExceptionRecord
|
||||||
|
.as_mut()
|
||||||
|
.unwrap()
|
||||||
|
.ExceptionCode
|
||||||
|
.0,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
eprintln!("Crashed with {}", code);
|
||||||
|
} else {
|
||||||
|
eprintln!("Crashed without exception (probably due to SIGABRT)");
|
||||||
|
};
|
||||||
|
|
||||||
if data.current_input_ptr.is_null() {
|
if data.current_input_ptr.is_null() {
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
{
|
{
|
||||||
|
@ -75,8 +75,7 @@ where
|
|||||||
let o2_name = o2.name().to_string();
|
let o2_name = o2.name().to_string();
|
||||||
if o1_name == o2_name {
|
if o1_name == o2_name {
|
||||||
Err(Error::illegal_argument(format!(
|
Err(Error::illegal_argument(format!(
|
||||||
"DiffFeedback: observer names must be different (both were {})",
|
"DiffFeedback: observer names must be different (both were {o1_name})"
|
||||||
o1_name
|
|
||||||
)))
|
)))
|
||||||
} else {
|
} else {
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
|
@ -963,6 +963,7 @@ where
|
|||||||
T: Debug + Serialize + serde::de::DeserializeOwned,
|
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||||
{
|
{
|
||||||
name: String,
|
name: String,
|
||||||
|
last_addr: usize,
|
||||||
phantom: PhantomData<T>,
|
phantom: PhantomData<T>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1012,6 +1013,7 @@ where
|
|||||||
pub fn new(name: &'static str) -> Self {
|
pub fn new(name: &'static str) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
|
last_addr: 0,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1021,6 +1023,7 @@ where
|
|||||||
pub fn new_with_observer(observer: &ListObserver<T>) -> Self {
|
pub fn new_with_observer(observer: &ListObserver<T>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
name: observer.name().to_string(),
|
name: observer.name().to_string(),
|
||||||
|
last_addr: 0,
|
||||||
phantom: PhantomData,
|
phantom: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -229,6 +229,82 @@ where
|
|||||||
|
|
||||||
Ok(ret)
|
Ok(ret)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fuzz for n iterations.
|
||||||
|
/// Returns the index of the last fuzzed corpus item.
|
||||||
|
/// (Note: An iteration represents a complete run of every stage.
|
||||||
|
/// therefore the number n is not always equal to the number of the actual harness executions,
|
||||||
|
/// because each stage could run the harness for multiple times)
|
||||||
|
///
|
||||||
|
/// If you use this fn in a restarting scenario to only run for `n` iterations,
|
||||||
|
/// before exiting, make sure you call `event_mgr.on_restart(&mut state)?;`.
|
||||||
|
/// This way, the state will be available in the next, respawned, iteration.
|
||||||
|
fn fuzz_loop_for_duration(
|
||||||
|
&mut self,
|
||||||
|
stages: &mut ST,
|
||||||
|
executor: &mut E,
|
||||||
|
state: &mut EM::State,
|
||||||
|
manager: &mut EM,
|
||||||
|
time: Duration
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
if time==Duration::ZERO {
|
||||||
|
return Err(Error::illegal_argument(
|
||||||
|
"Cannot fuzz for 0 duration!".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut ret = 0;
|
||||||
|
let mut last = current_time();
|
||||||
|
let monitor_timeout = STATS_TIMEOUT_DEFAULT;
|
||||||
|
|
||||||
|
let starttime = std::time::Instant::now();
|
||||||
|
|
||||||
|
while std::time::Instant::now().duration_since(starttime) < time {
|
||||||
|
ret = self.fuzz_one(stages, executor, state, manager)?;
|
||||||
|
last = manager.maybe_report_progress(state, last, monitor_timeout)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we would assume the fuzzer loop will always exit after this, we could do this here:
|
||||||
|
// manager.on_restart(state)?;
|
||||||
|
// But as the state may grow to a few megabytes,
|
||||||
|
// for now we won' and the user has to do it (unless we find a way to do this on `Drop`).
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Fuzz for n iterations.
|
||||||
|
/// Returns the index of the last fuzzed corpus item.
|
||||||
|
/// (Note: An iteration represents a complete run of every stage.
|
||||||
|
/// therefore the number n is not always equal to the number of the actual harness executions,
|
||||||
|
/// because each stage could run the harness for multiple times)
|
||||||
|
///
|
||||||
|
/// If you use this fn in a restarting scenario to only run for `n` iterations,
|
||||||
|
/// before exiting, make sure you call `event_mgr.on_restart(&mut state)?;`.
|
||||||
|
/// This way, the state will be available in the next, respawned, iteration.
|
||||||
|
fn fuzz_loop_until(
|
||||||
|
&mut self,
|
||||||
|
stages: &mut ST,
|
||||||
|
executor: &mut E,
|
||||||
|
state: &mut EM::State,
|
||||||
|
manager: &mut EM,
|
||||||
|
time: std::time::Instant
|
||||||
|
) -> Result<usize, Error> {
|
||||||
|
let mut ret = 0;
|
||||||
|
let mut last = current_time();
|
||||||
|
let monitor_timeout = STATS_TIMEOUT_DEFAULT;
|
||||||
|
|
||||||
|
while std::time::Instant::now() < time {
|
||||||
|
ret = self.fuzz_one(stages, executor, state, manager)?;
|
||||||
|
last = manager.maybe_report_progress(state, last, monitor_timeout)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we would assume the fuzzer loop will always exit after this, we could do this here:
|
||||||
|
// manager.on_restart(state)?;
|
||||||
|
// But as the state may grow to a few megabytes,
|
||||||
|
// for now we won' and the user has to do it (unless we find a way to do this on `Drop`).
|
||||||
|
|
||||||
|
Ok(ret)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The corpus this input should be added to
|
/// The corpus this input should be added to
|
||||||
|
@ -832,8 +832,7 @@ impl core::fmt::Display for ClientPerfMonitor {
|
|||||||
// Create the formatted string
|
// Create the formatted string
|
||||||
writeln!(
|
writeln!(
|
||||||
f,
|
f,
|
||||||
" {:6.4}: Scheduler\n {:6.4}: Manager",
|
" {scheduler_percent:6.4}: Scheduler\n {manager_percent:6.4}: Manager"
|
||||||
scheduler_percent, manager_percent
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
// Calculate each stage
|
// Calculate each stage
|
||||||
|
@ -113,8 +113,7 @@ impl Tokens {
|
|||||||
}
|
}
|
||||||
if token_stop < token_start {
|
if token_stop < token_start {
|
||||||
return Err(Error::illegal_argument(format!(
|
return Err(Error::illegal_argument(format!(
|
||||||
"Tried to create tokens from illegal section: stop < start ({:?} < {:?})",
|
"Tried to create tokens from illegal section: stop < start ({token_stop:?} < {token_start:?})"
|
||||||
token_stop, token_start
|
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
let section_size: usize = token_stop.offset_from(token_start).try_into().unwrap();
|
let section_size: usize = token_stop.offset_from(token_start).try_into().unwrap();
|
||||||
@ -185,8 +184,7 @@ impl Tokens {
|
|||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
return Err(Error::illegal_argument(format!(
|
return Err(Error::illegal_argument(format!(
|
||||||
"Illegal line (hex decoding): {}",
|
"Illegal line (hex decoding): {line}"
|
||||||
line
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -108,14 +108,6 @@ pub trait MapObserver: HasLen + Named + Serialize + serde::de::DeserializeOwned
|
|||||||
/// Get the initial value for reset()
|
/// Get the initial value for reset()
|
||||||
fn initial(&self) -> Self::Entry;
|
fn initial(&self) -> Self::Entry;
|
||||||
|
|
||||||
/// Get the initial value for reset() (mutable)
|
|
||||||
fn initial_mut(&mut self) -> &mut Self::Entry;
|
|
||||||
|
|
||||||
/// Set the initial value for reset()
|
|
||||||
fn set_initial(&mut self, initial: Self::Entry) {
|
|
||||||
*self.initial_mut() = initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset the map
|
/// Reset the map
|
||||||
fn reset_map(&mut self) -> Result<(), Error>;
|
fn reset_map(&mut self) -> Result<(), Error>;
|
||||||
|
|
||||||
@ -389,11 +381,6 @@ where
|
|||||||
self.initial
|
self.initial
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn initial_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.initial
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_vec(&self) -> Vec<T> {
|
fn to_vec(&self) -> Vec<T> {
|
||||||
self.as_slice().to_vec()
|
self.as_slice().to_vec()
|
||||||
}
|
}
|
||||||
@ -776,11 +763,6 @@ where
|
|||||||
self.initial
|
self.initial
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn initial_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.initial
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn get(&self, idx: usize) -> &T {
|
fn get(&self, idx: usize) -> &T {
|
||||||
&self.as_slice()[idx]
|
&self.as_slice()[idx]
|
||||||
@ -1050,11 +1032,6 @@ where
|
|||||||
self.initial
|
self.initial
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn initial_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.initial
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn usable_count(&self) -> usize {
|
fn usable_count(&self) -> usize {
|
||||||
*self.size.as_ref()
|
*self.size.as_ref()
|
||||||
@ -1261,11 +1238,6 @@ where
|
|||||||
self.base.initial()
|
self.base.initial()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn initial_mut(&mut self) -> &mut u8 {
|
|
||||||
self.base.initial_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn usable_count(&self) -> usize {
|
fn usable_count(&self) -> usize {
|
||||||
self.base.usable_count()
|
self.base.usable_count()
|
||||||
@ -1489,11 +1461,6 @@ where
|
|||||||
self.base.initial()
|
self.base.initial()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn initial_mut(&mut self) -> &mut u8 {
|
|
||||||
self.base.initial_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn usable_count(&self) -> usize {
|
fn usable_count(&self) -> usize {
|
||||||
self.base.usable_count()
|
self.base.usable_count()
|
||||||
@ -1734,11 +1701,6 @@ where
|
|||||||
self.initial
|
self.initial
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn initial_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.initial
|
|
||||||
}
|
|
||||||
|
|
||||||
fn count_bytes(&self) -> u64 {
|
fn count_bytes(&self) -> u64 {
|
||||||
let initial = self.initial();
|
let initial = self.initial();
|
||||||
let mut res = 0;
|
let mut res = 0;
|
||||||
@ -2090,16 +2052,6 @@ where
|
|||||||
self.initial
|
self.initial
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn initial_mut(&mut self) -> &mut T {
|
|
||||||
&mut self.initial
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn set_initial(&mut self, initial: T) {
|
|
||||||
self.initial = initial;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Reset the map
|
/// Reset the map
|
||||||
#[inline]
|
#[inline]
|
||||||
fn reset_map(&mut self) -> Result<(), Error> {
|
fn reset_map(&mut self) -> Result<(), Error> {
|
||||||
@ -2478,17 +2430,6 @@ pub mod pybind {
|
|||||||
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.initial() })
|
mapob_unwrap_me!($wrapper_name, self.wrapper, m, { m.initial() })
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn initial_mut(&mut self) -> &mut $datatype {
|
|
||||||
let ptr = mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.initial_mut() as *mut $datatype });
|
|
||||||
unsafe { ptr.as_mut().unwrap() }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
|
||||||
fn set_initial(&mut self, initial: $datatype) {
|
|
||||||
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.set_initial(initial) });
|
|
||||||
}
|
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn reset_map(&mut self) -> Result<(), Error> {
|
fn reset_map(&mut self) -> Result<(), Error> {
|
||||||
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.reset_map() })
|
mapob_unwrap_me_mut!($wrapper_name, self.wrapper, m, { m.reset_map() })
|
||||||
|
@ -18,12 +18,12 @@ pub use stacktrace::*;
|
|||||||
|
|
||||||
pub mod concolic;
|
pub mod concolic;
|
||||||
|
|
||||||
|
pub mod value;
|
||||||
// Rust is breaking this with 'error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `type_id`' and so we disable this component for the moment
|
// Rust is breaking this with 'error: intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `type_id`' and so we disable this component for the moment
|
||||||
//#[cfg(unstable_feature)]
|
//#[cfg(unstable_feature)]
|
||||||
//pub mod owned;
|
//pub mod owned;
|
||||||
//#[cfg(unstable_feature)]
|
//#[cfg(unstable_feature)]
|
||||||
//pub use owned::*;
|
//pub use owned::*;
|
||||||
|
|
||||||
use alloc::{
|
use alloc::{
|
||||||
string::{String, ToString},
|
string::{String, ToString},
|
||||||
vec::Vec,
|
vec::Vec,
|
||||||
@ -31,6 +31,7 @@ use alloc::{
|
|||||||
use core::{fmt::Debug, time::Duration};
|
use core::{fmt::Debug, time::Duration};
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
pub use value::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
bolts::{
|
bolts::{
|
||||||
|
90
libafl/src/observers/value.rs
Normal file
90
libafl/src/observers/value.rs
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
//! A simple observer with a single value.
|
||||||
|
|
||||||
|
use alloc::{
|
||||||
|
boxed::Box,
|
||||||
|
string::{String, ToString},
|
||||||
|
};
|
||||||
|
use core::fmt::Debug;
|
||||||
|
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::Observer;
|
||||||
|
use crate::{
|
||||||
|
bolts::{ownedref::OwnedRef, tuples::Named},
|
||||||
|
inputs::UsesInput,
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// A simple observer with a single value.
|
||||||
|
///
|
||||||
|
/// The intent is that the value is something with interior mutability (e.g., a
|
||||||
|
/// `RefCell`), which the target could write to even though this observer has a
|
||||||
|
/// reference to it.
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
#[serde(bound = "T: serde::de::DeserializeOwned")]
|
||||||
|
pub struct ValueObserver<'a, T>
|
||||||
|
where
|
||||||
|
T: Debug + Serialize,
|
||||||
|
{
|
||||||
|
/// The name of this observer.
|
||||||
|
name: String,
|
||||||
|
/// The value.
|
||||||
|
pub value: OwnedRef<'a, T>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> ValueObserver<'a, T>
|
||||||
|
where
|
||||||
|
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
/// Creates a new [`ValueObserver`] with the given name.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(name: &'static str, value: &'a T) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
value: OwnedRef::Ref(value),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a reference to the underlying value.
|
||||||
|
#[must_use]
|
||||||
|
pub fn get_ref(&self) -> &T {
|
||||||
|
self.value.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the value.
|
||||||
|
pub fn set(&mut self, new_value: T) {
|
||||||
|
self.value = OwnedRef::Owned(Box::new(new_value));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Clone or move the current value out of this object.
|
||||||
|
#[must_use]
|
||||||
|
pub fn take(self) -> T
|
||||||
|
where
|
||||||
|
T: Clone,
|
||||||
|
{
|
||||||
|
match self.value {
|
||||||
|
OwnedRef::Ref(r) => r.clone(),
|
||||||
|
OwnedRef::Owned(v) => *v,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This *does not* reset the value inside the observer.
|
||||||
|
impl<'a, S, T> Observer<S> for ValueObserver<'a, T>
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, T> Named for ValueObserver<'a, T>
|
||||||
|
where
|
||||||
|
T: Debug + Serialize + serde::de::DeserializeOwned,
|
||||||
|
{
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
@ -210,8 +210,7 @@ where
|
|||||||
let must_remove = {
|
let must_remove = {
|
||||||
let old_meta = old.metadata_mut().get_mut::<AccountingIndexesMetadata>().ok_or_else(|| {
|
let old_meta = old.metadata_mut().get_mut::<AccountingIndexesMetadata>().ok_or_else(|| {
|
||||||
Error::key_not_found(format!(
|
Error::key_not_found(format!(
|
||||||
"AccountingIndexesMetadata, needed by CoverageAccountingScheduler, not found in testcase #{}",
|
"AccountingIndexesMetadata, needed by CoverageAccountingScheduler, not found in testcase #{old_idx}"
|
||||||
old_idx
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
*old_meta.refcnt_mut() -= 1;
|
*old_meta.refcnt_mut() -= 1;
|
||||||
|
@ -209,8 +209,7 @@ where
|
|||||||
let factor = F::compute(&mut *entry, state)?;
|
let factor = F::compute(&mut *entry, state)?;
|
||||||
let meta = entry.metadata_mut().get_mut::<M>().ok_or_else(|| {
|
let meta = entry.metadata_mut().get_mut::<M>().ok_or_else(|| {
|
||||||
Error::key_not_found(format!(
|
Error::key_not_found(format!(
|
||||||
"Metadata needed for MinimizerScheduler not found in testcase #{}",
|
"Metadata needed for MinimizerScheduler not found in testcase #{idx}"
|
||||||
idx
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
for elem in meta.as_slice() {
|
for elem in meta.as_slice() {
|
||||||
@ -229,8 +228,7 @@ where
|
|||||||
let must_remove = {
|
let must_remove = {
|
||||||
let old_meta = old.metadata_mut().get_mut::<M>().ok_or_else(|| {
|
let old_meta = old.metadata_mut().get_mut::<M>().ok_or_else(|| {
|
||||||
Error::key_not_found(format!(
|
Error::key_not_found(format!(
|
||||||
"Metadata needed for MinimizerScheduler not found in testcase #{}",
|
"Metadata needed for MinimizerScheduler not found in testcase #{old_idx}"
|
||||||
old_idx
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
*old_meta.refcnt_mut() -= 1;
|
*old_meta.refcnt_mut() -= 1;
|
||||||
@ -283,8 +281,7 @@ where
|
|||||||
let mut entry = state.corpus().get(*idx)?.borrow_mut();
|
let mut entry = state.corpus().get(*idx)?.borrow_mut();
|
||||||
let meta = entry.metadata().get::<M>().ok_or_else(|| {
|
let meta = entry.metadata().get::<M>().ok_or_else(|| {
|
||||||
Error::key_not_found(format!(
|
Error::key_not_found(format!(
|
||||||
"Metadata needed for MinimizerScheduler not found in testcase #{}",
|
"Metadata needed for MinimizerScheduler not found in testcase #{idx}"
|
||||||
idx
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
for elem in meta.as_slice() {
|
for elem in meta.as_slice() {
|
||||||
|
@ -133,7 +133,7 @@ impl SchedulerMetadata {
|
|||||||
/// The power schedule to use
|
/// The power schedule to use
|
||||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
||||||
pub enum PowerSchedule {
|
pub enum PowerSchedule {
|
||||||
/// The `explore" power schedule
|
/// The `explore` power schedule
|
||||||
EXPLORE,
|
EXPLORE,
|
||||||
/// The `exploit` power schedule
|
/// The `exploit` power schedule
|
||||||
EXPLOIT,
|
EXPLOIT,
|
||||||
|
@ -127,8 +127,7 @@ where
|
|||||||
let original = input.clone();
|
let original = input.clone();
|
||||||
let meta = entry.metadata().get::<MapNoveltiesMetadata>().ok_or_else(|| {
|
let meta = entry.metadata().get::<MapNoveltiesMetadata>().ok_or_else(|| {
|
||||||
Error::key_not_found(format!(
|
Error::key_not_found(format!(
|
||||||
"MapNoveltiesMetadata needed for GeneralizationStage not found in testcase #{} (check the arguments of MapFeedback::new(...))",
|
"MapNoveltiesMetadata needed for GeneralizationStage not found in testcase #{corpus_idx} (check the arguments of MapFeedback::new(...))"
|
||||||
corpus_idx
|
|
||||||
))
|
))
|
||||||
})?;
|
})?;
|
||||||
(payload, original, meta.as_slice().to_vec())
|
(payload, original, meta.as_slice().to_vec())
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
//! While normal stages call the executor over and over again, push stages turn this concept upside down:
|
//! While normal stages call the executor over and over again, push stages turn this concept upside down:
|
||||||
//! A push stage instead returns an iterator that generates a new result for each time it gets called.
|
//! A push stage instead returns an iterator that generates a new result for each time it gets called.
|
||||||
//! With the new testcase, you will have to take care about testcase execution, manually.
|
//! With the new testcase, you will have to take care about testcase execution, manually.
|
||||||
//! The push stage relies on internal muttability of the supplied `Observers`.
|
//! The push stage relies on internal mutability of the supplied `Observers`.
|
||||||
//!
|
//!
|
||||||
|
|
||||||
/// Mutational stage is the normal fuzzing stage.
|
/// Mutational stage is the normal fuzzing stage.
|
||||||
|
@ -46,8 +46,7 @@ fn find_llvm_config_brew() -> Result<PathBuf, String> {
|
|||||||
match glob_results.last() {
|
match glob_results.last() {
|
||||||
Some(path) => Ok(path.unwrap()),
|
Some(path) => Ok(path.unwrap()),
|
||||||
None => Err(format!(
|
None => Err(format!(
|
||||||
"No llvm-config found in brew cellar with pattern {}",
|
"No llvm-config found in brew cellar with pattern {cellar_glob}"
|
||||||
cellar_glob
|
|
||||||
)),
|
)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -182,8 +182,7 @@ fn write_symcc_runtime_bindings_file(out_path: &Path, cpp_bindings: &bindgen::Bi
|
|||||||
let function_name = &captures[1];
|
let function_name = &captures[1];
|
||||||
writeln!(
|
writeln!(
|
||||||
bindings_file,
|
bindings_file,
|
||||||
"#[link_name=\"{}{}\"]",
|
"#[link_name=\"{SYMCC_RUNTIME_FUNCTION_NAME_PREFIX}{function_name}\"]"
|
||||||
SYMCC_RUNTIME_FUNCTION_NAME_PREFIX, function_name
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
@ -440,8 +440,7 @@ impl Allocator {
|
|||||||
Some(metadata) => metadata.size,
|
Some(metadata) => metadata.size,
|
||||||
None => {
|
None => {
|
||||||
panic!(
|
panic!(
|
||||||
"Attempted to get_usable_size on a pointer ({:?}) which was not allocated!",
|
"Attempted to get_usable_size on a pointer ({ptr:?}) which was not allocated!"
|
||||||
ptr
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -103,6 +103,13 @@ pub const ASAN_SAVE_REGISTER_NAMES: [&str; ASAN_SAVE_REGISTER_COUNT] = [
|
|||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
pub const ASAN_SAVE_REGISTER_COUNT: usize = 32;
|
pub const ASAN_SAVE_REGISTER_COUNT: usize = 32;
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
const ASAN_EH_FRAME_DWORD_COUNT: usize = 14;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
const ASAN_EH_FRAME_FDE_OFFSET: u32 = 20;
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
const ASAN_EH_FRAME_FDE_ADDRESS_OFFSET: u32 = 28;
|
||||||
|
|
||||||
/// The frida address sanitizer runtime, providing address sanitization.
|
/// The frida address sanitizer runtime, providing address sanitization.
|
||||||
/// When executing in `ASAN`, each memory access will get checked, using frida stalker under the hood.
|
/// When executing in `ASAN`, each memory access will get checked, using frida stalker under the hood.
|
||||||
/// The runtime can report memory errors that occurred during execution,
|
/// The runtime can report memory errors that occurred during execution,
|
||||||
@ -131,6 +138,9 @@ pub struct AsanRuntime {
|
|||||||
module_map: Option<ModuleMap>,
|
module_map: Option<ModuleMap>,
|
||||||
suppressed_addresses: Vec<usize>,
|
suppressed_addresses: Vec<usize>,
|
||||||
shadow_check_func: Option<extern "C" fn(*const c_void, usize) -> bool>,
|
shadow_check_func: Option<extern "C" fn(*const c_void, usize) -> bool>,
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
eh_frame: [u32; ASAN_EH_FRAME_DWORD_COUNT],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for AsanRuntime {
|
impl Debug for AsanRuntime {
|
||||||
@ -304,6 +314,9 @@ impl AsanRuntime {
|
|||||||
module_map: None,
|
module_map: None,
|
||||||
suppressed_addresses: Vec::new(),
|
suppressed_addresses: Vec::new(),
|
||||||
shadow_check_func: None,
|
shadow_check_func: None,
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
eh_frame: [0; ASAN_EH_FRAME_DWORD_COUNT],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -386,8 +399,7 @@ impl AsanRuntime {
|
|||||||
self.allocator
|
self.allocator
|
||||||
.map_shadow_for_region(tls_start, tls_end, true);
|
.map_shadow_for_region(tls_start, tls_end, true);
|
||||||
println!(
|
println!(
|
||||||
"registering thread with stack {:x}:{:x} and tls {:x}:{:x}",
|
"registering thread with stack {stack_start:x}:{stack_end:x} and tls {tls_start:x}:{tls_end:x}"
|
||||||
stack_start, stack_end, tls_start, tls_end
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1534,7 +1546,8 @@ impl AsanRuntime {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
#[allow(clippy::unused_self, clippy::identity_op)] // identity_op appears to be a false positive in ubfx
|
// identity_op appears to be a false positive in ubfx
|
||||||
|
#[allow(clippy::unused_self, clippy::identity_op, clippy::too_many_lines)]
|
||||||
fn generate_shadow_check_function(&mut self) {
|
fn generate_shadow_check_function(&mut self) {
|
||||||
let shadow_bit = self.allocator.shadow_bit();
|
let shadow_bit = self.allocator.shadow_bit();
|
||||||
let mut ops = dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
|
let mut ops = dynasmrt::VecAssembler::<dynasmrt::aarch64::Aarch64Relocation>::new(0);
|
||||||
@ -1641,18 +1654,34 @@ impl AsanRuntime {
|
|||||||
);
|
);
|
||||||
|
|
||||||
let blob = ops.finalize().unwrap();
|
let blob = ops.finalize().unwrap();
|
||||||
|
let mut map_flags = MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE;
|
||||||
|
|
||||||
|
// apple aarch64 requires MAP_JIT to allocates WX pages
|
||||||
|
if cfg!(all(target_vendor = "apple", target_arch = "aarch64")) {
|
||||||
|
map_flags |= MapFlags::MAP_JIT;
|
||||||
|
}
|
||||||
|
|
||||||
unsafe {
|
unsafe {
|
||||||
let mapping = mmap(
|
let mapping = mmap(
|
||||||
std::ptr::null_mut(),
|
std::ptr::null_mut(),
|
||||||
0x1000,
|
0x1000,
|
||||||
ProtFlags::all(),
|
ProtFlags::all(),
|
||||||
MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE,
|
map_flags,
|
||||||
-1,
|
-1,
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
|
// on apple aarch64, WX pages can't be both writable and executable at the same time.
|
||||||
|
// pthread_jit_write_protect_np flips them from executable (1) to writable (0)
|
||||||
|
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
|
||||||
|
libc::pthread_jit_write_protect_np(0);
|
||||||
|
|
||||||
blob.as_ptr()
|
blob.as_ptr()
|
||||||
.copy_to_nonoverlapping(mapping as *mut u8, blob.len());
|
.copy_to_nonoverlapping(mapping as *mut u8, blob.len());
|
||||||
|
|
||||||
|
#[cfg(all(target_vendor = "apple", target_arch = "aarch64"))]
|
||||||
|
libc::pthread_jit_write_protect_np(1);
|
||||||
self.shadow_check_func = Some(std::mem::transmute(mapping as *mut u8));
|
self.shadow_check_func = Some(std::mem::transmute(mapping as *mut u8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1967,8 +1996,10 @@ impl AsanRuntime {
|
|||||||
; stp x0, x1, [x28]
|
; stp x0, x1, [x28]
|
||||||
|
|
||||||
; adr x25, <report
|
; adr x25, <report
|
||||||
; adr x0, >eh_frame_fde
|
; adr x15, >eh_frame_cie_addr
|
||||||
; adr x27, >fde_address
|
; ldr x15, [x15]
|
||||||
|
; add x0, x15, ASAN_EH_FRAME_FDE_OFFSET // eh_frame_fde
|
||||||
|
; add x27, x15, ASAN_EH_FRAME_FDE_ADDRESS_OFFSET // fde_address
|
||||||
; ldr w26, [x27]
|
; ldr w26, [x27]
|
||||||
; cmp w26, #0x0
|
; cmp w26, #0x0
|
||||||
; b.ne >skip_register
|
; b.ne >skip_register
|
||||||
@ -2013,29 +2044,25 @@ impl AsanRuntime {
|
|||||||
; .qword AsanRuntime::handle_trap as *mut c_void as i64
|
; .qword AsanRuntime::handle_trap as *mut c_void as i64
|
||||||
; register_frame_func:
|
; register_frame_func:
|
||||||
; .qword __register_frame as *mut c_void as i64
|
; .qword __register_frame as *mut c_void as i64
|
||||||
; eh_frame_cie:
|
; eh_frame_cie_addr:
|
||||||
; .dword 0x14
|
; .qword addr_of_mut!(self.eh_frame) as i64
|
||||||
; .dword 0x00
|
|
||||||
; .dword 0x00527a01
|
|
||||||
; .dword 0x011e7c01
|
|
||||||
; .dword 0x001f0c1b
|
|
||||||
; eh_frame_fde:
|
|
||||||
; .dword 0x14
|
|
||||||
; .dword 0x18
|
|
||||||
; fde_address:
|
|
||||||
; .dword 0x0 // <-- address offset goes here
|
|
||||||
; .dword 0x104
|
|
||||||
//advance_loc 12
|
|
||||||
//def_cfa r29 (x29) at offset 16
|
|
||||||
//offset r30 (x30) at cfa-8
|
|
||||||
//offset r29 (x29) at cfa-16
|
|
||||||
; .dword 0x1d0c4c00
|
|
||||||
; .dword 0x9d029e10u32 as i32
|
|
||||||
; .dword 0x04
|
|
||||||
// empty next FDE:
|
|
||||||
; .dword 0x0
|
|
||||||
; .dword 0x0
|
|
||||||
);
|
);
|
||||||
|
self.eh_frame = [
|
||||||
|
0x14, 0, 0x00527a01, 0x011e7c01, 0x001f0c1b, //
|
||||||
|
// eh_frame_fde
|
||||||
|
0x14, 0x18, //
|
||||||
|
// fde_address
|
||||||
|
0, // <-- address offset goes here
|
||||||
|
0x104,
|
||||||
|
// advance_loc 12
|
||||||
|
// def_cfa r29 (x29) at offset 16
|
||||||
|
// offset r30 (x30) at cfa-8
|
||||||
|
// offset r29 (x29) at cfa-16
|
||||||
|
0x1d0c4c00, 0x9d029e10, 0x4, //
|
||||||
|
// empty next FDE:
|
||||||
|
0, 0,
|
||||||
|
];
|
||||||
|
|
||||||
self.blob_report = Some(ops_report.finalize().unwrap().into_boxed_slice());
|
self.blob_report = Some(ops_report.finalize().unwrap().into_boxed_slice());
|
||||||
|
|
||||||
self.blob_check_mem_byte = Some(self.generate_shadow_check_blob(0));
|
self.blob_check_mem_byte = Some(self.generate_shadow_check_blob(0));
|
||||||
|
@ -312,8 +312,7 @@ impl AsanErrors {
|
|||||||
| AsanError::BadFuncArgWrite((name, _pc, address, size, backtrace)) => {
|
| AsanError::BadFuncArgWrite((name, _pc, address, size, backtrace)) => {
|
||||||
writeln!(
|
writeln!(
|
||||||
output,
|
output,
|
||||||
" in call to {}, argument {:#016x}, size: {:#x}",
|
" in call to {name}, argument {address:#016x}, size: {size:#x}"
|
||||||
name, address, size
|
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
output.reset().unwrap();
|
output.reset().unwrap();
|
||||||
@ -432,12 +431,7 @@ impl AsanErrors {
|
|||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
} else {
|
} else {
|
||||||
writeln!(
|
writeln!(output, " at 0x{pc:x}, faulting address 0x{fault_address:x}").unwrap();
|
||||||
output,
|
|
||||||
" at 0x{:x}, faulting address 0x{:x}",
|
|
||||||
pc, fault_address
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
}
|
}
|
||||||
output.reset().unwrap();
|
output.reset().unwrap();
|
||||||
|
|
||||||
|
@ -1,5 +1,12 @@
|
|||||||
//! Functionality regarding binary-only coverage collection.
|
//! Functionality regarding binary-only coverage collection.
|
||||||
use core::ptr::addr_of_mut;
|
use core::ptr::addr_of_mut;
|
||||||
|
use std::{
|
||||||
|
cell::{Ref, RefCell},
|
||||||
|
marker::PhantomPinned,
|
||||||
|
ops::Deref,
|
||||||
|
pin::Pin,
|
||||||
|
rc::Rc,
|
||||||
|
};
|
||||||
|
|
||||||
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
use dynasmrt::{dynasm, DynasmApi, DynasmLabelApi};
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
@ -15,15 +22,19 @@ use crate::helper::FridaRuntime;
|
|||||||
/// (Default) map size for frida coverage reporting
|
/// (Default) map size for frida coverage reporting
|
||||||
pub const MAP_SIZE: usize = 64 * 1024;
|
pub const MAP_SIZE: usize = 64 * 1024;
|
||||||
|
|
||||||
/// Frida binary-only coverage
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct CoverageRuntime {
|
struct CoverageRuntimeInner {
|
||||||
map: [u8; MAP_SIZE],
|
map: [u8; MAP_SIZE],
|
||||||
previous_pc: u64,
|
previous_pc: u64,
|
||||||
current_log_impl: u64,
|
current_log_impl: u64,
|
||||||
blob_maybe_log: Option<Box<[u8]>>,
|
blob_maybe_log: Option<Box<[u8]>>,
|
||||||
|
_pinned: PhantomPinned,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Frida binary-only coverage
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct CoverageRuntime(Pin<Rc<RefCell<CoverageRuntimeInner>>>);
|
||||||
|
|
||||||
impl Default for CoverageRuntime {
|
impl Default for CoverageRuntime {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::new()
|
Self::new()
|
||||||
@ -32,6 +43,7 @@ impl Default for CoverageRuntime {
|
|||||||
|
|
||||||
impl FridaRuntime for CoverageRuntime {
|
impl FridaRuntime for CoverageRuntime {
|
||||||
/// Initialize the coverage runtime
|
/// Initialize the coverage runtime
|
||||||
|
/// The struct MUST NOT be moved after this function is called, as the generated assembly references it
|
||||||
fn init(
|
fn init(
|
||||||
&mut self,
|
&mut self,
|
||||||
_gum: &frida_gum::Gum,
|
_gum: &frida_gum::Gum,
|
||||||
@ -60,23 +72,24 @@ impl CoverageRuntime {
|
|||||||
/// Create a new coverage runtime
|
/// Create a new coverage runtime
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self(Rc::pin(RefCell::new(CoverageRuntimeInner {
|
||||||
map: [0_u8; MAP_SIZE],
|
map: [0_u8; MAP_SIZE],
|
||||||
previous_pc: 0,
|
previous_pc: 0,
|
||||||
current_log_impl: 0,
|
current_log_impl: 0,
|
||||||
blob_maybe_log: None,
|
blob_maybe_log: None,
|
||||||
}
|
_pinned: PhantomPinned,
|
||||||
|
})))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the coverage map pointer
|
/// Retrieve the coverage map pointer
|
||||||
pub fn map_ptr_mut(&mut self) -> *mut u8 {
|
pub fn map_ptr_mut(&mut self) -> *mut u8 {
|
||||||
self.map.as_mut_ptr()
|
self.0.borrow_mut().map.as_mut_ptr()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Retrieve the `maybe_log` code blob, that will write coverage into the map
|
/// Retrieve the `maybe_log` code blob, that will write coverage into the map
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn blob_maybe_log(&self) -> &[u8] {
|
pub fn blob_maybe_log(&self) -> impl Deref<Target = Box<[u8]>> + '_ {
|
||||||
self.blob_maybe_log.as_ref().unwrap()
|
Ref::map(self.0.borrow(), |s| s.blob_maybe_log.as_ref().unwrap())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A minimal `maybe_log` implementation. We insert this into the transformed instruction stream
|
/// A minimal `maybe_log` implementation. We insert this into the transformed instruction stream
|
||||||
@ -104,12 +117,13 @@ impl CoverageRuntime {
|
|||||||
; ldp x1, x2, [sp], #0x10
|
; ldp x1, x2, [sp], #0x10
|
||||||
; ret
|
; ret
|
||||||
;map_addr:
|
;map_addr:
|
||||||
;.qword addr_of_mut!(self.map) as i64
|
;.qword addr_of_mut!(self.0.borrow_mut().map) as i64
|
||||||
;previous_loc:
|
;previous_loc:
|
||||||
;.qword 0
|
;.qword 0
|
||||||
);
|
);
|
||||||
let ops_vec = ops.finalize().unwrap();
|
let ops_vec = ops.finalize().unwrap();
|
||||||
self.blob_maybe_log = Some(ops_vec[..ops_vec.len() - 8].to_vec().into_boxed_slice());
|
self.0.borrow_mut().blob_maybe_log =
|
||||||
|
Some(ops_vec[..ops_vec.len() - 8].to_vec().into_boxed_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A minimal `maybe_log` implementation. We insert this into the transformed instruction stream
|
/// A minimal `maybe_log` implementation. We insert this into the transformed instruction stream
|
||||||
@ -140,12 +154,13 @@ impl CoverageRuntime {
|
|||||||
; popfq
|
; popfq
|
||||||
; ret
|
; ret
|
||||||
;map_addr:
|
;map_addr:
|
||||||
;.qword addr_of_mut!(self.map) as i64
|
;.qword addr_of_mut!(self.0.borrow_mut().map) as i64
|
||||||
;previous_loc:
|
;previous_loc:
|
||||||
;.qword 0
|
;.qword 0
|
||||||
);
|
);
|
||||||
let ops_vec = ops.finalize().unwrap();
|
let ops_vec = ops.finalize().unwrap();
|
||||||
self.blob_maybe_log = Some(ops_vec[..ops_vec.len() - 8].to_vec().into_boxed_slice());
|
self.0.borrow_mut().blob_maybe_log =
|
||||||
|
Some(ops_vec[..ops_vec.len() - 8].to_vec().into_boxed_slice());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emits coverage mapping into the current basic block.
|
/// Emits coverage mapping into the current basic block.
|
||||||
@ -156,9 +171,10 @@ impl CoverageRuntime {
|
|||||||
let writer = output.writer();
|
let writer = output.writer();
|
||||||
#[allow(clippy::cast_possible_wrap)] // gum redzone size is u32, we need an offset as i32.
|
#[allow(clippy::cast_possible_wrap)] // gum redzone size is u32, we need an offset as i32.
|
||||||
let redzone_size = i64::from(frida_gum_sys::GUM_RED_ZONE_SIZE);
|
let redzone_size = i64::from(frida_gum_sys::GUM_RED_ZONE_SIZE);
|
||||||
if self.current_log_impl == 0
|
if self.0.borrow().current_log_impl == 0
|
||||||
|| !writer.can_branch_directly_to(self.current_log_impl)
|
|| !writer.can_branch_directly_to(self.0.borrow().current_log_impl)
|
||||||
|| !writer.can_branch_directly_between(writer.pc() + 128, self.current_log_impl)
|
|| !writer
|
||||||
|
.can_branch_directly_between(writer.pc() + 128, self.0.borrow().current_log_impl)
|
||||||
{
|
{
|
||||||
let after_log_impl = writer.code_offset() + 1;
|
let after_log_impl = writer.code_offset() + 1;
|
||||||
|
|
||||||
@ -167,9 +183,9 @@ impl CoverageRuntime {
|
|||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
writer.put_b_label(after_log_impl);
|
writer.put_b_label(after_log_impl);
|
||||||
|
|
||||||
self.current_log_impl = writer.pc();
|
self.0.borrow_mut().current_log_impl = writer.pc();
|
||||||
writer.put_bytes(self.blob_maybe_log());
|
writer.put_bytes(&self.blob_maybe_log());
|
||||||
let prev_loc_pointer = addr_of_mut!(self.previous_pc) as u64; // Get the pointer to self.previous_pc
|
let prev_loc_pointer = addr_of_mut!(self.0.borrow_mut().previous_pc) as u64; // Get the pointer to self.previous_pc
|
||||||
|
|
||||||
writer.put_bytes(&prev_loc_pointer.to_ne_bytes());
|
writer.put_bytes(&prev_loc_pointer.to_ne_bytes());
|
||||||
|
|
||||||
@ -180,7 +196,7 @@ impl CoverageRuntime {
|
|||||||
writer.put_lea_reg_reg_offset(X86Register::Rsp, X86Register::Rsp, -(redzone_size));
|
writer.put_lea_reg_reg_offset(X86Register::Rsp, X86Register::Rsp, -(redzone_size));
|
||||||
writer.put_push_reg(X86Register::Rdi);
|
writer.put_push_reg(X86Register::Rdi);
|
||||||
writer.put_mov_reg_address(X86Register::Rdi, h64 & (MAP_SIZE as u64 - 1));
|
writer.put_mov_reg_address(X86Register::Rdi, h64 & (MAP_SIZE as u64 - 1));
|
||||||
writer.put_call_address(self.current_log_impl);
|
writer.put_call_address(self.0.borrow().current_log_impl);
|
||||||
writer.put_pop_reg(X86Register::Rdi);
|
writer.put_pop_reg(X86Register::Rdi);
|
||||||
writer.put_lea_reg_reg_offset(X86Register::Rsp, X86Register::Rsp, redzone_size);
|
writer.put_lea_reg_reg_offset(X86Register::Rsp, X86Register::Rsp, redzone_size);
|
||||||
}
|
}
|
||||||
@ -195,7 +211,7 @@ impl CoverageRuntime {
|
|||||||
);
|
);
|
||||||
writer.put_ldr_reg_u64(Aarch64Register::X0, h64 & (MAP_SIZE as u64 - 1));
|
writer.put_ldr_reg_u64(Aarch64Register::X0, h64 & (MAP_SIZE as u64 - 1));
|
||||||
|
|
||||||
writer.put_bl_imm(self.current_log_impl);
|
writer.put_bl_imm(self.0.borrow().current_log_impl);
|
||||||
writer.put_ldp_reg_reg_reg_offset(
|
writer.put_ldp_reg_reg_reg_offset(
|
||||||
Aarch64Register::Lr,
|
Aarch64Register::Lr,
|
||||||
Aarch64Register::X0,
|
Aarch64Register::X0,
|
||||||
|
@ -31,6 +31,7 @@ where
|
|||||||
S::Input: HasTargetBytes,
|
S::Input: HasTargetBytes,
|
||||||
S: UsesInput,
|
S: UsesInput,
|
||||||
OT: ObserversTuple<S>,
|
OT: ObserversTuple<S>,
|
||||||
|
'a: 'b,
|
||||||
{
|
{
|
||||||
base: InProcessExecutor<'a, H, OT, S>,
|
base: InProcessExecutor<'a, H, OT, S>,
|
||||||
/// Frida's dynamic rewriting engine
|
/// Frida's dynamic rewriting engine
|
||||||
@ -38,6 +39,7 @@ where
|
|||||||
/// User provided callback for instrumentation
|
/// User provided callback for instrumentation
|
||||||
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
|
helper: &'c mut FridaInstrumentationHelper<'b, RT>,
|
||||||
followed: bool,
|
followed: bool,
|
||||||
|
gum: &'b Gum,
|
||||||
_phantom: PhantomData<&'b u8>,
|
_phantom: PhantomData<&'b u8>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,8 +85,8 @@ where
|
|||||||
self.stalker.activate(NativePointer(core::ptr::null_mut()));
|
self.stalker.activate(NativePointer(core::ptr::null_mut()));
|
||||||
} else {
|
} else {
|
||||||
self.followed = true;
|
self.followed = true;
|
||||||
self.stalker
|
let transformer = self.helper.transformer(self.gum);
|
||||||
.follow_me::<NoneEventSink>(self.helper.transformer(), None);
|
self.stalker.follow_me::<NoneEventSink>(&transformer, None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let res = self.base.run_target(fuzzer, state, mgr, input);
|
let res = self.base.run_target(fuzzer, state, mgr, input);
|
||||||
@ -185,6 +187,7 @@ where
|
|||||||
base,
|
base,
|
||||||
stalker,
|
stalker,
|
||||||
helper,
|
helper,
|
||||||
|
gum,
|
||||||
followed: false,
|
followed: false,
|
||||||
_phantom: PhantomData,
|
_phantom: PhantomData,
|
||||||
}
|
}
|
||||||
|
@ -115,8 +115,6 @@ where
|
|||||||
|
|
||||||
/// An helper that feeds `FridaInProcessExecutor` with edge-coverage instrumentation
|
/// An helper that feeds `FridaInProcessExecutor` with edge-coverage instrumentation
|
||||||
pub struct FridaInstrumentationHelper<'a, RT> {
|
pub struct FridaInstrumentationHelper<'a, RT> {
|
||||||
/// Transformer that has to be passed to FridaInProcessExecutor
|
|
||||||
transformer: Option<Transformer<'a>>,
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
capstone: Capstone,
|
capstone: Capstone,
|
||||||
ranges: RangeMap<usize, (u16, String)>,
|
ranges: RangeMap<usize, (u16, String)>,
|
||||||
@ -204,7 +202,6 @@ where
|
|||||||
modules_to_instrument.iter().map(AsRef::as_ref).collect();
|
modules_to_instrument.iter().map(AsRef::as_ref).collect();
|
||||||
|
|
||||||
let mut helper = Self {
|
let mut helper = Self {
|
||||||
transformer: None,
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
#[cfg(target_arch = "aarch64")]
|
||||||
capstone: Capstone::new()
|
capstone: Capstone::new()
|
||||||
.arm64()
|
.arm64()
|
||||||
@ -245,122 +242,12 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let transformer = Transformer::from_callback(gum, |basic_block, output| {
|
// make sure we aren't in the instrumented list, as it would cause recursions
|
||||||
let mut first = true;
|
assert!(
|
||||||
for instruction in basic_block {
|
!helper.ranges.contains_key(&(Self::new as usize)),
|
||||||
let instr = instruction.instr();
|
"instrumented libraries must not include the fuzzer"
|
||||||
#[cfg(unix)]
|
);
|
||||||
let instr_size = instr.bytes().len();
|
|
||||||
let address = instr.address();
|
|
||||||
//println!("block @ {:x} transformed to {:x}", address, output.writer().pc());
|
|
||||||
|
|
||||||
//println!(
|
|
||||||
//"address: {:x} contains: {:?}",
|
|
||||||
//address,
|
|
||||||
//helper.ranges.contains_key(&(address as usize))
|
|
||||||
//);
|
|
||||||
|
|
||||||
// println!("Ranges: {:#?}", helper.ranges);
|
|
||||||
if helper.ranges.contains_key(&(address as usize)) {
|
|
||||||
if first {
|
|
||||||
first = false;
|
|
||||||
//println!("block @ {:x} transformed to {:x}", address, output.writer().pc());
|
|
||||||
if let Some(rt) = helper.runtime_mut::<CoverageRuntime>() {
|
|
||||||
rt.emit_coverage_mapping(address, &output);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
if let Some(rt) = helper.runtime_mut::<DrCovRuntime>() {
|
|
||||||
instruction.put_callout(|context| {
|
|
||||||
let real_address = rt.real_address_for_stalked(pc(&context));
|
|
||||||
//let (range, (id, name)) = helper.ranges.get_key_value(&real_address).unwrap();
|
|
||||||
//println!("{}:0x{:016x}", name, real_address - range.start);
|
|
||||||
rt.drcov_basic_blocks.push(DrCovBasicBlock::new(
|
|
||||||
real_address,
|
|
||||||
real_address + instr_size,
|
|
||||||
));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
let res = if let Some(_rt) = helper.runtime::<AsanRuntime>() {
|
|
||||||
AsanRuntime::asan_is_interesting_instruction(
|
|
||||||
&helper.capstone,
|
|
||||||
address,
|
|
||||||
instr,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
#[cfg(all(target_arch = "x86_64", unix))]
|
|
||||||
if let Some((segment, width, basereg, indexreg, scale, disp)) = res {
|
|
||||||
if let Some(rt) = helper.runtime_mut::<AsanRuntime>() {
|
|
||||||
rt.emit_shadow_check(
|
|
||||||
address, &output, segment, width, basereg, indexreg, scale,
|
|
||||||
disp,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "aarch64")]
|
|
||||||
if let Some((basereg, indexreg, displacement, width, shift, extender)) = res
|
|
||||||
{
|
|
||||||
if let Some(rt) = helper.runtime_mut::<AsanRuntime>() {
|
|
||||||
rt.emit_shadow_check(
|
|
||||||
address,
|
|
||||||
&output,
|
|
||||||
basereg,
|
|
||||||
indexreg,
|
|
||||||
displacement,
|
|
||||||
width,
|
|
||||||
shift,
|
|
||||||
extender,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
|
||||||
if let Some(rt) = helper.runtime::<CmpLogRuntime>() {
|
|
||||||
if let Some((op1, op2, special_case)) =
|
|
||||||
CmpLogRuntime::cmplog_is_interesting_instruction(
|
|
||||||
&helper.capstone,
|
|
||||||
address,
|
|
||||||
instr,
|
|
||||||
)
|
|
||||||
{
|
|
||||||
//emit code that saves the relevant data in runtime(passes it to x0, x1)
|
|
||||||
rt.emit_comparison_handling(
|
|
||||||
address,
|
|
||||||
&output,
|
|
||||||
&op1,
|
|
||||||
&op2,
|
|
||||||
special_case,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
if let Some(rt) = helper.runtime_mut::<AsanRuntime>() {
|
|
||||||
rt.add_stalked_address(
|
|
||||||
output.writer().pc() as usize - instr_size,
|
|
||||||
address as usize,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(unix)]
|
|
||||||
if let Some(rt) = helper.runtime_mut::<DrCovRuntime>() {
|
|
||||||
rt.add_stalked_address(
|
|
||||||
output.writer().pc() as usize - instr_size,
|
|
||||||
address as usize,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
instruction.keep();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
helper.transformer = Some(transformer);
|
|
||||||
helper
|
helper
|
||||||
.runtimes
|
.runtimes
|
||||||
.init_all(gum, &helper.ranges, &modules_to_instrument);
|
.init_all(gum, &helper.ranges, &modules_to_instrument);
|
||||||
@ -385,8 +272,110 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Returns ref to the Transformer
|
/// Returns ref to the Transformer
|
||||||
pub fn transformer(&self) -> &Transformer<'a> {
|
pub fn transformer(&mut self, gum: &'a Gum) -> Transformer<'a> {
|
||||||
self.transformer.as_ref().unwrap()
|
Transformer::from_callback(gum, |basic_block, output| {
|
||||||
|
let mut first = true;
|
||||||
|
for instruction in basic_block {
|
||||||
|
let instr = instruction.instr();
|
||||||
|
#[cfg(unix)]
|
||||||
|
let instr_size = instr.bytes().len();
|
||||||
|
let address = instr.address();
|
||||||
|
//println!("block @ {:x} transformed to {:x}", address, output.writer().pc());
|
||||||
|
|
||||||
|
//println!(
|
||||||
|
//"address: {:x} contains: {:?}",
|
||||||
|
//address,
|
||||||
|
//self.ranges().contains_key(&(address as usize))
|
||||||
|
//);
|
||||||
|
|
||||||
|
// println!("Ranges: {:#?}", self.ranges());
|
||||||
|
if self.ranges().contains_key(&(address as usize)) {
|
||||||
|
if first {
|
||||||
|
first = false;
|
||||||
|
//println!("block @ {:x} transformed to {:x}", address, output.writer().pc());
|
||||||
|
if let Some(rt) = self.runtime_mut::<CoverageRuntime>() {
|
||||||
|
rt.emit_coverage_mapping(address, &output);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some(rt) = self.runtime_mut::<DrCovRuntime>() {
|
||||||
|
instruction.put_callout(|context| {
|
||||||
|
let real_address = rt.real_address_for_stalked(pc(&context));
|
||||||
|
//let (range, (id, name)) = helper.ranges.get_key_value(&real_address).unwrap();
|
||||||
|
//println!("{}:0x{:016x}", name, real_address - range.start);
|
||||||
|
rt.drcov_basic_blocks.push(DrCovBasicBlock::new(
|
||||||
|
real_address,
|
||||||
|
real_address + instr_size,
|
||||||
|
));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
let res = if let Some(_rt) = self.runtime::<AsanRuntime>() {
|
||||||
|
AsanRuntime::asan_is_interesting_instruction(&self.capstone, address, instr)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
#[cfg(all(target_arch = "x86_64", unix))]
|
||||||
|
if let Some((segment, width, basereg, indexreg, scale, disp)) = res {
|
||||||
|
if let Some(rt) = self.runtime_mut::<AsanRuntime>() {
|
||||||
|
rt.emit_shadow_check(
|
||||||
|
address, &output, segment, width, basereg, indexreg, scale, disp,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "aarch64")]
|
||||||
|
if let Some((basereg, indexreg, displacement, width, shift, extender)) = res {
|
||||||
|
if let Some(rt) = self.runtime_mut::<AsanRuntime>() {
|
||||||
|
rt.emit_shadow_check(
|
||||||
|
address,
|
||||||
|
&output,
|
||||||
|
basereg,
|
||||||
|
indexreg,
|
||||||
|
displacement,
|
||||||
|
width,
|
||||||
|
shift,
|
||||||
|
extender,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(all(feature = "cmplog", target_arch = "aarch64"))]
|
||||||
|
if let Some(rt) = self.runtime::<CmpLogRuntime>() {
|
||||||
|
if let Some((op1, op2, special_case)) =
|
||||||
|
CmpLogRuntime::cmplog_is_interesting_instruction(
|
||||||
|
&self.capstone,
|
||||||
|
address,
|
||||||
|
instr,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
//emit code that saves the relevant data in runtime(passes it to x0, x1)
|
||||||
|
rt.emit_comparison_handling(address, &output, &op1, &op2, special_case);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some(rt) = self.runtime_mut::<AsanRuntime>() {
|
||||||
|
rt.add_stalked_address(
|
||||||
|
output.writer().pc() as usize - instr_size,
|
||||||
|
address as usize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(unix)]
|
||||||
|
if let Some(rt) = self.runtime_mut::<DrCovRuntime>() {
|
||||||
|
rt.add_stalked_address(
|
||||||
|
output.writer().pc() as usize - instr_size,
|
||||||
|
address as usize,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
instruction.keep();
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializa all
|
/// Initializa all
|
||||||
|
@ -9,14 +9,16 @@ echo "[*] Making sure all Nyx is checked out"
|
|||||||
|
|
||||||
git status 1>/dev/null 2>/dev/null
|
git status 1>/dev/null 2>/dev/null
|
||||||
|
|
||||||
if [ ! -d ./QEMU-Nyx ]; then
|
if [ ! -e ./QEMU-Nyx/.git ]; then
|
||||||
|
rm -rf ./QEMU-Nyx
|
||||||
git clone https://github.com/nyx-fuzz/QEMU-Nyx.git || exit 1
|
git clone https://github.com/nyx-fuzz/QEMU-Nyx.git || exit 1
|
||||||
pushd QEMU-Nyx
|
pushd QEMU-Nyx
|
||||||
git reset --hard 80f22f77d6aab14e62bf11c80db4e210bbca5fb5
|
git reset --hard 80f22f77d6aab14e62bf11c80db4e210bbca5fb5
|
||||||
popd
|
popd
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [ ! -d ./packer ]; then
|
if [ ! -e ./packer/.git ]; then
|
||||||
|
rm -rf ./packer
|
||||||
git clone https://github.com/syheliel/packer.git || exit 1
|
git clone https://github.com/syheliel/packer.git || exit 1
|
||||||
pushd QEMU-Nyx
|
pushd QEMU-Nyx
|
||||||
git reset --hard 86b159bafc0b2ba8feeaa8761a45b6201d34084f
|
git reset --hard 86b159bafc0b2ba8feeaa8761a45b6201d34084f
|
||||||
|
@ -21,6 +21,8 @@ x86_64 = ["libafl_qemu_sys/x86_64"] # build qemu for x86_64 (default)
|
|||||||
i386 = ["libafl_qemu_sys/i386"] # build qemu for i386
|
i386 = ["libafl_qemu_sys/i386"] # build qemu for i386
|
||||||
arm = ["libafl_qemu_sys/arm"] # build qemu for arm
|
arm = ["libafl_qemu_sys/arm"] # build qemu for arm
|
||||||
aarch64 = ["libafl_qemu_sys/aarch64"] # build qemu for aarch64
|
aarch64 = ["libafl_qemu_sys/aarch64"] # build qemu for aarch64
|
||||||
|
mips = ["libafl_qemu_sys/mips"] # build qemu for mips (el, use with the 'be' feature of mips be)
|
||||||
|
|
||||||
be = ["libafl_qemu_sys/be"]
|
be = ["libafl_qemu_sys/be"]
|
||||||
|
|
||||||
usermode = ["libafl_qemu_sys/usermode"]
|
usermode = ["libafl_qemu_sys/usermode"]
|
||||||
|
@ -26,6 +26,8 @@ pub fn build() {
|
|||||||
"aarch64".to_string()
|
"aarch64".to_string()
|
||||||
} else if cfg!(feature = "i386") {
|
} else if cfg!(feature = "i386") {
|
||||||
"i386".to_string()
|
"i386".to_string()
|
||||||
|
} else if cfg!(feature = "mips") {
|
||||||
|
"mips".to_string()
|
||||||
} else {
|
} else {
|
||||||
env::var("CPU_TARGET").unwrap_or_else(|_| {
|
env::var("CPU_TARGET").unwrap_or_else(|_| {
|
||||||
"x86_64".to_string()
|
"x86_64".to_string()
|
||||||
|
@ -45,6 +45,8 @@ const WRAPPER_HEADER: &str = r#"
|
|||||||
#include "sysemu/tcg.h"
|
#include "sysemu/tcg.h"
|
||||||
#include "sysemu/replay.h"
|
#include "sysemu/replay.h"
|
||||||
|
|
||||||
|
#include "libafl_extras/syx-snapshot/syx-snapshot.h"
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "exec/cpu-common.h"
|
#include "exec/cpu-common.h"
|
||||||
@ -105,6 +107,10 @@ pub fn generate(
|
|||||||
.allowlist_function("tlb_plugin_lookup")
|
.allowlist_function("tlb_plugin_lookup")
|
||||||
.allowlist_function("qemu_plugin_hwaddr_phys_addr")
|
.allowlist_function("qemu_plugin_hwaddr_phys_addr")
|
||||||
.allowlist_function("qemu_plugin_get_hwaddr")
|
.allowlist_function("qemu_plugin_get_hwaddr")
|
||||||
|
.allowlist_function("syx_snapshot_init")
|
||||||
|
.allowlist_function("syx_snapshot_create")
|
||||||
|
.allowlist_function("syx_snapshot_root_restore")
|
||||||
|
.allowlist_function("syx_snapshot_dirty_list_add")
|
||||||
.blocklist_function("main_loop_wait") // bindgen issue #1313
|
.blocklist_function("main_loop_wait") // bindgen issue #1313
|
||||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks));
|
.parse_callbacks(Box::new(bindgen::CargoCallbacks));
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ use which::which;
|
|||||||
|
|
||||||
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
const QEMU_URL: &str = "https://github.com/AFLplusplus/qemu-libafl-bridge";
|
||||||
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
const QEMU_DIRNAME: &str = "qemu-libafl-bridge";
|
||||||
const QEMU_REVISION: &str = "9707dd2d211221367915d5da21fe8693d6842eaf";
|
const QEMU_REVISION: &str = "e5424c34d223c2b638af6e4c9eef039db8b69dd4";
|
||||||
|
|
||||||
fn build_dep_check(tools: &[&str]) {
|
fn build_dep_check(tools: &[&str]) {
|
||||||
for tool in tools {
|
for tool in tools {
|
||||||
@ -39,6 +39,10 @@ pub fn build(
|
|||||||
cpu_target += "eb";
|
cpu_target += "eb";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !is_big_endian && cpu_target == "mips" && !cfg!(feature = "clippy") {
|
||||||
|
cpu_target += "el";
|
||||||
|
}
|
||||||
|
|
||||||
let custum_qemu_dir = env::var_os("CUSTOM_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
|
let custum_qemu_dir = env::var_os("CUSTOM_QEMU_DIR").map(|x| x.to_string_lossy().to_string());
|
||||||
let custum_qemu_no_build = env::var("CUSTOM_QEMU_NO_BUILD").is_ok();
|
let custum_qemu_no_build = env::var("CUSTOM_QEMU_NO_BUILD").is_ok();
|
||||||
let custum_qemu_no_configure = env::var("CUSTOM_QEMU_NO_CONFIGURE").is_ok();
|
let custum_qemu_no_configure = env::var("CUSTOM_QEMU_NO_CONFIGURE").is_ok();
|
||||||
@ -69,10 +73,7 @@ pub fn build(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if !qemu_path.is_dir() {
|
if !qemu_path.is_dir() {
|
||||||
println!(
|
println!("cargo:warning=Qemu not found, cloning with git ({QEMU_REVISION})...");
|
||||||
"cargo:warning=Qemu not found, cloning with git ({})...",
|
|
||||||
QEMU_REVISION
|
|
||||||
);
|
|
||||||
fs::create_dir_all(&qemu_path).unwrap();
|
fs::create_dir_all(&qemu_path).unwrap();
|
||||||
Command::new("git")
|
Command::new("git")
|
||||||
.current_dir(&qemu_path)
|
.current_dir(&qemu_path)
|
||||||
|
@ -5,5 +5,5 @@ use libafl_qemu_build::build_with_bindings;
|
|||||||
// RUST_BACKTRACE=1 OUT_DIR=/tmp/foo/a/b/c cargo run
|
// RUST_BACKTRACE=1 OUT_DIR=/tmp/foo/a/b/c cargo run
|
||||||
fn main() {
|
fn main() {
|
||||||
let bfile = PathBuf::from("generated_qemu_bindings.rs");
|
let bfile = PathBuf::from("generated_qemu_bindings.rs");
|
||||||
build_with_bindings("arm", false, true, None, &bfile);
|
build_with_bindings("arm", false, false, None, &bfile);
|
||||||
}
|
}
|
||||||
|
@ -17,6 +17,8 @@ x86_64 = [] # build qemu for x86_64 (default)
|
|||||||
i386 = [] # build qemu for i386
|
i386 = [] # build qemu for i386
|
||||||
arm = [] # build qemu for arm
|
arm = [] # build qemu for arm
|
||||||
aarch64 = [] # build qemu for aarch64
|
aarch64 = [] # build qemu for aarch64
|
||||||
|
mips = [] # build qemu for mips (el, use with the 'be' feature of mips be)
|
||||||
|
|
||||||
be = []
|
be = []
|
||||||
|
|
||||||
usermode = []
|
usermode = []
|
||||||
|
@ -38,9 +38,9 @@ pub fn build() {
|
|||||||
|
|
||||||
// Make sure we have at most one architecutre feature set
|
// Make sure we have at most one architecutre feature set
|
||||||
// Else, we default to `x86_64` - having a default makes CI easier :)
|
// Else, we default to `x86_64` - having a default makes CI easier :)
|
||||||
assert_unique_feature!("arm", "aarch64", "i386", "i86_64");
|
assert_unique_feature!("arm", "aarch64", "i386", "i86_64", "mips");
|
||||||
|
|
||||||
// Make sure that we don't have BE set for any architecture other than arm
|
// Make sure that we don't have BE set for any architecture other than arm and mips
|
||||||
// Sure aarch64 may support BE, but its not in common usage and we don't
|
// Sure aarch64 may support BE, but its not in common usage and we don't
|
||||||
// need it yet and so haven't tested it
|
// need it yet and so haven't tested it
|
||||||
assert_unique_feature!("be", "aarch64", "i386", "i86_64");
|
assert_unique_feature!("be", "aarch64", "i386", "i86_64");
|
||||||
@ -53,6 +53,8 @@ pub fn build() {
|
|||||||
"aarch64".to_string()
|
"aarch64".to_string()
|
||||||
} else if cfg!(feature = "i386") {
|
} else if cfg!(feature = "i386") {
|
||||||
"i386".to_string()
|
"i386".to_string()
|
||||||
|
} else if cfg!(feature = "mips") {
|
||||||
|
"mips".to_string()
|
||||||
} else {
|
} else {
|
||||||
env::var("CPU_TARGET").unwrap_or_else(|_| {
|
env::var("CPU_TARGET").unwrap_or_else(|_| {
|
||||||
println!(
|
println!(
|
||||||
|
@ -43,36 +43,7 @@ pub fn pc2basicblock(pc: GuestAddr, emu: &Emulator) -> Result<Vec<Instruction>,
|
|||||||
let mut iaddr = pc;
|
let mut iaddr = pc;
|
||||||
let mut block = Vec::<Instruction>::new();
|
let mut block = Vec::<Instruction>::new();
|
||||||
|
|
||||||
#[cfg(cpu_target = "x86_64")]
|
let cs = crate::capstone().detail(true).build().unwrap();
|
||||||
let cs = Capstone::new()
|
|
||||||
.x86()
|
|
||||||
.mode(capstone::arch::x86::ArchMode::Mode64)
|
|
||||||
.detail(true)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
#[cfg(cpu_target = "i386")]
|
|
||||||
let cs = Capstone::new()
|
|
||||||
.x86()
|
|
||||||
.mode(capstone::arch::x86::ArchMode::Mode32)
|
|
||||||
.detail(true)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
#[cfg(cpu_target = "arm")]
|
|
||||||
let cs = Capstone::new()
|
|
||||||
.arm()
|
|
||||||
.mode(capstone::arch::arm::ArchMode::Arm)
|
|
||||||
.detail(true)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
#[cfg(cpu_target = "aarch64")]
|
|
||||||
let cs = Capstone::new()
|
|
||||||
.arm64()
|
|
||||||
.mode(capstone::arch::arm64::ArchMode::Arm)
|
|
||||||
.detail(true)
|
|
||||||
.build()
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
'disasm: while let Ok(insns) = cs.disasm_count(code, iaddr.into(), 1) {
|
'disasm: while let Ok(insns) = cs.disasm_count(code, iaddr.into(), 1) {
|
||||||
if insns.is_empty() {
|
if insns.is_empty() {
|
||||||
|
@ -24,6 +24,9 @@ pub type GuestPhysAddr = libafl_qemu_sys::hwaddr;
|
|||||||
|
|
||||||
pub type GuestHwAddrInfo = libafl_qemu_sys::qemu_plugin_hwaddr;
|
pub type GuestHwAddrInfo = libafl_qemu_sys::qemu_plugin_hwaddr;
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
pub type FastSnapshot = *mut libafl_qemu_sys::syx_snapshot_t;
|
||||||
|
|
||||||
#[repr(transparent)]
|
#[repr(transparent)]
|
||||||
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct MemAccessInfo {
|
pub struct MemAccessInfo {
|
||||||
@ -279,8 +282,10 @@ extern "C" {
|
|||||||
fn qemu_cleanup();
|
fn qemu_cleanup();
|
||||||
|
|
||||||
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
fn libafl_save_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
#[allow(unused)]
|
|
||||||
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
fn libafl_load_qemu_snapshot(name: *const u8, sync: bool);
|
||||||
|
|
||||||
|
pub fn icount_get_raw() -> u64;
|
||||||
|
fn libafl_start_int_timer();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
@ -309,6 +314,9 @@ extern "C" {
|
|||||||
|
|
||||||
fn libafl_qemu_set_breakpoint(addr: u64) -> i32;
|
fn libafl_qemu_set_breakpoint(addr: u64) -> i32;
|
||||||
fn libafl_qemu_remove_breakpoint(addr: u64) -> i32;
|
fn libafl_qemu_remove_breakpoint(addr: u64) -> i32;
|
||||||
|
pub fn libafl_qemu_set_native_breakpoint(addr: u32);
|
||||||
|
pub fn libafl_qemu_remove_native_breakpoint(addr: u32);
|
||||||
|
|
||||||
fn libafl_flush_jit();
|
fn libafl_flush_jit();
|
||||||
fn libafl_qemu_trigger_breakpoint(cpu: CPUStatePtr);
|
fn libafl_qemu_trigger_breakpoint(cpu: CPUStatePtr);
|
||||||
|
|
||||||
@ -395,6 +403,15 @@ extern "C" {
|
|||||||
data: *const (),
|
data: *const (),
|
||||||
);
|
);
|
||||||
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
|
fn libafl_qemu_gdb_reply(buf: *const u8, len: usize);
|
||||||
|
|
||||||
|
// void libafl_add_jmp_hook(uint64_t (*gen)(target_ulong src, target_ulong dst, uint64_t data),
|
||||||
|
// void (*exec)(target_ulong src, target_ulong dst, uint64_t id, uint64_t data),
|
||||||
|
// uint64_t data);
|
||||||
|
fn libafl_add_jmp_hook(
|
||||||
|
gen: Option<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
|
||||||
|
exec: Option<extern "C" fn(GuestAddr, GuestAddr, u64, u64)>,
|
||||||
|
data: u64,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
@ -462,7 +479,7 @@ impl Drop for GuestMaps {
|
|||||||
|
|
||||||
#[repr(C)]
|
#[repr(C)]
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
pub(crate) struct FatPtr(*const c_void, *const c_void);
|
pub(crate) struct FatPtr(pub *const c_void, pub *const c_void);
|
||||||
|
|
||||||
static mut GDB_COMMANDS: Vec<FatPtr> = vec![];
|
static mut GDB_COMMANDS: Vec<FatPtr> = vec![];
|
||||||
|
|
||||||
@ -708,6 +725,7 @@ impl Emulator {
|
|||||||
envp.as_ptr() as *const *const u8,
|
envp.as_ptr() as *const *const u8,
|
||||||
);
|
);
|
||||||
libc::atexit(qemu_cleanup_atexit);
|
libc::atexit(qemu_cleanup_atexit);
|
||||||
|
libafl_qemu_sys::syx_snapshot_init();
|
||||||
}
|
}
|
||||||
EMULATOR_IS_INITIALIZED = true;
|
EMULATOR_IS_INITIALIZED = true;
|
||||||
}
|
}
|
||||||
@ -867,6 +885,7 @@ impl Emulator {
|
|||||||
libafl_qemu_run();
|
libafl_qemu_run();
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
{
|
{
|
||||||
|
libafl_start_int_timer();
|
||||||
vm_start();
|
vm_start();
|
||||||
qemu_main_loop();
|
qemu_main_loop();
|
||||||
}
|
}
|
||||||
@ -1049,6 +1068,15 @@ impl Emulator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_jmp_hooks(
|
||||||
|
&self,
|
||||||
|
gen: Option<extern "C" fn(GuestAddr, GuestAddr, u64) -> u64>,
|
||||||
|
exec: Option<extern "C" fn(GuestAddr, GuestAddr, u64, u64)>,
|
||||||
|
data: u64,
|
||||||
|
) {
|
||||||
|
unsafe { libafl_add_jmp_hook(gen, exec, data) }
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "systemmode")]
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
pub fn save_snapshot(&self, name: &str, sync: bool) {
|
pub fn save_snapshot(&self, name: &str, sync: bool) {
|
||||||
let s = CString::new(name).expect("Invalid snapshot name");
|
let s = CString::new(name).expect("Invalid snapshot name");
|
||||||
@ -1061,6 +1089,17 @@ impl Emulator {
|
|||||||
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
unsafe { libafl_load_qemu_snapshot(s.as_ptr() as *const _, sync) };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
#[must_use]
|
||||||
|
pub fn create_fast_snapshot(&self, track: bool) -> FastSnapshot {
|
||||||
|
unsafe { libafl_qemu_sys::syx_snapshot_create(track) }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(emulation_mode = "systemmode")]
|
||||||
|
pub fn restore_fast_snapshot(&self, snapshot: FastSnapshot) {
|
||||||
|
unsafe { libafl_qemu_sys::syx_snapshot_root_restore(snapshot) }
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub fn set_pre_syscall_hook(
|
pub fn set_pre_syscall_hook(
|
||||||
&self,
|
&self,
|
||||||
|
@ -655,6 +655,78 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static mut JMP_HOOKS: Vec<(Hook, Hook)> = vec![];
|
||||||
|
|
||||||
|
extern "C" fn gen_jmp_hook_wrapper<QT, S>(src: GuestAddr, dst: GuestAddr, index: u64) -> u64
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let hooks = get_qemu_hooks::<QT, S>();
|
||||||
|
let (gen, _) = &mut JMP_HOOKS[index as usize];
|
||||||
|
match gen {
|
||||||
|
Hook::Function(ptr) => {
|
||||||
|
let func: fn(
|
||||||
|
&mut QemuHooks<'_, QT, S>,
|
||||||
|
Option<&mut S>,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
) -> Option<u64> = transmute(*ptr);
|
||||||
|
(func)(hooks, inprocess_get_state::<S>(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
|
||||||
|
}
|
||||||
|
Hook::Closure(ptr) => {
|
||||||
|
let func: &mut Box<
|
||||||
|
dyn FnMut(
|
||||||
|
&mut QemuHooks<'_, QT, S>,
|
||||||
|
Option<&mut S>,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
) -> Option<u64>,
|
||||||
|
> = transmute(ptr);
|
||||||
|
(func)(hooks, inprocess_get_state::<S>(), src, dst).map_or(SKIP_EXEC_HOOK, |id| id)
|
||||||
|
}
|
||||||
|
_ => 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn exec_jmp_hook_wrapper<QT, S>(src: GuestAddr, dst: GuestAddr, id: u64, index: u64)
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
QT: QemuHelperTuple<S>,
|
||||||
|
{
|
||||||
|
unsafe {
|
||||||
|
let hooks = get_qemu_hooks::<QT, S>();
|
||||||
|
let (_, exec) = &mut JMP_HOOKS[index as usize];
|
||||||
|
match exec {
|
||||||
|
Hook::Function(ptr) => {
|
||||||
|
let func: fn(
|
||||||
|
&mut QemuHooks<'_, QT, S>,
|
||||||
|
Option<&mut S>,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
u64,
|
||||||
|
) = transmute(*ptr);
|
||||||
|
(func)(hooks, inprocess_get_state::<S>(), src, dst, id);
|
||||||
|
}
|
||||||
|
Hook::Closure(ptr) => {
|
||||||
|
let func: &mut Box<
|
||||||
|
dyn FnMut(
|
||||||
|
&mut QemuHooks<'_, QT, S>,
|
||||||
|
Option<&mut S>,
|
||||||
|
GuestAddr,
|
||||||
|
GuestAddr,
|
||||||
|
u64,
|
||||||
|
),
|
||||||
|
> = transmute(ptr);
|
||||||
|
(func)(hooks, inprocess_get_state::<S>(), src, dst, id);
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static mut HOOKS_IS_INITIALIZED: bool = false;
|
static mut HOOKS_IS_INITIALIZED: bool = false;
|
||||||
|
|
||||||
pub struct QemuHooks<'a, QT, S>
|
pub struct QemuHooks<'a, QT, S>
|
||||||
@ -1556,4 +1628,37 @@ where
|
|||||||
self.emulator
|
self.emulator
|
||||||
.set_post_syscall_hook(syscall_after_hooks_wrapper::<QT, S>);
|
.set_post_syscall_hook(syscall_after_hooks_wrapper::<QT, S>);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn jmps(
|
||||||
|
&self,
|
||||||
|
generation_hook: Option<
|
||||||
|
fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr) -> Option<u64>,
|
||||||
|
>,
|
||||||
|
execution_hook: Option<fn(&mut Self, Option<&mut S>, src: GuestAddr, dest: GuestAddr, id: u64)>,
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
let index = JMP_HOOKS.len();
|
||||||
|
self.emulator.add_jmp_hooks(
|
||||||
|
if generation_hook.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(gen_jmp_hook_wrapper::<QT, S>)
|
||||||
|
},
|
||||||
|
if execution_hook.is_none() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(exec_jmp_hook_wrapper::<QT, S>)
|
||||||
|
},
|
||||||
|
index as u64,
|
||||||
|
);
|
||||||
|
JMP_HOOKS.push((
|
||||||
|
generation_hook.map_or(Hook::Empty, |hook| {
|
||||||
|
Hook::Function(hook as *const libc::c_void)
|
||||||
|
}),
|
||||||
|
execution_hook.map_or(Hook::Empty, |hook| {
|
||||||
|
Hook::Function(hook as *const libc::c_void)
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,11 @@ pub mod x86_64;
|
|||||||
#[cfg(cpu_target = "x86_64")]
|
#[cfg(cpu_target = "x86_64")]
|
||||||
pub use x86_64::*;
|
pub use x86_64::*;
|
||||||
|
|
||||||
|
#[cfg(cpu_target = "mips")]
|
||||||
|
pub mod mips;
|
||||||
|
#[cfg(cpu_target = "mips")]
|
||||||
|
pub use mips::*;
|
||||||
|
|
||||||
pub mod elf;
|
pub mod elf;
|
||||||
|
|
||||||
pub mod helper;
|
pub mod helper;
|
||||||
@ -53,20 +58,27 @@ pub use hooks::*;
|
|||||||
|
|
||||||
pub mod edges;
|
pub mod edges;
|
||||||
pub use edges::QemuEdgeCoverageHelper;
|
pub use edges::QemuEdgeCoverageHelper;
|
||||||
|
|
||||||
|
#[cfg(not(cpu_target = "mips"))]
|
||||||
pub mod cmplog;
|
pub mod cmplog;
|
||||||
pub mod drcov;
|
#[cfg(not(cpu_target = "mips"))]
|
||||||
pub use cmplog::QemuCmpLogHelper;
|
pub use cmplog::QemuCmpLogHelper;
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub mod snapshot;
|
pub mod snapshot;
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub use snapshot::QemuSnapshotHelper;
|
pub use snapshot::QemuSnapshotHelper;
|
||||||
|
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub mod asan;
|
pub mod asan;
|
||||||
#[cfg(emulation_mode = "usermode")]
|
#[cfg(emulation_mode = "usermode")]
|
||||||
pub use asan::{init_with_asan, QemuAsanHelper};
|
pub use asan::{init_with_asan, QemuAsanHelper};
|
||||||
|
|
||||||
pub mod blocks;
|
pub mod blocks;
|
||||||
|
|
||||||
|
#[cfg(not(cpu_target = "mips"))]
|
||||||
pub mod calls;
|
pub mod calls;
|
||||||
|
pub mod drcov;
|
||||||
|
|
||||||
pub mod executor;
|
pub mod executor;
|
||||||
pub use executor::QemuExecutor;
|
pub use executor::QemuExecutor;
|
||||||
|
66
libafl_qemu/src/mips.rs
Normal file
66
libafl_qemu/src/mips.rs
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
use pyo3::prelude::*;
|
||||||
|
pub use strum_macros::EnumIter;
|
||||||
|
pub use syscall_numbers::mips::*;
|
||||||
|
|
||||||
|
/// Registers for the ARM instruction set.
|
||||||
|
#[derive(IntoPrimitive, TryFromPrimitive, Debug, Clone, Copy, EnumIter)]
|
||||||
|
#[repr(i32)]
|
||||||
|
pub enum Regs {
|
||||||
|
R0 = 0,
|
||||||
|
R1 = 1,
|
||||||
|
R2 = 2,
|
||||||
|
R3 = 3,
|
||||||
|
R4 = 4,
|
||||||
|
R5 = 5,
|
||||||
|
R6 = 6,
|
||||||
|
R7 = 7,
|
||||||
|
R8 = 8,
|
||||||
|
R9 = 9,
|
||||||
|
R10 = 10,
|
||||||
|
R11 = 11,
|
||||||
|
R12 = 12,
|
||||||
|
R13 = 13,
|
||||||
|
R14 = 14,
|
||||||
|
R15 = 15,
|
||||||
|
R16 = 16,
|
||||||
|
R17 = 17,
|
||||||
|
R18 = 18,
|
||||||
|
R19 = 19,
|
||||||
|
R20 = 20,
|
||||||
|
R21 = 21,
|
||||||
|
R22 = 22,
|
||||||
|
R23 = 23,
|
||||||
|
R24 = 24,
|
||||||
|
R25 = 25,
|
||||||
|
R26 = 26,
|
||||||
|
R27 = 27,
|
||||||
|
R28 = 28,
|
||||||
|
R29 = 29,
|
||||||
|
R30 = 30,
|
||||||
|
R31 = 31,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// alias registers
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
impl Regs {
|
||||||
|
pub const Zero: Regs = Regs::R0;
|
||||||
|
pub const Gp: Regs = Regs::R28;
|
||||||
|
pub const Sp: Regs = Regs::R29;
|
||||||
|
pub const Fp: Regs = Regs::R30;
|
||||||
|
pub const Ra: Regs = Regs::R31;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "python")]
|
||||||
|
impl IntoPy<PyObject> for Regs {
|
||||||
|
fn into_py(self, py: Python) -> PyObject {
|
||||||
|
let n: i32 = self.into();
|
||||||
|
n.into_py(py)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Return an MIPS ArchCapstoneBuilder
|
||||||
|
pub fn capstone() -> capstone::arch::mips::ArchCapstoneBuilder {
|
||||||
|
capstone::Capstone::new().mips()
|
||||||
|
}
|
@ -8,6 +8,14 @@ use libafl::{inputs::UsesInput, state::HasMetadata};
|
|||||||
use meminterval::{Interval, IntervalTree};
|
use meminterval::{Interval, IntervalTree};
|
||||||
use thread_local::ThreadLocal;
|
use thread_local::ThreadLocal;
|
||||||
|
|
||||||
|
#[cfg(any(cpu_target = "arm", cpu_target = "i386", cpu_target = "mips"))]
|
||||||
|
use crate::SYS_fstatat64;
|
||||||
|
#[cfg(not(cpu_target = "arm"))]
|
||||||
|
use crate::SYS_mmap;
|
||||||
|
#[cfg(any(cpu_target = "arm", cpu_target = "mips"))]
|
||||||
|
use crate::SYS_mmap2;
|
||||||
|
#[cfg(not(any(cpu_target = "arm", cpu_target = "mips", cpu_target = "i386")))]
|
||||||
|
use crate::SYS_newfstatat;
|
||||||
use crate::{
|
use crate::{
|
||||||
emu::{Emulator, MmapPerms, SyscallHookResult},
|
emu::{Emulator, MmapPerms, SyscallHookResult},
|
||||||
helper::{QemuHelper, QemuHelperTuple},
|
helper::{QemuHelper, QemuHelperTuple},
|
||||||
@ -15,12 +23,6 @@ use crate::{
|
|||||||
GuestAddr, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap,
|
GuestAddr, SYS_fstat, SYS_fstatfs, SYS_futex, SYS_getrandom, SYS_mprotect, SYS_mremap,
|
||||||
SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs,
|
SYS_munmap, SYS_pread64, SYS_read, SYS_readlinkat, SYS_statfs,
|
||||||
};
|
};
|
||||||
#[cfg(cpu_target = "i386")]
|
|
||||||
use crate::{SYS_fstatat64, SYS_mmap};
|
|
||||||
#[cfg(cpu_target = "arm")]
|
|
||||||
use crate::{SYS_fstatat64, SYS_mmap2};
|
|
||||||
#[cfg(not(any(cpu_target = "arm", cpu_target = "i386")))]
|
|
||||||
use crate::{SYS_mmap, SYS_newfstatat};
|
|
||||||
|
|
||||||
// TODO use the functions provided by Emulator
|
// TODO use the functions provided by Emulator
|
||||||
pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
|
pub const SNAPSHOT_PAGE_SIZE: usize = 4096;
|
||||||
@ -301,6 +303,10 @@ impl QemuSnapshotHelper {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_mapped(&mut self, start: GuestAddr, mut size: usize, perms: Option<MmapPerms>) {
|
pub fn add_mapped(&mut self, start: GuestAddr, mut size: usize, perms: Option<MmapPerms>) {
|
||||||
|
if size == 0 {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let total_size = {
|
let total_size = {
|
||||||
if size % SNAPSHOT_PAGE_SIZE != 0 {
|
if size % SNAPSHOT_PAGE_SIZE != 0 {
|
||||||
size = size + (SNAPSHOT_PAGE_SIZE - size % SNAPSHOT_PAGE_SIZE);
|
size = size + (SNAPSHOT_PAGE_SIZE - size % SNAPSHOT_PAGE_SIZE);
|
||||||
@ -632,14 +638,14 @@ where
|
|||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.access(a0 as GuestAddr, a3 as usize);
|
h.access(a0 as GuestAddr, a3 as usize);
|
||||||
}
|
}
|
||||||
#[cfg(not(any(cpu_target = "arm", cpu_target = "i386")))]
|
#[cfg(not(any(cpu_target = "arm", cpu_target = "i386", cpu_target = "mips")))]
|
||||||
SYS_newfstatat => {
|
SYS_newfstatat => {
|
||||||
if a2 != 0 {
|
if a2 != 0 {
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.access(a2 as GuestAddr, 4096); // stat is not greater than a page
|
h.access(a2 as GuestAddr, 4096); // stat is not greater than a page
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(any(cpu_target = "arm", cpu_target = "i386"))]
|
#[cfg(any(cpu_target = "arm", cpu_target = "mips", cpu_target = "i386"))]
|
||||||
SYS_fstatat64 => {
|
SYS_fstatat64 => {
|
||||||
if a2 != 0 {
|
if a2 != 0 {
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
@ -664,27 +670,12 @@ where
|
|||||||
|
|
||||||
// TODO handle huge pages
|
// TODO handle huge pages
|
||||||
|
|
||||||
#[cfg(cpu_target = "arm")]
|
#[cfg(any(cpu_target = "arm", cpu_target = "mips"))]
|
||||||
if i64::from(sys_num) == SYS_mmap2 {
|
if i64::from(sys_num) == SYS_mmap2 {
|
||||||
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
|
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.add_mapped(result as GuestAddr, a1 as usize, Some(prot));
|
h.add_mapped(result as GuestAddr, a1 as usize, Some(prot));
|
||||||
}
|
}
|
||||||
} else if i64::from(sys_num) == SYS_mremap {
|
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
|
||||||
h.remove_mapped(a0 as GuestAddr, a1 as usize);
|
|
||||||
h.add_mapped(result as GuestAddr, a2 as usize, None);
|
|
||||||
// TODO get the old permissions from the removed mapping
|
|
||||||
} else if i64::from(sys_num) == SYS_mprotect {
|
|
||||||
if let Ok(prot) = MmapPerms::try_from(a2 as i32) {
|
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
|
||||||
h.add_mapped(a0 as GuestAddr, a1 as usize, Some(prot));
|
|
||||||
}
|
|
||||||
} else if i64::from(sys_num) == SYS_munmap {
|
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
|
||||||
if !h.accurate_unmap && !h.is_unmap_allowed(a0 as GuestAddr, a1 as usize) {
|
|
||||||
h.remove_mapped(a0 as GuestAddr, a1 as usize);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(cpu_target = "arm"))]
|
#[cfg(not(cpu_target = "arm"))]
|
||||||
@ -693,7 +684,9 @@ where
|
|||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.add_mapped(result as GuestAddr, a1 as usize, Some(prot));
|
h.add_mapped(result as GuestAddr, a1 as usize, Some(prot));
|
||||||
}
|
}
|
||||||
} else if i64::from(sys_num) == SYS_mremap {
|
}
|
||||||
|
|
||||||
|
if i64::from(sys_num) == SYS_mremap {
|
||||||
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
let h = hooks.match_helper_mut::<QemuSnapshotHelper>().unwrap();
|
||||||
h.remove_mapped(a0 as GuestAddr, a1 as usize);
|
h.remove_mapped(a0 as GuestAddr, a1 as usize);
|
||||||
h.add_mapped(result as GuestAddr, a2 as usize, None);
|
h.add_mapped(result as GuestAddr, a2 as usize, None);
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use std::{env, fs::File, io::Write, path::Path};
|
use std::{env, fs::File, io::Write, path::Path};
|
||||||
|
|
||||||
|
#[allow(clippy::too_many_lines)]
|
||||||
fn main() {
|
fn main() {
|
||||||
let out_dir = env::var_os("OUT_DIR").unwrap();
|
let out_dir = env::var_os("OUT_DIR").unwrap();
|
||||||
let out_dir = out_dir.to_string_lossy().to_string();
|
let out_dir = out_dir.to_string_lossy().to_string();
|
||||||
@ -32,17 +33,16 @@ fn main() {
|
|||||||
"// These constants are autogenerated by build.rs
|
"// These constants are autogenerated by build.rs
|
||||||
|
|
||||||
/// The size of the edges map
|
/// The size of the edges map
|
||||||
pub const EDGES_MAP_SIZE: usize = {};
|
pub const EDGES_MAP_SIZE: usize = {edges_map_size};
|
||||||
/// The size of the cmps map
|
/// The size of the cmps map
|
||||||
pub const CMP_MAP_SIZE: usize = {};
|
pub const CMP_MAP_SIZE: usize = {cmp_map_size};
|
||||||
/// The width of the `CmpLog` map
|
/// The width of the `CmpLog` map
|
||||||
pub const CMPLOG_MAP_W: usize = {};
|
pub const CMPLOG_MAP_W: usize = {cmplog_map_w};
|
||||||
/// The height of the `CmpLog` map
|
/// The height of the `CmpLog` map
|
||||||
pub const CMPLOG_MAP_H: usize = {};
|
pub const CMPLOG_MAP_H: usize = {cmplog_map_h};
|
||||||
/// The size of the accounting maps
|
/// The size of the accounting maps
|
||||||
pub const ACCOUNTING_MAP_SIZE: usize = {};
|
pub const ACCOUNTING_MAP_SIZE: usize = {acc_map_size};
|
||||||
",
|
"
|
||||||
edges_map_size, cmp_map_size, cmplog_map_w, cmplog_map_h, acc_map_size
|
|
||||||
)
|
)
|
||||||
.expect("Could not write file");
|
.expect("Could not write file");
|
||||||
|
|
||||||
@ -127,6 +127,15 @@ fn main() {
|
|||||||
.compile("forkserver");
|
.compile("forkserver");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
println!("cargo:rerun-if-changed=src/windows_asan.c");
|
||||||
|
|
||||||
|
cc::Build::new()
|
||||||
|
.file(src_dir.join("windows_asan.c"))
|
||||||
|
.compile("windows_asan");
|
||||||
|
}
|
||||||
|
|
||||||
println!("cargo:rustc-link-search=native={}", &out_dir);
|
println!("cargo:rustc-link-search=native={}", &out_dir);
|
||||||
|
|
||||||
println!("cargo:rerun-if-changed=build.rs");
|
println!("cargo:rerun-if-changed=build.rs");
|
||||||
|
@ -101,6 +101,11 @@ pub use cmplog::*;
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
pub mod drcov;
|
pub mod drcov;
|
||||||
|
|
||||||
|
#[cfg(all(windows, feature = "std"))]
|
||||||
|
pub mod windows_asan;
|
||||||
|
#[cfg(all(windows, feature = "std"))]
|
||||||
|
pub use windows_asan::*;
|
||||||
|
|
||||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||||
pub mod forkserver;
|
pub mod forkserver;
|
||||||
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
#[cfg(any(target_os = "linux", target_os = "freebsd"))]
|
||||||
|
5
libafl_targets/src/windows_asan.c
Normal file
5
libafl_targets/src/windows_asan.c
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
EXT_FUNC_IMPL(__sanitizer_set_death_callback, void, (void), false) {
|
||||||
|
return;
|
||||||
|
}
|
35
libafl_targets/src/windows_asan.rs
Normal file
35
libafl_targets/src/windows_asan.rs
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//! Setup asan death callbback
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
events::{EventFirer, EventRestarter},
|
||||||
|
executors::{inprocess::windows_asan_handler::asan_death_handler, Executor, HasObservers},
|
||||||
|
feedbacks::Feedback,
|
||||||
|
state::{HasClientPerfMonitor, HasSolutions},
|
||||||
|
HasObjective,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Asan death callback type
|
||||||
|
pub type CB = unsafe extern "C" fn() -> ();
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
|
fn __sanitizer_set_death_callback(cb: CB);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// # Safety
|
||||||
|
/// Setup asan callback on windows
|
||||||
|
// See https://github.com/AFLplusplus/LibAFL/issues/769
|
||||||
|
// This is needed to intercept asan error exit
|
||||||
|
// When we use AddressSanitizer on windows, the crash handler is not called when ASAN detects an error
|
||||||
|
// This is because, on linux, ASAN runtime raises SIGABRT so we can rely on the signal handler
|
||||||
|
// but on windows it simply calls TerminateProcess.
|
||||||
|
// so we need to call the api by asan to register the callback when asan is about to finish the process.
|
||||||
|
pub unsafe fn setup_asan_callback<E, EM, OF, Z>(_executor: &E, _event_mgr: &EM, _fuzzer: &Z)
|
||||||
|
where
|
||||||
|
E: Executor<EM, Z> + HasObservers,
|
||||||
|
EM: EventFirer<State = E::State> + EventRestarter<State = E::State>,
|
||||||
|
OF: Feedback<E::State>,
|
||||||
|
E::State: HasSolutions + HasClientPerfMonitor,
|
||||||
|
Z: HasObjective<Objective = OF, State = E::State>,
|
||||||
|
{
|
||||||
|
__sanitizer_set_death_callback(asan_death_handler::<E, EM, OF, Z>);
|
||||||
|
}
|
24
libafl_tinyinst/Cargo.toml
Normal file
24
libafl_tinyinst/Cargo.toml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "libafl_tinyinst"
|
||||||
|
version.workspace = true
|
||||||
|
edition = "2021"
|
||||||
|
authors = ["elbiazo <eric.l.biazo@gmail.com>", "Dongjia Zhang <tokazerkje@outlook.com>"]
|
||||||
|
repository = "https://github.com/AFLplusplus/LibAFL/"
|
||||||
|
categories = ["development-tools::testing", "emulators", "embedded", "os", "no-std"]
|
||||||
|
license = "MIT OR Apache-2.0"
|
||||||
|
keywords = ["fuzzing", "testing", "security"]
|
||||||
|
description = "TinyInst backend for libafl"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
cxx = "1.0"
|
||||||
|
libafl = { path = "../libafl", version = "0.8.0", features = [
|
||||||
|
"std",
|
||||||
|
"libafl_derive",
|
||||||
|
] }
|
||||||
|
tinyinst-rs = { git = "https://github.com/elbiazo/tinyinst-rs" }
|
||||||
|
# tinyinst-rs = { path = "../../tinyinst-rs" }
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
cmake = "0.1"
|
3
libafl_tinyinst/README.md
Normal file
3
libafl_tinyinst/README.md
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Requirements
|
||||||
|
- you need `Visual Studio 17 2022`, though it's not tested, it should work on older versions.
|
||||||
|
- you need `cxxbridge-cmd` to generate bridge files between Rust and c++, you can install this via `cargo install cxxbridge-cmd`
|
138
libafl_tinyinst/src/executor.rs
Normal file
138
libafl_tinyinst/src/executor.rs
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
use core::marker::PhantomData;
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
bolts::fs::{InputFile, INPUTFILE_STD},
|
||||||
|
executors::{Executor, ExitKind, HasObservers},
|
||||||
|
inputs::{HasTargetBytes, UsesInput},
|
||||||
|
observers::{ObserversTuple, UsesObservers},
|
||||||
|
prelude::AsSlice,
|
||||||
|
state::{State, UsesState},
|
||||||
|
Error,
|
||||||
|
};
|
||||||
|
use tinyinst_rs::tinyinst::{litecov::RunResult, TinyInst};
|
||||||
|
|
||||||
|
pub struct TinyInstExecutor<'a, S, OT> {
|
||||||
|
tinyinst: TinyInst,
|
||||||
|
coverage: &'a mut Vec<u64>,
|
||||||
|
timeout: u32,
|
||||||
|
observers: OT,
|
||||||
|
phantom: PhantomData<S>,
|
||||||
|
cur_input: InputFile,
|
||||||
|
use_stdin: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S, OT> std::fmt::Debug for TinyInstExecutor<'a, S, OT> {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.debug_struct("TinyInstExecutor")
|
||||||
|
.field("timeout", &self.timeout)
|
||||||
|
.finish_non_exhaustive()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, EM, S, Z, OT> Executor<EM, Z> for TinyInstExecutor<'a, S, OT>
|
||||||
|
where
|
||||||
|
EM: UsesState<State = S>,
|
||||||
|
S: UsesInput,
|
||||||
|
S::Input: HasTargetBytes,
|
||||||
|
Z: UsesState<State = S>,
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn run_target(
|
||||||
|
&mut self,
|
||||||
|
_fuzzer: &mut Z,
|
||||||
|
_state: &mut Self::State,
|
||||||
|
_mgr: &mut EM,
|
||||||
|
input: &Self::Input,
|
||||||
|
) -> Result<ExitKind, Error> {
|
||||||
|
if !self.use_stdin {
|
||||||
|
self.cur_input.write_buf(input.target_bytes().as_slice())?;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unused_assignments)]
|
||||||
|
let mut status = RunResult::OK;
|
||||||
|
unsafe {
|
||||||
|
status = self.tinyinst.run();
|
||||||
|
self.tinyinst.vec_coverage(self.coverage, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
match status {
|
||||||
|
RunResult::CRASH | RunResult::HANG => Ok(ExitKind::Crash),
|
||||||
|
RunResult::OK => Ok(ExitKind::Ok),
|
||||||
|
RunResult::OTHER_ERROR => Err(Error::unknown(
|
||||||
|
"Tinyinst RunResult is other error".to_string(),
|
||||||
|
)),
|
||||||
|
_ => Err(Error::unknown("Tinyinst RunResult is unknown".to_string())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S, OT> TinyInstExecutor<'a, S, OT>
|
||||||
|
where
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
/// # Safety
|
||||||
|
pub unsafe fn new(
|
||||||
|
coverage: &'a mut Vec<u64>,
|
||||||
|
tinyinst_args: Vec<String>,
|
||||||
|
program_args: Vec<String>,
|
||||||
|
timeout: u32,
|
||||||
|
observers: OT,
|
||||||
|
) -> Self {
|
||||||
|
let mut use_stdin = true;
|
||||||
|
|
||||||
|
let program_args = program_args
|
||||||
|
.into_iter()
|
||||||
|
.map(|arg| {
|
||||||
|
if arg == "@@" {
|
||||||
|
println!("Not using stdin");
|
||||||
|
use_stdin = false;
|
||||||
|
INPUTFILE_STD.to_string()
|
||||||
|
} else {
|
||||||
|
arg
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let cur_input = InputFile::create(INPUTFILE_STD).expect("Unable to create cur_file");
|
||||||
|
println!("post init");
|
||||||
|
let tinyinst = TinyInst::new(tinyinst_args, program_args, timeout);
|
||||||
|
|
||||||
|
Self {
|
||||||
|
tinyinst,
|
||||||
|
coverage,
|
||||||
|
timeout,
|
||||||
|
observers,
|
||||||
|
phantom: PhantomData,
|
||||||
|
cur_input,
|
||||||
|
use_stdin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, S, OT> HasObservers for TinyInstExecutor<'a, S, OT>
|
||||||
|
where
|
||||||
|
S: State,
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
{
|
||||||
|
fn observers(&self) -> &OT {
|
||||||
|
&self.observers
|
||||||
|
}
|
||||||
|
|
||||||
|
fn observers_mut(&mut self) -> &mut OT {
|
||||||
|
&mut self.observers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl<'a, S, OT> UsesState for TinyInstExecutor<'a, S, OT>
|
||||||
|
where
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
type State = S;
|
||||||
|
}
|
||||||
|
impl<'a, S, OT> UsesObservers for TinyInstExecutor<'a, S, OT>
|
||||||
|
where
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
S: UsesInput,
|
||||||
|
{
|
||||||
|
type Observers = OT;
|
||||||
|
}
|
1
libafl_tinyinst/src/lib.rs
Normal file
1
libafl_tinyinst/src/lib.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod executor;
|
Loading…
x
Reference in New Issue
Block a user