Compare commits

...

325 Commits

Author SHA1 Message Date
2b95a9d686 small build fixes 2023-06-12 12:13:28 +02:00
0e54bf24f2 port to libafl 0.10.1 2023-06-12 10:47:35 +02:00
173be564e5 remove dead code 2023-06-02 10:11:08 +02:00
32e3613473 igonre archives 2023-06-02 10:11:08 +02:00
6b497b758f eval script wrangeling 2023-06-02 10:11:08 +02:00
e0c438621a HACK: interrupt limit for random fuzzing 2023-06-02 10:11:08 +02:00
14f4f360cb plot enpoints 2023-06-02 10:11:08 +02:00
d3c8e30aa4 paralellize plots 2023-06-02 10:11:08 +02:00
84439cd4b9 update plot script 2023-06-02 10:11:08 +02:00
9877714a83 tweak time outputs 2023-06-02 10:11:08 +02:00
967aac60a3 add run_until_saturation 2023-06-02 10:11:08 +02:00
7f07ec21dd update snakefile 2023-06-02 10:11:08 +02:00
c70041d868 fix empty iterator crash, restart 2023-06-02 10:11:08 +02:00
dfda200eed set up configurations 2023-06-02 10:11:08 +02:00
ded4c0f6c3 add missing use 2023-06-02 10:11:08 +02:00
35102c031f randomize interrupts until wort 2023-06-02 10:11:08 +02:00
c389a84c1b Test: remove pc from hash 2023-06-02 10:11:08 +02:00
ce1a323063 Test: hash notification states 2023-06-02 10:11:08 +02:00
623e398749 allow plotting from remote mount 2023-06-02 10:11:08 +02:00
361800a0c9 update snakefile 2023-06-02 10:11:08 +02:00
8f80c326e2 small fixes 2023-06-02 10:11:08 +02:00
10f27eaf4d WIP: add simple interrupt time randomizer 2023-06-02 10:11:08 +02:00
52912c8c41 update target_symbols 2023-06-02 10:11:08 +02:00
6614969c6d skip unchanged interrupts 2023-06-02 10:11:08 +02:00
e5515274ff fix staeg setup 2023-06-02 10:11:08 +02:00
86180b3857 fix use 2023-06-02 10:11:08 +02:00
c3a7905950 WIP: move interrupt mutation to new stage 2023-06-02 10:11:08 +02:00
7a37e385d8 wip: interrupt placement 2023-06-02 10:11:08 +02:00
c80c9cea08 add interrupt mutator 2023-06-02 10:11:08 +02:00
700861712c plot min and max lines 2023-06-02 10:11:08 +02:00
7ce229312f plot lines instead of points 2023-06-02 10:11:08 +02:00
b50cc54b60 fixes 2023-06-02 10:11:08 +02:00
eca974c2e6 revert changes 2023-06-02 10:11:08 +02:00
4125f2be22 add generation based genetic testing 2023-06-02 10:11:08 +02:00
def4071c2b snakefile: dump cases, fix random fuzzing 2023-06-02 10:11:08 +02:00
99b46bc3f1 add a new scheduler for systemtraces 2023-06-02 10:11:08 +02:00
fe00fa18c1 fix snakefile, symbols 2023-06-02 10:11:08 +02:00
88242fc113 seed rng from SEED_RANDOM 2023-06-02 10:11:08 +02:00
0bf060b8c2 determinism fixes, scheduler precision, restarts 2023-06-02 10:11:08 +02:00
aaf267c72d switch to native breakpoints 2023-06-02 10:11:08 +02:00
0fb45ccbdf fix interrupt config 2023-06-02 10:11:08 +02:00
e62af5d4d0 fix rng seed 2023-06-02 10:11:08 +02:00
ea1e0e26bc configure restarting manager 2023-06-02 10:11:08 +02:00
9722f5ebe1 add micro_longint 2023-06-02 10:11:08 +02:00
ce87a9fe23 fuzz multiple interrupts 2023-06-02 10:11:08 +02:00
ec0606afae rework plotting 2023-06-02 10:11:08 +02:00
6b2eb6fad2 add interrupt fuzzing 2023-06-02 10:11:08 +02:00
133a53da94 dump time for showmap 2023-06-02 10:11:08 +02:00
b919bbecac add plotting to snakefile 2023-06-02 10:11:08 +02:00
b6e692b9f6 add feed_longest to record random cases 2023-06-02 10:11:07 +02:00
42ab549ba0 plotting: respect types 2023-06-02 10:11:07 +02:00
189a18d247 change feedback order 2023-06-02 10:11:07 +02:00
82afd3cd9a fix build 2023-06-02 10:11:07 +02:00
84440b37d1 fix feedbacks 2023-06-02 10:11:07 +02:00
ea6d17fb5c benchmark using snakemake 2023-06-02 10:11:07 +02:00
fcb663b844 trace_abbs and dump path 2023-06-02 10:11:07 +02:00
70bb1322c8 add graph feedback 2023-06-02 10:11:07 +02:00
7641857faa update input sizes, dump worstcase, benchmarking 2023-06-02 10:11:07 +02:00
3ec0f91195 remove address translations, extend plots 2023-06-02 10:11:07 +02:00
e2d89f3fc3 add hists to plot script 2023-06-02 10:11:07 +02:00
6f65e1fadc speed up random generation 2023-06-02 10:11:07 +02:00
ebb45e564c write out times over time 2023-06-02 10:11:07 +02:00
716388f54b add sytemstate sceduler, fuzz until time 2023-06-02 10:11:07 +02:00
32c245e619 re-add system state fuzzing 2023-06-02 10:11:07 +02:00
61aa829d60 add virtual edge to longest runs 2023-06-02 10:11:07 +02:00
4c7f312413 do not force generated inputs 2023-06-02 10:11:07 +02:00
68df4ab131 debug stuff 2023-06-02 10:11:07 +02:00
7c148781e6 random seeds, better plots 2023-06-02 10:11:07 +02:00
2d12d80184 add more benchmarks 2023-06-02 10:11:07 +02:00
95a639a78e exectime increase feedback 2023-06-02 10:11:07 +02:00
fd85ae8ae8 rename bin, allow random fuzzing 2023-06-02 10:11:07 +02:00
f8fae814ec benchmark with duration 2023-06-02 10:11:07 +02:00
775364340a add benchmark scripts 2023-06-02 10:11:07 +02:00
6ea42839e7 add systemstate feature and dump times 2023-06-02 10:11:07 +02:00
42b39baef4 fix multicore build 2023-06-02 10:11:07 +02:00
961fc628bf ignore artifacts 2023-06-02 10:11:07 +02:00
2b70a3b335 minimal changes 2023-06-02 10:11:07 +02:00
0d1d215e2d add interrupt injection 2023-06-02 10:11:07 +02:00
f6f367ae6d input length and read input pointer 2023-06-02 10:11:07 +02:00
72d074cebf draft: add graph feedback 2023-06-02 10:11:07 +02:00
12af0b592b add TimeMaximizerCorpusScheduler 2023-06-02 10:11:07 +02:00
a2b9ed6cbc add last api callsite to system state 2023-06-02 10:11:07 +02:00
2887349923 libafl_qemu: add jmp instrumentation 2023-06-02 10:11:07 +02:00
36049011cf WIP: add systemstate tracking 2023-06-02 10:11:07 +02:00
57c5a49fc7 add arguments 2023-06-02 10:11:07 +02:00
6fa04cfee7 get time from ClockTimeFeedback 2023-06-02 10:11:07 +02:00
0ff6d32550 fixup 2023-06-02 10:11:07 +02:00
cc6580eaab WIP: port fret 2023-06-02 10:11:07 +02:00
Dimitri Kokkonis
89876f2d89
Use listings for baby_fuzzer book chapter (#1289)
* Clarify setup steps for the baby fuzzer

Specifically:
- Explicitly mention that the dependency path must point to a specific
  directory in the cloned repo (and not the root directory)
- Explicitly mention how to manually trigger the panic in the harness
  for testing purposes

* Clean up documentation on the baby fuzzer

Since the baby fuzzer chapter of the documentation is done in a
"tutorial", step-by-step fashion, it would be nice to be able to see
where exactly new lines have to be placed in the existing code. To that
end, the code used in the tutorial is moved to snippets (as is done in
the Rust Book), as it allows for much more convenient maintenance of the
snippets, as well as easy hiding of the non-important code on any given
snippet.

Furthermore, a few minor fixes are applied; a typo on a comment and a
missing unsafe block.

* Fix code snippet attributes for baby fuzzer

Specifically:
- Remove unnecessary `compile_fail` attribute
- Add `ignore` attribute to the snippets of the complete baby fuzzer. As
  explained in [#1290], it is expected for the baby fuzzer to return a
  non-0 exit code, so this should not trigger a failure during `mdbook
  test`.

* Fix CLI snippet language

For CLI snippets, the "language" should be set to `console`.

* Remove nested safe block in baby_fuzzer listings
2023-05-24 12:18:26 +02:00
Rowan Hart
5a6d683fed
Add an observer for COUNTERS_MAPS for 8-bit SanCov (#1283)
* Add sancov multimap observer to sancov_8bit target

* Undo autofmt of Cargo.toml

* Fix formatting

* Fix import errors under no-default-features, add Safety to counters_maps_observer

* Make observer function no_mangle to allow it to easily be used in a staticlib crate

* Make clippy happy by using export_name instead of no_mangle

* Add observers feature flag and hide counters maps observer behind it

* Fix formatting
2023-05-23 23:31:07 +02:00
Dongjia "toka" Zhang
aa6d331110
Fix CI (#1292) 2023-05-23 18:07:54 +02:00
David CARLIER
dc82a53bec
minibsod dragonflybsd's portage (#1287) 2023-05-22 13:13:07 +02:00
Dongjia "toka" Zhang
2be9686a80
Fix clippy (#1288) 2023-05-22 13:12:43 +02:00
David CARLIER
b9b70b0d51
minibsod: generate_minibsod further memory maps data for freebsd. (#1285) 2023-05-22 12:43:31 +02:00
David CARLIER
0d7d52decf
afl_cc fix build for LLVM 17 (#1286) 2023-05-22 12:42:56 +02:00
Dongjia "toka" Zhang
6f21cb3848
Bump to 0.10.1 (#1280)
* bmp

* remove DEBUG env var
2023-05-22 12:42:36 +02:00
Dongjia "toka" Zhang
cba9df9a28
Delete gitmodules (#1278)
* fix

* rm
2023-05-18 13:07:10 +02:00
WorksButNotTested
4029069640
Disable capstone when building qemu-afl-bridge for user-mode fuzzing (#1281)
Co-authored-by: Your Name <you@example.com>
2023-05-18 00:47:59 +02:00
Dongjia "toka" Zhang
52557aefdd
Fix #1276 2023-05-17 15:39:21 +02:00
Dongjia "toka" Zhang
3fd5671909
add (#1276) 2023-05-17 15:22:49 +02:00
Dongjia "toka" Zhang
66127d8492
Revert "Don't pass LLVM pass & its args during linking (#1274)" (#1275)
This reverts commit efc5756e3fac9ed3b82e6d82b770b578b47b42d0.
2023-05-17 14:53:17 +02:00
Dongjia "toka" Zhang
efc5756e3f
Don't pass LLVM pass & its args during linking (#1274) 2023-05-17 14:32:56 +02:00
Dongjia "toka" Zhang
b7c1591b00
Linking arguments for LLVM passes (#1273) 2023-05-17 13:53:02 +02:00
Dominik Maier
20f8cb10eb
Revert "Automatically add the comment about executions when a new PR triggers (#1270)" (#1271)
This reverts commit d2e4b9f206307db0898caac707c0d2687079bfb1.
2023-05-17 02:48:15 +02:00
ToSeven
d2e4b9f206
Automatically add the comment about executions when a new PR triggers (#1270)
Co-authored-by: toseven <Byone.heng@gmail.com>
2023-05-17 00:30:15 +02:00
Arpan Kapoor
6d2284d8b9
Allow configuring timeout for CommandExecutor (#1269)
* Allow configuring timeout for CommandExecutor

* import Duration on windows as well

* fix example fuzzers
2023-05-15 12:59:06 +02:00
Addison Crump
1da621456f
Add check for if mutation succeeded, else skip processing (#1265) 2023-05-14 12:03:25 +02:00
ToSeven
659e91fb68
Fix performance regression detection in CI #1248 (#1259)
Co-authored-by: toseven <Byone.heng@gmail.com>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
2023-05-14 12:02:32 +02:00
Dongjia "toka" Zhang
b9a540561b
Don't add llvm pass args when there're no passes & Don't pass -mllvm arguments when compiling asm files (#1266)
* f

* fix

* clp
2023-05-12 11:48:12 +02:00
intrigus-lgtm
91b10f8c40
LibAFL_qemu: Disable Capstone to fix build issues on some distributions (#1263) 2023-05-11 09:52:57 +02:00
Kevin Phoenix
6883c776ef
Update pyo3 crate to 0.18.3 (#1255)
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-05-10 20:01:51 +02:00
Lei Zhu
a2719cf559
Add suggestion for arg & args (#1257)
* Add suggestion for arg & args

* Make fmt happy

* Explain @@

* Spotlight afl-fuzz

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-05-10 13:53:40 +02:00
Dominik Maier
0c7d42d28b
Clippy Debug fixes (#1261) 2023-05-09 17:05:51 +02:00
Dongjia "toka" Zhang
52d557aa8f
Ecofuzz Fix 2 (#1262)
* finally works

* f
2023-05-09 16:45:20 +02:00
Kevin Phoenix
be1d3da159
Add pyproject.toml to python bindings (#1239)
* Add pyproject.toml to python bindings

* Improve pyproject.toml

* Update CI pipeline to use pip to install python bindings
2023-05-09 13:23:28 +02:00
Dominik Maier
8bd18ef007
Fix latest clippy (#1258)
* Fix latest clippy

* oops needs alloc
2023-05-09 13:17:57 +02:00
Dongjia "toka" Zhang
fe8c06dd8f
Eco fuzz fix (#1253)
* f

* more fix

* aaaaa

* f

* fix

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-05-09 12:56:23 +02:00
Dongjia "toka" Zhang
721c02cd2c
Revert "Add a CI task that checks performance regression (#1248)" (#1254)
This reverts commit 6929c89b861faa4f382a51ab07e3ee1eb727df8c.
2023-05-04 20:03:06 +02:00
Addison Crump
a8e64be169
Alternative scheduled count strategy (#1252)
* early return generalization stage

* scheduled count

* aaa

* compile

* fix

* implement alternative scheduled count strategy

---------

Co-authored-by: toka <tokazerkje@outlook.com>
2023-05-04 13:15:28 +02:00
Tomas Duchac
53659f8a5c
Add file extension for clang in libafl_cc/build.rs (#1237)
* Add CLANG and CLANG_PP env vars

Add CLANG and CLANG_PP env variables for Windows. Resolves issue if clang and llvm-config are not in the same location.

* Just add clang".exe" for windows

User should have llvm-config and clang.exe clang++.exe in the same directory anyways.

* Ran cargo fmt
2023-05-04 11:52:51 +02:00
ToSeven
6929c89b86
Add a CI task that checks performance regression (#1248)
Co-authored-by: hengzh <byone.heng@gmail.com>
2023-05-04 11:51:42 +02:00
Arpan Kapoor
c8c5d89f33
Ignore 'Broken Pipe' if child process does not read all of stdin (#1244)
* Ignore 'Broken Pipe' if child process does not read all of stdin

* follow clippy suggestion
2023-05-03 15:45:27 +02:00
Dongjia "toka" Zhang
abd8efabd3
Add additional security sensitive functions for coverage accounting (#1246) 2023-05-03 15:45:07 +02:00
Dominik Maier
f9c74ed5d6
Update llvm for FreeBSD CI (#1243) 2023-05-03 11:45:49 +02:00
Dongjia "toka" Zhang
cf79d13d17
Change DumpToDiskStage's callback (#1242) 2023-05-03 11:16:53 +02:00
Dongjia "toka" Zhang
5b02fb420d
Fix CI (#1241) 2023-05-02 17:38:56 +02:00
Dongjia "toka" Zhang
c8fad7833d
Use InMemoryOnDiskCorpus in fuzzbench fuzzer (#1240)
* in memory

* f

* aaa

* nn
2023-05-02 15:18:17 +02:00
Michael Rodler
95d1069393
Use sancov_8bit.rs for OwnedMutSlice (#1235)
Co-authored-by: Michael Rodler <mrodler@amazon.de>
2023-05-02 14:41:33 +02:00
Dongjia "toka" Zhang
b2f9e23975
Fix double crash for solutions with the same filename (#1232) (#1236)
* fix

* mre

* why delete it???

* fmt

* clp

* comment
2023-04-29 23:42:51 +02:00
van Hauser
8ff8ae41f1
switch fuzzbench to FAST schedule (#1233) 2023-04-27 11:11:53 +02:00
Dongjia "toka" Zhang
dde7bc9b5c
Dump Call Graph (#1230)
* call graph

* nl

* typo
2023-04-26 20:22:57 +02:00
ToSeven
f248a061ef
add the version information of fuzzers in the UI (#1224)
Co-authored-by: toseven <Byone.heng@gmail.com>
2023-04-26 16:52:21 +02:00
Dongjia "toka" Zhang
037b9551ea
Fix #1228 (#1229) 2023-04-26 12:00:36 +02:00
Dongjia "toka" Zhang
eab7c32e9f
Dump whole program's CFG pass (#1226)
* skelton

* pass

* compiles

* python

* optional pass

* rev

* chg
2023-04-24 14:57:24 +02:00
Andrea Fioraldi
8ade809588
Centralized Testcase evaluation EventManager (#1216)
* template

* moar

* merge

* compiles

* fuzzer

* forward event newtestcase

* clippy
2023-04-24 11:38:55 +02:00
Dongjia "toka" Zhang
39c0a2040b
Fix CI (#1225) 2023-04-20 19:04:31 +02:00
Dongjia "toka" Zhang
e2f4e83890
Fix CommandExecutor type params (#1222) 2023-04-20 16:34:41 +02:00
Dongjia "toka" Zhang
fc23782dc3
Fix is_valid on Windows (#1217)
* git add -u

* fix cfg

* pub

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-04-18 12:15:47 +02:00
Dominik Maier
96e24d1c8b
Move Input loading and dumping APIs from Testcase to Corpus (#1201)
* Less allocatiosn for filenames

* clippy for wasm fuzzer

* Reworked filename and rename APIs

* python, clippy

* fmt

* More cleanup, fixed metadata location

* clippy

* fix fuzzbench_text / cached len, invert parameters (state first)

* clippy

* oops

* Caching for paths

* simplified, fixed

* no_std

* cached_len

* Nider API for input getting
2023-04-18 12:14:49 +02:00
Andrea Fioraldi
fd68c8a81f
Batch mode timeouts (Linux only ATM) (#1193)
* batch mode timeouts for linux

* batch_mode is linux only atm

* fix

* fix

* fix

* imports

* winfix

* more fix

* winfix

* fix

* fix

* fix

* fix

* clippy

* fix macos

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-04-17 18:16:44 +02:00
Andrea Fioraldi
fafa27a7e9
serial_test as normal optional dep enabled with std (#1215)
* sertial_test as a std and test only dependency

* serial_test as normal optional dep enabled with std
2023-04-17 18:16:29 +02:00
Dominik Maier
c881dc996d
Example fuzzers with less UB (#1212)
* Example fuzzers with even less UB

* more less ub, fixes

* unused dep
2023-04-16 14:29:41 +02:00
Dominik Maier
cdd3d8ace0
Tuneable stage with per-seed timeout (#1209)
* Tunable mutations with timeouts

* fix

* fmt

* Introspection fix
2023-04-13 15:30:47 +02:00
Langston Barrett
863a6b8b7c
Mark buffer_{self_,}copy as unsafe, don't export them (#1207) 2023-04-12 17:42:16 +02:00
Langston Barrett
1b9ffcec74
LibAFL: LLMP manager docstring cleanup (#1208) 2023-04-12 17:40:16 +02:00
Langston Barrett
aa3f126100
LibAFL_qemu: Return errors from Emulator::new instead of asserting (#1197)
* qemu: Return errors from Emulator::new instead of asserting

Libraries should not `assert!` except in cases of unrecoverable (library)
programmer error. These errors are all potentially recoverable, and aren't
internal errors in `libafl_qemu` itself.

* Respond to review comments
2023-04-09 21:27:27 +02:00
Elsa Granger
21ee8d2cae
Fix on_remove of MinimizerScheduler (#1161)
* Fix cursor not step

* Update ref_cnt after remove

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
2023-04-08 16:58:25 +02:00
Vincent
b519d24981
Fix Testcase renaming on disk (#1191)
* fix set_filename

* use ? quantifier instead of expect

* fix clippy

* cargo fmt

* add rename old file to new file logic

* add cfg feature std

* add no_std set_filename

* fix create and remove lockfile logic

* fix cargo fmt

* remove unused import

* cargo fmt

* fix clippy

* fix lock filecondition

* remove useless import

* fix path

* revert fuzzer Makefile.toml

* fix fmt

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-04-07 02:36:08 +02:00
Dominik Maier
f73e2006fc
Removed more new_ (#1200) 2023-04-07 02:34:28 +02:00
Andrea Fioraldi
0f633962ff
Bump to 0.10.0 (#1156)
* Bump to 0.10.0

* fix

* Fix CI

* Fix copyright

* fmt

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-04-05 17:49:53 +02:00
Felipe Baltor
6523341c4d
fix: output directory name; monitor output with println! (#1192) 2023-04-05 11:00:21 +02:00
Dominik Maier
0d446bab20
Updated dependencies (#1174)
* Updated deps

* win

* Revert "win"

This reverts commit a6dfd95f1c63a9471659481d92c5cbc480af6360.

* revert win

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-04-04 22:58:00 +02:00
Andrey Fedotov
807a534121
Use observers to handle crashes in run_target for TimeoutForkserverExecutor (#1189)
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-04-04 19:17:48 +02:00
Andrey Fedotov
1bd21509af
CASR deduplication for StacktraceObservers (#1184)
* Implement CasrAsanBacktraceObserver for dedupe crashes using libCASR and ASAN reports.

* Use casr observer with forkserver executor

* Add casr deduplication for AsanBacktraceObserver

* Add casr deduplication for BacktraceObserver

* Add Stacktrace filtering

* Move init_ignored_frames to constructors

* Add go ignore regexps for BacktraceObservers

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-04-04 19:03:11 +02:00
Dongjia "toka" Zhang
ccd1211cd2
Remove qemu_arm_launcher test from CI (#1179)
* remove qemu arm

* trigger all

* debug

* revert

* api chg

* fix

* try

* debug

* remove qemu

* no_std

* Update build_and_test.yml

* llvm16

* revert z3

* macos

* fix

* remove test

* don't grep on mac

* fix

* ok

* f

* f

* f

* aaa
2023-04-04 14:49:58 +02:00
LiuZhihong
590d3655cd
add readme documentation description about the tui feature (#1198) 2023-04-04 10:34:22 +02:00
ToSeven
ea512f70f8
Fix a build error in baby_fuzzer_minimizing (#1195)
Co-authored-by: toseven <Byone.heng@gmail.com>
2023-04-03 13:38:11 +02:00
Andrea Fioraldi
a01863696e
Implement restarting without serializing the corpus (#1182)
* Restart without serializing state option

* libfuzzer libpng example

* clippy

* libfuzzer_libpng_norestart

* fix
2023-03-30 14:35:58 +02:00
Dongjia "toka" Zhang
702f163c13
Fix #1181 2023-03-26 17:37:44 +09:00
Dongjia "toka" Zhang
0c9933c3e4
Remove libfuzzer_stb_image_sugar for now (#1177)
* a

* better check

* slow
2023-03-24 02:41:15 +09:00
Dongjia "toka" Zhang
ae2caff990
Revert "Fix testcase set_filename (#1092)" (#1175)
This reverts commit a659dd821c484c4e0524d0a2bf88023dae5e259b.
2023-03-24 01:53:42 +09:00
Dominik Maier
620b2861e3
Fix libafl_qemu testcase (#1173)
* Fix libafl_qemu testcase

* stb?

* Undo change for stb
2023-03-23 15:15:54 +01:00
Dominik Maier
66b2867ba2
Fix example fuzzers (#1171)
* Fix example fuzzers

* fmt
2023-03-23 11:02:18 +01:00
Vincent
a659dd821c
Fix testcase set_filename (#1092)
* fix set_filename

* use ? quantifier instead of expect

* fix clippy

* cargo fmt

* add rename old file to new file logic

* add cfg feature std

* add no_std set_filename

* fix create and remove lockfile logic

* fix cargo fmt

* remove unused import

* cargo fmt

* fix clippy

* fix lock filecondition

* remove useless import

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
2023-03-23 00:21:07 +01:00
David CARLIER
c9ea6ee6aa
core_affinity: freebsd constants are included in libc now. (#1170) 2023-03-23 00:11:02 +01:00
Dominik Maier
76e4f6031d
Fmt, no_std fixes (#1167)
* Fmt, no_std fixes

* push-stage fix
2023-03-23 00:10:01 +01:00
Aritra Mallick
02c6cab744
Link to mdbook book, not github (fixes #1137) (#1168) 2023-03-23 00:08:31 +01:00
Arpan Kapoor
d98384e582
Fix SimplePrintingMonitor (#1164) 2023-03-22 15:39:07 +01:00
Dominik Maier
3f7d35bfdc
Install libz3-dev in CI (#1163)
* install z3 in CI

* Update logics.rs

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-03-22 15:38:33 +01:00
v1ce0ye
7c514c3669
ix UB in baby_fuzzer_grimoire (#1166) 2023-03-22 15:37:57 +01:00
Vincent
38ea17b426
libafl_frida for Linux executables (#1117)
* add frida_executable_libpng

* fix makefile

* fix README.md

* remove author from Cargo.toml

* fix fuzzer

* fix fuzzer

* fix Makefile

* fix linter

* fix clang-format-13

* unsupport mac os

* fix build_and_test_fuzzers

* fix cargo fmt

* cargo fmt

* add safer libc_start_main

* fix call rax addr

* fix frida

* fix cargo fmt

* fix metadata() to metadata_map()

* fix toml

* fix maxmapfeedback
2023-03-22 15:18:21 +01:00
Alexander Zhang
c9a78f154b
Remove duplicate lines in attributes (#1165) 2023-03-22 19:08:32 +09:00
Dominik Maier
8f8e74d670
Don't build z3 from source by default (and add static_z3 feature) (#1160)
* Reduce build times by using preinstaled z3

* fix env order

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-03-20 10:04:27 +01:00
Dominik Maier
f4f23de32b
Fix fuzzers after HasTestcase (#1123) (#1162)
* Fix fuzzers after HasTestcase (#1123)

* Make the trait a trait

* Implement HasTestcase for Corpora

* fix

* fix

* a

* a

* fix

* wasm32

* a

* f

* f

* aa

---------

Co-authored-by: tokatoka <tokazerkje@outlook.com>
2023-03-19 11:58:32 +09:00
Dominik Maier
bbe4e85768
Removed new_ from constructors that don't need it (API consistency) (#1159)
* Removed new_ from constructors that don't need it (API consistency)

* un-change python bindings
2023-03-17 17:02:21 +01:00
Matheus Baptistella
104c170ade
Use the new metadata() function in more places (#1155) 2023-03-17 16:03:57 +01:00
David CARLIER
fd95560512
Check for the presence of clang frontends. (#1158)
* checks the presence of clang frontends.

close GH-1149.

* fix clippy complaints
2023-03-17 16:00:54 +01:00
Dongjia "toka" Zhang
d6ee2dbe12
IfStage (#1157)
* macro

* bracket

* IfStage

* remove macro

* revert
2023-03-17 23:25:01 +09:00
Dongjia "toka" Zhang
8245c7eda9
Logic stages (#1148)
* IfStage

* fmt clp

* constructor

* fmt

* else

* fmt

* while logic

* delete skippable

* fmt

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-03-17 10:21:51 +01:00
Dominik Maier
306cdcd800
Frida: Fix Android build (#1154)
* update android version

* fix android build some more
2023-03-16 16:26:22 +01:00
Dominik Maier
b9970cbdac
Implement From<CorpusId> for usize (#1152) 2023-03-16 16:12:00 +01:00
Dominik Maier
a351e7a509
Frida: fix aarch64 build (#1153) 2023-03-16 16:11:44 +01:00
Dongjia "toka" Zhang
08fe6ab791 Fix mutator slowdown (#1138)
* perf stat

* fix except swap

* swap

* fix

* reveral-based byte swap

* Revert "reveral-based byte swap"

This reverts commit 2bc9609ece47fd4e8f6d96862f8ad3fb77f11aec.

* no introspection

* clp fmt

* change rand_range to have at least 1 length

* don't use modulo

---------

Co-authored-by: Addison Crump <addison.crump@cispa.de>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
2023-03-16 15:58:03 +01:00
Dominik Maier
3c331e5a9b
Create SchedulerTestcaseMetadata if it doesn't exist (#1151)
* Create SchedulerTestcaseMetadata if it doesn't exist

* reset symcc
2023-03-16 14:29:40 +01:00
Dongjia "toka" Zhang
51bc1d0328
Fix infinite calibration (#1147)
* Fix infinite calibration

* fmt

* fix

* fix
2023-03-15 22:19:23 +09:00
Dongjia "toka" Zhang
6c98945fc3
Fix fuzzbench_forkserver 2023-03-15 13:32:04 +09:00
Dongjia "toka" Zhang
0fa815f2b8
Rename MetaData to Metadata 2023-03-14 23:57:55 +09:00
Matheus Baptistella
c38405ef83
Shorthand functions to get typed metadata, renamed metatdata -> metadata_map (#1123)
* Created macro to get the metadata form State and Testcase

* Expanded the macros for mutable, or not, State and Testcase metadata

* Created functions on traits HasMetadata and HasNamedMetadatato get, mutable or not, metadata

* Created the functions to get metadata

* Added #[inline] attribute and renamed the functions

* Renamed the functions and added #[inline] attribute

* Temporarily added testcase() function

* Added testcase() function

* Changed Ref import to core::cell:Ref

* Added testcase_mut() and renamed occurences of metadata() and metadata_mut()

* Renamed more occurences

* Renamed the metadata() on impl HasMetadata for NopState

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-03-14 14:24:33 +01:00
Dominik Maier
8dfdee6fce
Fix UB for differential map observer example (#1140)
* Fix UB for differential map observer example

* clippy

* undo submodule foo
2023-03-14 13:50:50 +01:00
Dongjia "toka" Zhang
460787196a
Update README.md (#1142) 2023-03-14 02:09:39 +09:00
Dominik Maier
6894a37ceb
Added Truncate trait (#1141) 2023-03-13 17:34:58 +01:00
Dongjia "toka" Zhang
44b798c07e
AFL++ RedQueen (#1087)
* step 1

* step 2

* Vec

* comment

* Observer

* tmp

* TaintedTracingStage

* more

* more

* more

* Idea

* more

* more

* mmmmmore

* moremoremore

* more

* all

* clp

* comment

* core

* push temporary debug change

* note for myself

* working

* rename to AFLCmplogTracingStage

* rename

* revert fuzzers' change
2023-03-13 17:34:16 +01:00
Andrea Fioraldi
e8d99b9975
Fix capsone mode in LibAFL QEMU (#1136)
* Use regex feature in libafl_qemu

* wip

* set mode for arm reading pc

* fixes
2023-03-12 23:29:55 +01:00
Addison Crump
786af9f6a9
resolve zero-sized allocation in swap diff fuzzer (#1139) 2023-03-12 23:24:22 +01:00
lazymio
b72bf55555
Add From BytesInput trait for Vec<u8> (#1135)
* Allow take the ownership of the BytesInput

* Add must_use as told

* Implement From&Into for better interoperability

* Format code

* Remove into_bytes

* Remove From<&BytesInput> to avoid misuse
2023-03-09 02:12:09 +01:00
Dongjia "toka" Zhang
4d778dd64d
Fix fuzz_level related thing, separate on_replace/on_remove from Scheduler & various fixes (#1119)
* delete HasFuzzedCorpusId

* more

* fmt clp

* aa

* fixing

* delete

* a

* append parent id when Objective

* add HasCorpus inprocss executor

* ecofuzz, delete was_fuzzed, update fuzz_level

* fix

* RemovableScheduler for Tunable, Queue, Weighted

* clp

* no std

* import

* on_execution

* fix

* win

* fmt

* fix

* revert to on_evaluation and propogate in the accounting scheduler

* fix

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-03-08 19:43:32 +01:00
Dominik Maier
2ed6583041
CI: Run miri tests (#1130)
* Fixes/ignores for miri support

* linux

* fix doctest for miri

* fix docs

* fix UB in baby_fuzzer

* no custom allocator in miri
2023-03-08 19:21:17 +01:00
Dominik Maier
e8838ebebe
Safer EoP handling (#1128) 2023-03-08 00:33:55 +01:00
Andrea Fioraldi
3ac439b345
Use regex feature in libafl_qemu (#1127) 2023-03-07 15:18:50 +01:00
Andrea Fioraldi
20c32316eb
Define custom collectors for QemuCallTracerHelper (#1099)
* Define custom collectors for QemuCallTracerHelper and create OnCrashBacktraceCollector

* fmt

* clippy
2023-03-07 13:16:51 +01:00
Andrea Fioraldi
3ffec79a17
Increase LLMP clients timeout to 5 min (#1126)
* LLMP client access fast path

* Increase LLMP client timeoit to 5min

---------

Co-authored-by: Your Name <you@example.com>
2023-03-07 11:47:45 +01:00
Marco Cavenati
b96e194812
Improve find_llvm on MacOS (#1124)
By looking for explicitly versioned Homebrew formulae for LLVM
2023-03-07 03:23:46 +01:00
Vincent
c8254dbd0e
Check CI result on cargo make test for available fuzzers (#1107)
* fix libfuzzer_libpng_cmin

* fix libfuzzer_libpng_ctx

* revert libfuzzer_libpng_cmin and check ci by grepping broker stdout result instead

* revert libfuzzer_libpng_ctx and check ci by grepping broker stdout result instead

* add check ci for fuzzers

* add check ci for fuzzers

* add check ci

* add fuzzbench test

* add validation for qemu fuzzer

---------

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-03-06 23:21:24 +09:00
Dongjia "toka" Zhang
9df95bd936
Use InMemoryCorpus in libfuzzer_libpng (#1125) 2023-03-05 23:23:42 +09:00
Elia Geretto
4f7b59aca4
Forward on_evaluation callback in MinimizerScheduler (#1122) 2023-03-05 22:00:32 +09:00
Langston Barrett
35e5b87188
Cargo feature to avoid regex dependency (#1102)
regex is a large crate, and is only used in a few specific spots. Users should
have the ability to avoid this transitive dependency if not using the features
in question.
2023-03-03 16:00:49 +01:00
Dongjia "toka" Zhang
5b4ae61cdd
SimpleLogger API improvements, printing to stdout, timestamps (#1109)
* log

* fix

* a

* rev

* remove

* 2 logger

* cfg std

* more

* more

* cf

* no_std

* features

* optional

* rename

* current_time()
2023-03-02 14:07:46 +01:00
van Hauser
2a3f1d68f5
fix weighting from hits (#1120) 2023-03-02 14:05:34 +01:00
Arpan Kapoor
672f4d1668
Use a different crash history in forkserver examples (#1118) 2023-03-02 19:49:50 +09:00
Dongjia "toka" Zhang
2a926f6546
Fix forkserver regression in LTO mode (#1114)
* regression

* fix

* chg

* Comment

* fmt
2023-03-02 10:17:20 +09:00
Andrea Fioraldi
452ca7a672
Implement EcoFuzz (#1115)
* Implement EcoFuzz

* clippy

* fix
2023-03-01 13:21:43 +01:00
Dominik Maier
df6271a0f3
Windows fix (#1116)
* Windows fix

* fix fix
2023-03-01 00:49:11 +01:00
Dominik Maier
31357aa7e2
Track parent testcase id, tuneable stage probabilistic settings (#1081)
* Added local event handlers

* clippy

* move tuned mutator to pow2

* Tunable updates

* parent ids

* no_std, etc

* windows

* remove local event manager handler

* maybe fix win

* win:

* win docs

* docs

* ASAN -> ASan
2023-02-28 16:36:04 +01:00
Dongjia "toka" Zhang
3e7322e395
Remove unnecessary check in calibration stage (#1111)
* save

* fix
2023-02-28 15:41:17 +01:00
Dominik Maier
fbe8cce1b8
Real OnDiskCorpus (#1096)
* Real OnDiskCorpus

* clippy

* python

* docs

* clippy

* docs

* move to reuse cachedinmem corpus

* fmt
2023-02-28 15:41:05 +01:00
Andrea Fioraldi
d36296c654
Fix llmp CliendId search (#1112) 2023-02-28 15:20:24 +01:00
Andrea Fioraldi
dc800f0814
on_evaluation Scheduler method (#1106)
* add on evaluation hook in schedulers

* on_evaluation for WeightedScheduler

* fix PowerQueueScheduler

* fix fuzzers

* upd qemu

* tests

* upd
2023-02-28 11:33:26 +01:00
Dongjia "toka" Zhang
59bf118a5a
Clippy (#1105) 2023-02-28 01:08:19 +09:00
Addison Crump
0727c80347
Add example for WASM (#1093)
* add baby_fuzzer for wasm targets

* elaborate in README

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-02-26 17:02:22 +01:00
Dongjia "toka" Zhang
c0f229ec23
Add UsesObserver to allow more generic MapFeedback (#1104)
* UsesObserver

* a

* more

* don't need these
2023-02-26 08:35:45 +01:00
clesmian
cf02553ea7
Cleanup forkserver exec builder (#1094)
* Don't use magic string but string constant

* Don't allow to specify multiple input files with different names

* Ensure that the file name for the current test case is unique for every fuzzer currently running

* Add note advising users to choose distinct names for the input file

* Move builder functions to more generic implementation to allow parse_afl_cmdline rewrite

* Rewrite parse_afl_cmdline to reduce code duplication

* Add remark to documentation regarding the program path

* Change behavior to allow the usage of actual AFL command lines, hopefully without breaking existing code

* Rustfmt

* Move generation of unique filename to fs

* Ensure default input filename for command executor is unique per fuzzing process

* Pass the input to the target via stdin, when no input file is specified

Previous solution of passing it via a standard file is useless, as the target does not know to read said file

* Rustfmt

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
2023-02-26 02:00:28 +01:00
Langston Barrett
3dbea91a63
Use Iterators as Generator (#1101)
Also, remove seemingly-unused generate_dummy method to enable this instance.

Also, add an adapter that creates an Iterator from a Generator.
2023-02-26 01:59:56 +01:00
Dongjia "toka" Zhang
95004aab7e
Update build_and_test.yml (#1103) 2023-02-25 15:12:59 +09:00
Dominik Maier
b3020d7296
Fix CoreId for Frida, FreeBSD (#1100) 2023-02-25 00:16:37 +01:00
Dominik Maier
672d25e5ac
LLMP Client timeouts, Exit broker when last client exits (#1057)
* Moving type definitions to transparent structs

* function to notify other side of exit

* docs

* Exmaple support windows now

* timeout fix

* Exiting after the last client quit

* inform about quits

* clippy

* clippy

* clean exits

* fix

* more unsafe

* fixes

* Move ClientId

* fix no_std

* Fix prometheus

* introduce Cores.trim()

* add always_track metadata

* docu

* add AlwaysUniqueMapFeedback

* rename to always_interesting

* return CoreId for Launcher

* CoreId as transparent tuple struct

* fix graceful exits for launcher

* Broker exits after launcher

* clippy

* Fix llmp eop race, introduce llmp shmem cache

* initialize cached page, clippy

* fix llmp_debug strings

* add error handling

* nicer error output

* More error handling convenience

* clippy

* fix macos example

* nits

* trying to add a logger

* no_std

* inline logger enabled

* fix windows, non-fork

* macos

* no_std docs

* clippy

* use ? instead of unwraps in example

* more logging

* docs
2023-02-24 11:50:42 +01:00
Dominik Maier
92842c8b04
Fix LLMP eop race, introduce LLMP ShMem cache (#1091)
* Fix llmp eop race, introduce llmp shmem cache

* initialize cached page, clippy

* fix llmp_debug strings

* add error handling

* nicer error output

* More error handling convenience

* clippy

* fix macos example

* nits

* trying to add a logger

* no_std

* inline logger enabled

* clippy
2023-02-24 10:28:21 +01:00
Mrmaxmeier
ff4e2f4192
Fix max input size for {CrossOverInsert,BytesInsertCopy}Mutator (#1097) 2023-02-24 00:07:54 +01:00
R. Elliott Childre
64a57ad3e3
Move bytecount to dev-dependencies (#1090)
It is only used in test code
2023-02-23 13:52:43 +01:00
Dongjia "toka" Zhang
9e88e5734e
Timeout executor cfg fix (#1088)
* fix

* fix

* fix from mac

* fix

* fix

* fix

* ?

* fix
2023-02-22 10:20:11 +09:00
Andrea Fioraldi
1b0cdab3e4
Use GuestAddr in QemuInstrumentationFilter (#1085)
* Use GuestAddr in QemuInstrumentationFilter

* fix types
2023-02-21 16:19:43 +01:00
Addison Crump
b7296db406
Fix exits which may cause double-free corruption (#1086) 2023-02-21 07:34:16 +09:00
Dongjia "toka" Zhang
20958a979f
Weak link token section (#1080)
* fix

* a
2023-02-17 10:38:46 +01:00
Addison Crump
bdac876dd4
Mutator sampling probability fixes (#1030)
* fixes for standard mutations

* more mutation updates for sampling probability, tests

* slight doc fix

* clippy gripe

* clippy fixes

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-02-16 17:29:57 +01:00
Michael Rodler
46b75747ef
Make sure input was loaded to avoid panic on unwrap in MutatedTransform (#1077)
* make sure input was loaded to avoid panic on unwrap
fixes issue #1059

* avoid unnecessary clone, avoid unnecessary branching

---------

Co-authored-by: Michael Rodler <m@mrodler.eu>
Co-authored-by: Addison Crump <addison.crump@cispa.de>
2023-02-16 17:29:33 +01:00
Addison Crump
f454d17482
CMin: don't add to map if it's the initial value (uninteresting) (#1078) 2023-02-16 17:28:49 +01:00
Dongjia "toka" Zhang
8bffd28b4c
fix (#1076) 2023-02-16 10:29:24 +09:00
Dongjia "toka" Zhang
e7ef6ae8b7
Forkserver: 1. Add mem barrier 2. Don't send the initial 4 bytes message when it uses dynamic map option only (#1073)
* fix

* Real fix

* a
2023-02-16 09:42:28 +09:00
Addison Crump
26aace6073
Fix grimoire when used with on_replace/on_remove (#1075) 2023-02-16 00:14:26 +01:00
Addison Crump
e42cd9c12f
Fixes for on_replace/on_remove and related for StdFuzzer and MapFeedback (#1067)
* scheduler replace fixes

* oops, no-std

* add

* changes on the fuzzers

* move map feedback history updates to append_metadata

* fixes for python bindings

* learn to clippy

* fix for fuzzer add_input

* clippy fixes for frida

* additional powersched differences

* corrections for bitmap_size

* off-by-one

* I live in a prison of my own creation and clippy is the warden

* clear the novelties map for the situation where is_interesting is invoked, but not append_metadata

---------

Co-authored-by: tokatoka <tokazerkje@outlook.com>
2023-02-15 17:04:18 +01:00
radl97
e61ac10656
Fix StdErrObserver not implementing needed traits (#1072) 2023-02-15 12:39:23 +01:00
Vincent
71d367af30
TimeObserver: Use Instant::now instead of Duration (#1064)
* Use Instant::now instead of duration

* Use Some

* add custom serde for Instant

* fix linter

* only enable TimeFeedback when std flag is enabled

* fix typo

* fix linter std

* cargo fmt

* allow clippy::trivially_copy_pass_by_ref on custom serde serialize function

* allow TimeObserver and Timefeedback for no_std

* cargo fmt

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-02-15 12:38:25 +01:00
David CARLIER
fb0d3b07ea
MiniBSoD: solaris on amd64 implementations (#1068) 2023-02-15 12:27:45 +01:00
Andrea Fioraldi
cf0a0a0698
Add stub lib for fuzzbench (#1074) 2023-02-15 12:05:10 +01:00
Elsa Granger
44b69666da
Fix fuzzbench build (#1004)
* Fix -z,defs handle

* Add libfuzzer_no_link_main option in libfuzzer

* Use libfuzzer_no_link_main for fuzzbench

* no_link_main feature

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-02-15 11:58:32 +01:00
Andrea Fioraldi
f8a4a020e8
QEMU: do not crash in helpers pre and post execs (#1065)
* QEMU: do not crash in helpers pre and post execs

* comma
2023-02-14 13:35:20 +01:00
Fabian Freyer
53dba5f49d
Use the log facade instead of println (#1060)
* switched a couple of println / dbg statements to use log crate
* Use pyo3-log for logging in python bindings
2023-02-14 10:01:51 +01:00
Dongjia "toka" Zhang
30b51bb810
Remove unused dependencies (#1069) 2023-02-14 09:02:18 +01:00
Dongjia "toka" Zhang
bd2de16b4e
Colorization stage (#1039)
* type_replace

* separate

* more

* heap

* comment

* f

* fix

* clp

* need rev

* comment

* ColorizationTracingStage

* get_raw_map_hash_run

* process_execution

* metadat

* unused TE

* resolve type errors

* remove colorizationtracingstage

* Finally compiles

* clp

* fmt

* a few debug println

* revert

* fix
2023-02-13 15:02:19 +09:00
Dongjia "toka" Zhang
a74e5da268
Revert FridaInstrumentationHelper changes (#1062)
* Revert "Send stability in calibration stage & FridaInstrumentationHelper retunrs Result<Self, Error> (#1056)"

This reverts commit 4d78878c02846b2c0a49686cd05cccadd2b0ac72.

* fux

* poc

* revert
2023-02-13 10:02:26 +09:00
Dongjia "toka" Zhang
4d78878c02
Send stability in calibration stage & FridaInstrumentationHelper retunrs Result<Self, Error> (#1056)
* fix

* fix

* clippy
2023-02-13 05:35:09 +09:00
Dongjia "toka" Zhang
b7a0b823c6
Fix frida_gdiplus (#1045)
* fix

* I don't like prelude

* clp

* cargo make test

* poc

* one to_vec()

* fix?

* del

* fix
2023-02-08 00:20:38 +09:00
Andrea Fioraldi
0173d722c6
Fix typo in directory visiting (#1050) 2023-02-07 10:47:36 +01:00
Dongjia "toka" Zhang
30e296968b
Rename LLMP Timeout message, increase timeout (#1048)
* rename

* Update llmp.rs

* Print every 30 seconds only

---------

Co-authored-by: Dominik Maier <dmnk@google.com>
2023-02-06 16:42:14 +01:00
Langston Barrett
4e15be182e
Increase default initial capacity of NewHashFeedback (#1049) 2023-02-06 15:50:26 +01:00
R. Elliott Childre
4d5a759955
Update deps for libafl (#1042)
Reduces total number of packages from 577 to 571 on building with:
`cargo +nightly build --workspace --all-features`

* ahash 0.7 -> 0.8
  * Move `AHasher::new_with_keys` to `RandomState::with_seeds` given the
    recommendation from: aHash maintainer:
    https://github.com/tkaitchuck/aHash/issues/132#issuecomment-1288207069

* bindgen: 0.61 -> 0.63

* c2rust-bitfields: 0.3 -> 0.17

* criterion: 0.3 -> 0.4

* crossterm: 0.25 -> 0.26

* dynasmrt: 1.2 -> 2

* goblin: 0.5.3 -> 0.6

* hashbrown: 0.12 -> 0.13

* nix: 0.25 -> 0.26
  * The `addr` arg of `mmap` is now of type `Option<NonZeroUsize>`
  * The `length` arg of `mmap` is now of type `NonZeroUsize`
  * Requires updating implementers to update `nix` as well

* prometheus-client: 0.18.0 -> 0.19
  * Do not box metrics
  * Gauges (a majority of the LibAFL metrics) are now i64 types so there
    is a small chance of overflow, with the u64 values that LibAFL
    tracks, but unlikely to be problematic.
 * Keep `exec_rate` as a floating point value

* serial_test: 0.8 -> 1

* typed-builder: 0.10.0 -> 0.12

* windows: 0.42.0 -> 0.44

Co-authored-by: Dominik Maier <domenukk@gmail.com>
2023-02-06 12:24:42 +01:00
Langston Barrett
e75f65080e
libafl: with_capacity method for NewHashFeedback (#1034)
Most of the time, fuzzing campaigns are reasonably long. Accordingly, when
using `NewHashFeedback`, you might have good reason to believe that you'll
find thousands (or more) different observations with different hashes. When
the `HashSet` outgrows its capacity, it can cause reallocation, which is slow.
See the following link for more details:

https://doc.rust-lang.org/std/vec/struct.Vec.html#capacity-and-reallocation
2023-02-06 01:44:57 +01:00
R. Elliott Childre
5d76707ede
Bump deps and fix Clippy warns in example fuzzers (#1043)
* Mostly addressing changing the `uninlined_format_args` lint which was
  changed to warn-by-default in rust clippy 1.67

* Bump dependencies:
  bindgen:  0.61 -> 0.63
  cc:       1.0 -> 1.0.42 (Exclue versions w/incompat rayon dependency)
  clap:     3.x -> 4.0
  rangemap: 0.1 -> 1
  xz -> xz2:  move to updated version

* Add fallthrough default return to `LLVMFuzzerTestOneInput` in
  **/fuzz.c to prevent Clang's -Wreturn-type

* libafl_atheris: Improve POSIX compatibility and reduce warnings
  * Check for .dylib and .so libraries
  * `source` -> `.` for POSIX shells
  * install wheel into the venv to support newer Python packaging
    standards
  * `LDPRELOAD` -> `LD_PRELOAD`
2023-02-05 21:53:45 +01:00
Dominik Maier
48caffb802
Allow to load a list of files (#1044) 2023-02-05 21:22:58 +01:00
Andrea Fioraldi
eaf5ff9de0
Restart loading initial inputs even after a crash/timeout (#1040)
* Track initial inputs loading

* libfuzzer libpng

* fuzzbench

* fix no_std

* fix no_std

* clippy

* fuzzers
2023-02-03 11:56:47 +01:00
Dominik Maier
86ab682e5a
Readme: Add information about system mode QEMU (#1038) 2023-02-02 15:20:45 +01:00
Dongjia "toka" Zhang
db62c26eda
Remove unused imports (#1035)
* remove unused imports

* fmt
2023-02-01 12:26:56 +01:00
WorksButNotTested
71f106be20
Fix accidental breakage of non-AARCH64 systems (#1036)
Co-authored-by: Your Name <you@example.com>
2023-02-01 17:46:12 +09:00
WorksButNotTested
d0b4c39acd
Change to combine restoration prologue with coverage register spill (#1029)
Co-authored-by: Your Name <you@example.com>
2023-02-01 04:41:56 +01:00
Andrea Fioraldi
2a88a776bf
Fix frida Cargo.toml (#1033) 2023-01-31 14:05:07 +01:00
Andrea Fioraldi
b77c0b78cc
Fix readme position in qemu sys (#1032) 2023-01-31 13:40:28 +01:00
Dongjia "toka" Zhang
f9dd67b59b
Comment Fix 2023-01-31 19:40:58 +09:00
Dominik Maier
cc53da85fb
Remove {update,clear}_hash from ObserverWithHashField, add hasher (extending #1019) (#1028)
* libafl: Remove `{update,clear}_hash` from `ObserverWithHashField`

These methods aren't used by `NewHashFeedback`, so there's no compelling reason
to keep them in the interface. They preclude implementations of
`ObserverWithHashField` that calculcate a hash on-the-fly from a value. For
example, my use-case is to store the stdout of a process, and use
`NewHashFeedback` to only collect inputs that result in new messages on stdout.

Both of these methods are pretty suspicious to begin with - why should other
code be able to update the internal state of the observer? What are the
semantics of `update_hash`? If there are compelling reasons to keep these
methods, let's clarify their intent in the documentation.

* libafl: Return hash by value from `ObserverWithHashField`

This allows implementors of this trait to not store the hash, but rather to
compute it on-the-fly. Since `Option<u64>` is `Copy` (and quite small), and
this method is called once per execution of the target program, this is likely
to have negligible performance impact.

* libafl: Implement `ObserverWithHashField` for `ValueObserver`

This demonstrates the utility of the previous two commits. Now, `ValueObserver`
can be used with `NewHashFeedback`.

* Clippy, move to ahasher

* Oops :)

---------

Co-authored-by: Langston Barrett <langston.barrett@gmail.com>
2023-01-31 10:45:42 +01:00
Andrea Fioraldi
fdf579bcd5
Bump to 0.9.0 (#946)
* bump to 0.9.0

* fix libafl_tinyinst

* fix

---------

Co-authored-by: Dominik Maier <domenukk@gmail.com>
2023-01-31 10:42:01 +01:00
Dominik Maier
d73fb92ddf
Python CI (#1024)
* Python CI

* fix testcase

* fix yml

* Fixing test

* format python

* cleanup
2023-01-31 05:04:19 +01:00
Dominik Maier
fc8c92514f
Update README.md (#1027) 2023-01-31 04:43:27 +01:00
Dominik Maier
7c4acb3b22
Update README.md (#1026) 2023-01-31 04:34:01 +01:00
Dominik Maier
2cd3fb8fea
New Logo (#1025) 2023-01-31 04:32:47 +01:00
Dominik Maier
ff9208f107
Fixing python example (#1016)
* Fixing python example

* Fix python baby fuzzer

---------

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-01-30 20:03:07 +01:00
Fabian Freyer
afa506c0c8
libafl_frida: Allow compilation for iOS (#1023)
iOS does not have any TLS, so we don't need to keep track of it.
This allows compiling for the aarch64-apple-ios target.
2023-01-30 18:05:00 +01:00
Dominik Maier
33ddce2cea
Introduce MutatorId, Tuneable fixes (#1022)
* Add simpler APIs for TunableStage

* Make API usable

* Add TunableScheduledMutator APIs

* Introduce MutatorId

* More API

* Cleanup

* add sampling-based mutation scheduling

* reduce precision for sampling

* clippy
2023-01-30 18:04:42 +01:00
Dongjia "toka" Zhang
b927fc9b06
Add filename_path to MmapShMemProvider (#1014)
* change how it is named

* Add more comments

* more

* macOS 32bytes onlyu

* chg

* comment, fix
2023-01-26 00:53:37 +01:00
Dominik Maier
e5c220519e
LLMP Broker: timeouts for inactive clients (#1005)
* LLMP Timeouts

* Make broker timeouts optional

* fix warning

* fix warning
2023-01-25 12:03:23 +01:00
Andrea Fioraldi
92c0c5eeab
Fix second Forkserver Broken Pipe (#1013)
* Truncate at MAX_FILE

* AFL_MAP_SIZE

* todo
2023-01-24 20:50:56 +01:00
Addison Crump
00ec7e143c
fix for MapIndexesMetadata (#1008)
Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-01-23 15:08:33 +01:00
Dongjia "toka" Zhang
fe51286586
TinyInst Update (#968)
* tmp

* more

* save

* TODO

* fix

* update to tinyinst on crates

* dep

* fmt

* shmem done

* cpp fmt

* clp

* fmt

* why??

* ver

* more makefile.toml

* windows test

* Update build_and_test.yml

* fix

* a

* install

* fmt

* fix

* only macos and win

* more

* The order matters

* remove

* fmt

* chg

* typo

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-01-23 11:11:53 +01:00
David CARLIER
3b68399cc9
libafl_cc fixes for clang 16 (#1010)
None constant being deprecated, it is recommended
to use the std::nullopt_t type instead.
2023-01-23 11:00:42 +01:00
Andrea Fioraldi
7fd9ac0952
SyncFromBrokerStage to sync from a broker with a different Input type (#997)
* ConverterLlmpEventManager

* docs

* SyncFromBrokerStage

* fix

* separate InputConverter

* nautilus

* nautilus_sync

* send testcases

* upd nautilus

* meta

* fix

* clippy

* fix

* Update build_and_test.yml

* fix

* fix

* Use find_libpython

* ci

* upd qemu
2023-01-20 17:16:29 +01:00
WorksButNotTested
5cdb7f7b05
Improve AARCH64 performance (#989) 2023-01-18 13:56:17 +01:00
Addison Crump
ebc886032f
Fixes for multiple subtle bugs with grimoire, mutators, and state (#1001)
* fix multiple subtle bugs with grimoire, mutators, and state

* obey the clippy overlord

* grimoire: skip over token after splice

* remove extraneous length check
2023-01-18 13:53:31 +01:00
Paul Walker
333a51aeaa
StacktraceObserver speedup using unresolved backtrace (#1002)
We don't use the symbols anyway, and it makes the call *way* faster.
2023-01-18 13:51:42 +01:00
Andrea Fioraldi
3c8a00bc42
Fix qemu user (#1003) 2023-01-18 13:38:49 +01:00
Dongjia "toka" Zhang
1446692f02
Fix stability UI (#1000)
* Update mod.rs

* fmt
2023-01-18 19:00:50 +09:00
van Hauser
15c1c0fb5e
Avoid no-op in ByteRandMutator (#999) 2023-01-17 11:07:50 +01:00
Andrea Fioraldi
7cf7d545a6
Update QEMU and fix snapshot restore mem leak (#998)
* Update qemu

* Fix leak

* upd

* fmt
2023-01-17 11:06:31 +01:00
Dominik Maier
97e88af0c5
OnDiskCorpus: Write metadata by default, metadata gzip compression (#995)
* Write metadata by default

* fix fuzzers

* Cleanup, gzip feature

* Fix casing for ondisk corpus

* fix fmt, clippy

* clippy

* clippy for gdiplus fuzzer

* fmt
2023-01-13 01:07:36 +01:00
Addison Crump
28786c943a
Grimoire fixes (#993)
* fixup grimoire/generalisation, remove GeneralizedInput in favour of metadata

* additional cleanup

* transformable inputs to solve the grimoire problem

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

* clippy fix

* fixes for nautilus, python

* explicit inlining for reflexive impl

* fix for tutorial

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-01-13 01:07:21 +01:00
Andrea Fioraldi
ec84c71eae
Corpus maps (#947)
* hashmap corpus for inmemory

* corpus_btreemap feature

* CorpusId

* queue

* MinimizerScheduler::remove

* fixes

* continue

* keys

* working corpus

* ok

* weighted left

* wip

* port weighted scheduler

* it compiles

* doc

* fix mutators

* fix queue

* fix iter

* tests

* fix

* fix

* fix

* py

* clippy

* clippy

* clippy

* cmin

* fix

* fix

* fix

* clippy

* remove Corpus::random_id
2023-01-09 14:15:07 +01:00
biazo
3345727c94
fixing linking issue on qemu build (#990)
* fixing linking issue

* insteading of linking lib we dont use. just disable them

* fixing the keyutils problem

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2023-01-09 10:10:14 +01:00
Dominik Maier
17cb317429
Fix early drop for frida transformer (#992)
* Fix early drop for frida transformer

* clippy
2023-01-08 18:19:30 +01:00
Dominik Maier
159e6ea480
Fix Launcher for M1, fix frida_libpng harness compilation, fix CI (#987)
* Fix harness compilation for frida_libpng

* M1: Always use fast cores

* always ignore result

* seeing if manualy installing libunistring fixes wget

* seeing if manualy installing wget fixes it, instead

* un-remove comment
2023-01-05 14:26:20 +01:00
WorksButNotTested
266677bb88
FRIDA: Fix previous_pc constant (#988)
Co-authored-by: Your Name <you@example.com>
2023-01-05 12:20:06 +01:00
WorksButNotTested
f27ca843e1
FRIDA x64 performance improvements (#985)
Co-authored-by: Your Name <you@example.com>
2023-01-05 11:51:58 +01:00
Dominik Maier
1bb37e4b98
Book: Explain SymCC constraint solving (follow up on #980) (#986)
* Make the kind of solving more clear (follow up on #980)

* Update docs/src/advanced_features/concolic.md

Co-authored-by: julihoh <julihoh@users.noreply.github.com>

Co-authored-by: julihoh <julihoh@users.noreply.github.com>
2023-01-05 11:51:31 +01:00
hexcoder
7d412693c8
Book review (#980)
* docs review

* docs review

* docs review wording

* docs review wording

* docs review wording

* wording

* nits

* wording

* wording

* nits

* docs_review wording

* wording

* wording

* wording

* Wording

* wording

* nits

* Wording

* fix main naming for afl++

* update symcc

Co-authored-by: Dominik Maier <dmnk@google.com>
2023-01-04 15:21:08 +01:00
Dongjia "toka" Zhang
d2985c5b2e
Clippy fix (#978)
* fix

* mac

* clp
2023-01-02 20:35:41 +09:00
Sparrrgh
43425cf103
Correct MIPS register naming in libafl_qemu (#977) 2022-12-31 09:23:50 +01:00
Dongjia "toka" Zhang
9458549fef
Remove declare -A (#976)
* fix

* fix

* debug

* debug

* older version

* newer version

* fix

* unix?

* fix
2022-12-30 02:02:38 +09:00
Erwan Grelet
3e38862837
Forkserver example with forkserver.c (#726) (#973)
* forkserver: Add an API to setup the shared memory region for edge coverage

This is inspired from and meant to be similar to afl-cc's instrumentation.
Remove ! return type from __afl_start_forkserver as it returns in several cases.

* Add example fuzzer using LibAFL's forkserver

The fuzzer is instrumented with libafl_cc as well.

Co-authored-by: ergrelet <ergrelet@users.noreply.github.com>
2022-12-28 22:16:27 +01:00
Dongjia "toka" Zhang
676a149497
Update CorpusWeightTestcaseScore (#975)
* fix

* clp

* fmt
2022-12-28 22:16:08 +01:00
Patrick Gersch
2b092f40fa
SimpleMonitor optionally displays user_monitor stats (#970)
* Adding with_user_monitor() to SimpleMonitor

* Satisfy clippy

* Satisfy fmt and pylibafl

* Fix leading whitespace
2022-12-26 11:20:30 +01:00
Dongjia "toka" Zhang
476cb7e7dc
Frida Makefile.toml fix (#969)
* frida build script fix

* fix

* Update build_and_test.yml

* Update build_and_test.yml

* Update build_and_test.yml

* Update build_and_test.yml

* Update build_and_test.yml

* Update build_and_test.yml

* Update build_and_test.yml

* Update build_and_test.yml

* Update build_and_test.yml
2022-12-26 14:19:37 +09:00
Dominik Maier
75f12bd0eb
Remodelling Observers/Examples that rely on UB, API cleanups (#950)
* Tackling UB

* PtrMut -> MutPtr, moved mapobservers to non-UB

* QEMU fixes

* test fixes

* qemu

* Change all interfaces, fix all fuzzers

* fixes

* fix more fixes

* fmt

* fix qemu sugar

* fix some qemus

* atheris

* fmt

* more fmt

* most fmt

* more fix

* nyx fyx

* fix qemu

* clippy, fixes

* more fixes

* no unfix, only fix

* fix

* fix

* more clippy

* fixes

* ListObserver

* fmt, clippy

* fix qemu on arm

* update zlib target

* fix?

* fix

* added migration guide

* ignore doc

* fix symcc

* fix new win fuzzer

* Fixes, rename PTR_SIZE to PTR_NUM

* Try fix linking on win

* Trying to fix win linking

* more cov

* trying to fix win some more

* trying to fix mac

* trying to fix mac

* Fix tests

* Fix tests

* trying to fix win

* more mac

* giving up for windows

* fmt

* python3

* mac?

* undo windows tests
2022-12-24 14:20:44 +01:00
Dominik Maier
3a1e499d9d
Documentation fixes (#967)
* a few stylistic/grammar changes

* expression

* some wording and a different git clone command

The original `git clone` command did not work for me (permission denied).

* small wording changes

* review

* typo

* neutral

Co-authored-by: hexcoder <hexcoder-@users.noreply.github.com>
2022-12-21 12:44:42 +01:00
humpty99
de6ee8b161
Fix Nyx build script (#965)
due to the call to pushd on line 23 not being directed at packer, the wrong commit was being used and so that resulted in the wrong init.cpio.gz being generated which hangs when trying to run the libxml2 examples however using the right commit (86b159bafc0b2ba8feeaa8761a45b6201d34084f) fixes this problem.
2022-12-21 20:29:42 +09:00
Dongjia "toka" Zhang
d77d9d5f31
Frida: Make stalker.exclude() configurable from command line arguments (#956)
* remove exclude on windows

* linux x86_64

* option
2022-12-21 11:23:57 +01:00
Max Ammann
4d8b566a87
[Windows] Add libfuzzer example for windows with ASAN (#934)
* Add libfuzzer example for window with ASAN

* Fix formatting

* Add link

* Fix cpp format

* Skip windows fuzzers

* Fix format

* Fix testing fuzzer

* Fix taks name

Co-authored-by: Dongjia "toka" Zhang <tokazerkje@outlook.com>
Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
2022-12-21 10:41:58 +01:00
Dongjia "toka" Zhang
3c7dcac41d
Deduplicate crash handlers (#951)
* unix

* win

* std

* fmt

* more

* more

* win

* rename

* win

Co-authored-by: Dominik Maier <domenukk@gmail.com>
2022-12-21 10:41:18 +01:00
Dongjia "toka" Zhang
038732bd92
no mold (#964) 2022-12-21 13:27:14 +09:00
Dongjia "toka" Zhang
f1b25fed65
Fix CI (#955)
* fix

* fmt

* Update build_and_test.yml

* no nightly

* fix

* why

* Update build_and_test.yml

* a

* a

* ok

* linux only

* fmt

* Update build_and_test.yml

* fix
2022-12-21 11:12:54 +09:00
hexcoder
b0df0a26a1
Docs: grammar fix (#961) 2022-12-21 00:19:38 +01:00
Dominik Maier
7ed1ac9c9b
Additional SymCC Build Fixes (#954) 2022-12-20 00:37:19 +01:00
Dominik Maier
e56d5318e4
Fix SymCC build (#952) 2022-12-19 13:48:07 +01:00
Dominik Maier
ccf6cc708a
Windows clippy fixes (#948) 2022-12-18 12:35:30 +01:00
radl97
016a4c3778
Human readable execs & run/exec rounding fix (#936)
* Calculate run/exec statistics as float to solve rounding issues

* Fixup

* Clippy fixes

* Clippy fixes

* Change execs_per_sec() to return float per suggestions

* Monitors: Write 2 decimal floating-point for execs/sec

* Prettify exec/sec

* Formatting & fix copy pasta

* Pretty-print floats in monitor: use mega and kilo SI suffices

* prettify -> prettify_float, apply suggestion

* Clippy

* Fix prometheus client cannot handle float values yet

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
Co-authored-by: Dominik Maier <domenukk@gmail.com>
2022-12-16 23:46:33 +01:00
Sparrrgh
d04346c870
Add mips support for QemuTracerHelper (#941)
* Add mips support for QemuTracerHelper

* Formatting

Ran cargo +nightly fmt

* Removed unnecessary `any`

* Removed cfg guarding calls module

Co-authored-by: Andrea Fioraldi <andreafioraldi@gmail.com>
2022-12-14 17:45:54 +01:00
omergreen
664e87809e
libafl-frida: disable LibAFL's default features in Cargo.toml (#939)
* disable libafl's default features in libafl-frida

to allow users to disable default features while using stuff from libafl-frida

* Update Cargo.toml

Co-authored-by: Dominik Maier <domenukk@gmail.com>
2022-12-14 01:59:17 +01:00
Andrea Fioraldi
55e220f0e8
CI: diff with main and HEAD^ (#945) 2022-12-14 01:10:36 +01:00
Andrea Fioraldi
162de0ceaf
Fix CI diffing (#944)
* fix build_and_test_fuzzers to diff from origin/main

* fix fuzzbench_forkserver
2022-12-13 21:19:44 +01:00
Ao Li
9e4a0513c6
Gramatron: Fix a typo in gnf_converter.py (#942)
This patch addresses the issue in #879, which fixes a typo in the `remove_left_recursion` method.
2022-12-13 17:27:16 +01:00
Andrea Fioraldi
08be5f732e
CI: Only test fuzzers with diffing deps (#940)
* build and test fuzzer crate

* diffing fuzzers ci only

* clippy

* clippy merda

* clippy merde

* improve it

* comment

* split ubuntu CI workflow

* fix
2022-12-13 14:10:34 +01:00
479 changed files with 25726 additions and 6390 deletions

View File

@ -61,18 +61,14 @@ jobs:
with:
profile: minimal
toolchain: stable
- name: set mold linker as default linker
uses: rui314/setup-mold@v1
- name: Install and cache deps
uses: awalsh128/cache-apt-pkgs-action@v1.1.0
with:
packages: llvm llvm-dev clang ninja-build clang-format-13 shellcheck libgtk-3-dev gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libslirp-dev
packages: llvm llvm-dev clang ninja-build clang-format-13 shellcheck libgtk-3-dev gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libslirp-dev libz3-dev
- name: get clang version
run: command -v llvm-config && clang -v
- name: Install cargo-hack
run: curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin
- name: Add nightly rustfmt and clippy
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
run: rustup toolchain install nightly --component rustfmt --component clippy --component miri --allow-downgrade
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
@ -82,6 +78,8 @@ jobs:
run: cargo check --features=sancov_pcguard_edges
- name: Format
run: cargo fmt -- --check
- name: Cleanup
run: cargo clean
- name: Run clang-format style check for C/C++ programs.
run: clang-format-13 -n -Werror --style=file $(find . -type f \( -name '*.cpp' -o -iname '*.hpp' -o -name '*.cc' -o -name '*.cxx' -o -name '*.cc' -o -name '*.h' \) | grep -v '/target/' | grep -v 'libpng-1\.6\.37' | grep -v 'stb_image\.h' | grep -v 'dlmalloc\.c' | grep -v 'QEMU-Nyx')
- name: run shellcheck
@ -95,9 +93,39 @@ jobs:
- name: Test Docs
run: cargo +nightly test --doc --all-features
# ---- build and feature check ----
# ---- build normal and examples ----
- name: Run a normal build
run: cargo build --verbose
- name: Build examples
run: cargo build --examples --verbose
# --- miri undefined behavior test --
- name: Run miri tests
run: RUST_BACKTRACE=1 MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri test
ubuntu-check:
runs-on: ubuntu-22.04
steps:
- name: Remove Dotnet & Haskell
run: rm -rf /usr/share/dotnet && rm -rf /opt/ghc
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- name: Install and cache deps
uses: awalsh128/cache-apt-pkgs-action@v1.1.0
with:
packages: llvm llvm-dev clang ninja-build clang-format-13 shellcheck libgtk-3-dev gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libslirp-dev libz3-dev
- name: get clang version
run: command -v llvm-config && clang -v
- name: Install cargo-hack
run: curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin
- name: Add nightly
run: rustup toolchain install nightly --allow-downgrade
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
# ---- build and feature check ----
# cargo-hack's --feature-powerset would be nice here but libafl has a too many knobs
- name: Check each feature
# Skipping `python` as it has to be built with the `maturin` tool
@ -106,8 +134,6 @@ jobs:
run: cargo hack check --each-feature --clean-per-run --exclude-features=prelude,agpl,nautilus,python,sancov_pcguard_edges,arm,aarch64,i386,be,systemmode --no-dev-deps
- name: Check nightly features
run: cargo +nightly check --features=agpl && cargo +nightly check --features=nautilus
- name: Build examples
run: cargo build --examples --verbose
ubuntu-concolic:
runs-on: ubuntu-latest
@ -130,16 +156,16 @@ jobs:
with:
profile: minimal
toolchain: stable
- name: set mold linker as default linker
uses: rui314/setup-mold@v1
- name: Install deps
run: sudo apt-get install -y llvm llvm-dev clang ninja-build python3-dev python3-pip python3-venv
run: sudo apt-get install -y llvm llvm-dev clang ninja-build python3-dev python3-pip python3-venv libz3-dev
- name: Install maturin
run: python3 -m pip install maturin
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
- name: Run a maturin build
run: cd ./bindings/pylibafl && maturin build
run: cd ./bindings/pylibafl && python3 -m venv .env && . .env/bin/activate && pip install . && ./test.sh
- name: Run python test
run: . ./bindings/pylibafl/.env/bin/activate && cd ./fuzzers/baby_fuzzer && python3 baby_fuzzer.py 2>&1 | grep "Bye"
fuzzers:
strategy:
@ -151,24 +177,27 @@ jobs:
with:
profile: minimal
toolchain: stable
- name: set mold linker as default linker
if: runner.os == 'Linux' # mold only support linux until now
uses: rui314/setup-mold@v1
- name: Add nightly rustfmt and clippy
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
- name: Add no_std toolchain
run: rustup toolchain install nightly-x86_64-unknown-linux-gnu ; rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
- name: Install python
- name: Add wasm target
run: rustup target add wasm32-unknown-unknown
- name: Install cxxbridge
if: runner.os == 'macOS'
run: brew install --force-bottle --overwrite python@3.11
run: cargo install cxxbridge-cmd
- name: Install python (macOS)
# Removing macOS things already installed in CI against failed linking
if: runner.os == 'macOS'
run: rm /usr/local/bin/2to3* /usr/local/bin/idle3* /usr/local/bin/pydoc3* /usr/local/bin/python3*; brew install --force-bottle --overwrite python
- uses: lyricwulf/abc@v1
with:
# todo: remove afl++-clang when nyx support samcov_pcguard
linux: llvm llvm-dev clang nasm ninja-build gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libgtk-3-dev afl++-clang pax-utils
linux: llvm llvm-dev clang nasm ninja-build gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libgtk-3-dev afl++-clang pax-utils libz3-dev
# update bash for macos to support `declare -A` command`
macos: llvm libpng nasm coreutils z3 bash
macos: llvm libpng nasm coreutils z3 bash wget
- name: pip install
run: python3 -m pip install msgpack jinja2
run: python3 -m pip install msgpack jinja2 find_libpython
# Note that nproc needs to have coreutils installed on macOS, so the order of CI commands matters.
- name: enable mult-thread for `make`
run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)"
@ -176,16 +205,51 @@ jobs:
uses: baptiste0928/cargo-install@v1.3.0
with:
crate: cargo-make
- name: install wasm-pack
uses: baptiste0928/cargo-install@v1.3.0
with:
crate: wasm-pack
- name: install chrome
uses: browser-actions/setup-chrome@v1
with:
chrome-version: stable
- uses: actions/checkout@v3
with:
submodules: true # recursively checkout submodules
fetch-depth: 0 # to diff with origin/main
- uses: Swatinem/rust-cache@v2
- name: Build and run example fuzzers (Linux)
if: runner.os == 'Linux'
run: ./scripts/test_all_fuzzers.sh
run: RUN_ON_CI=1 ./scripts/test_all_fuzzers.sh
- name: Build and run example fuzzers (macOS)
if: runner.os == 'macOS' # use bash v4
run: /usr/local/bin/bash ./scripts/test_all_fuzzers.sh
run: /usr/local/bin/bash -c 'RUN_ON_CI=1 ./scripts/test_all_fuzzers.sh'
executions-check:
strategy:
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions-rs/toolchain@v1
with:
profile: minimal
toolchain: stable
- uses: lyricwulf/abc@v1
with:
linux: llvm llvm-dev clang
macos: llvm bash coreutils
- uses: actions/checkout@v3
with:
submodules: true # recursively checkout submodules
fetch-depth: 0
- uses: Swatinem/rust-cache@v2
- name: Build and run libfuzzer_libpng (Linux)
if: runner.os == 'Linux'
run: ./scripts/executions-check.sh
- name: Build and run libfuzzer_libpng (macOS)
if: runner.os == 'macOS'
run: /usr/local/bin/bash -c './scripts/executions-check.sh'
nostd-build:
runs-on: ubuntu-latest
@ -238,10 +302,14 @@ jobs:
- name: install cargo-make
run: cargo install --force cargo-make
- uses: ilammy/msvc-dev-cmd@v1
- name: install cxx bridge
run: cargo install cxxbridge-cmd
- name: Build fuzzers/frida_libpng
run: cd fuzzers/frida_libpng/ && cargo make test
- name: Build fuzzers/frida_gdiplus
run: cd fuzzers/frida_gdiplus/ && cargo make test
- name: Build fuzzers/tinyinst_simple
run: cd fuzzers/tinyinst_simple/ && cargo make test
macos:
runs-on: macOS-latest
@ -254,6 +322,8 @@ jobs:
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
- name: Install deps
run: brew install z3 gtk+3
- name: Install cxxbridge
run: cargo install cxxbridge-cmd
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
- name: MacOS Build
@ -274,7 +344,7 @@ jobs:
toolchain: stable
- uses: nttld/setup-ndk@v1
with:
ndk-version: r21e
ndk-version: r25b
- name: install ios
run: rustup target add aarch64-apple-ios
- name: install android
@ -284,7 +354,7 @@ jobs:
- uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2
- name: Build iOS
run: cargo build --target aarch64-apple-ios
run: cargo build --target aarch64-apple-ios && cd libafl_frida && cargo build --target aarch64-apple-ios && cd ..
- name: Build Android
run: cargo ndk -t arm64-v8a build --release
#run: cargo build --target aarch64-linux-android
@ -312,14 +382,14 @@ jobs:
mem: 2048
release: 13.1
prepare: |
pkg install -y curl bash sudo llvm14
pkg install -y curl bash sudo llvm16
curl https://sh.rustup.rs -sSf | sh -s -- -y
run: |
freebsd-version
. "$HOME/.cargo/env"
rustup toolchain install nightly
export LLVM_CONFIG=/usr/local/bin/llvm-config14
export LLVM_CONFIG=/usr/local/bin/llvm-config16
pwd
ls -lah
echo "local/bin"

4
.gitignore vendored
View File

@ -21,6 +21,7 @@ vendor
*.obj
.cur_input
.cur_input_*
.venv
crashes
@ -46,6 +47,7 @@ a
forkserver_test
__pycache__
*.lafl_lock
*.metadata
*atomic_file_testfile*
**/libxml2
@ -53,4 +55,4 @@ __pycache__
**/libxml2-*.tar.gz
libafl_nyx/QEMU-Nyx
libafl_nyx/packer
libafl_nyx/packer

3
.gitmodules vendored
View File

@ -1,3 +0,0 @@
[submodule "libafl_concolic/symcc_runtime/symcc"]
path = libafl_concolic/symcc_runtime/symcc
url = https://github.com/AFLplusplus/symcc.git

View File

@ -16,6 +16,7 @@ members = [
"utils/deexit",
"utils/gramatron/construct_automata",
"utils/libafl_benches",
"utils/build_and_test_fuzzers",
]
default-members = [
"libafl",
@ -32,7 +33,7 @@ exclude = [
]
[workspace.package]
version = "0.8.2"
version = "0.10.1"
[profile.release]
lto = true

View File

@ -1,6 +1,6 @@
# LibAFL, the fuzzer library.
<img align="right" src="https://github.com/AFLplusplus/Website/raw/master/static/logo_256x256.png" alt="AFL++ Logo">
<img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/main/static/libafl_logo.svg" alt="LibAFL logo" width="250" heigh="250">
Advanced Fuzzing Library - Slot your own fuzzers together and extend their features using Rust.
@ -10,6 +10,7 @@ LibAFL is written and maintained by
* [Dominik Maier](https://twitter.com/domenuk) <dominik@aflplus.plus>
* [s1341](https://twitter.com/srubenst1341) <github@shmarya.net>
* [Dongjia Zhang](https://github.com/tokatoka) <toka@aflplus.plus>
* [Addison Crump](https://github.com/addisoncrump) <me@addisoncrump.info>
## Why LibAFL?
@ -33,7 +34,7 @@ LibAFL offers integrations with popular instrumentation frameworks. At the momen
+ SanitizerCoverage, in [libafl_targets](./libafl_targets)
+ Frida, in [libafl_frida](./libafl_frida)
+ QEMU user-mode, in [libafl_qemu](./libafl_qemu)
+ QEMU user-mode and system mode, including hooks for emulation, in [libafl_qemu](./libafl_qemu)
+ TinyInst, in [libafl_tinyinst](./libafl_tinyinst) by [elbiazo](https://github.com/elbiazo)
## Getting started
@ -44,7 +45,7 @@ We highly recommend *not* to use e.g. your Linux distribition package as this is
Rust directly, instructions can be found [here](https://www.rust-lang.org/tools/install).
- LLVM tools
The LLVM tools are needed (newer than LLVM 11.0.0 but older than LLVM 15.0.0)
The LLVM tools (including clang, clang++) are needed (newer than LLVM 11.0.0 but older than LLVM 15.0.0)
- Cargo-make
We use cargo-make to build the fuzzers in `fuzzers/` directory. You can install it with
@ -71,7 +72,7 @@ cargo build --release
cargo doc
```
5. Browse the LibAFL book (WIP!) with (requires [mdbook](https://github.com/rust-lang/mdBook))
5. Browse the LibAFL book (WIP!) with (requires [mdbook](https://rust-lang.github.io/mdBook/index.html))
```
cd docs && mdbook serve

1
bindings/pylibafl/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
dist/

View File

@ -1,13 +1,14 @@
[package]
name = "pylibafl"
version = "0.8.2"
version = "0.10.1"
edition = "2021"
[dependencies]
pyo3 = { version = "0.17", features = ["extension-module"] }
libafl_qemu = { path = "../../libafl_qemu", version = "0.8.2", features = ["python"] }
libafl_sugar = { path = "../../libafl_sugar", version = "0.8.2", features = ["python"] }
libafl = { path = "../../libafl", version = "0.8.2", features = ["python"] }
pyo3 = { version = "0.18.3", features = ["extension-module"] }
pyo3-log = "0.8.1"
libafl_qemu = { path = "../../libafl_qemu", version = "0.10.1", features = ["python"] }
libafl_sugar = { path = "../../libafl_sugar", version = "0.10.1", features = ["python"] }
libafl = { path = "../../libafl", version = "0.10.1", features = ["python"] }
[build-dependencies]
pyo3-build-config = { version = "0.17" }

View File

@ -0,0 +1,26 @@
[build-system]
requires = ["maturin[patchelf]>=0.14.10,<0.15"]
build-backend = "maturin"
[project]
name = "PyLibAFL"
version = "0.10.1"
description = "Advanced Fuzzing Library for Python"
readme = "README.md"
requires-python = ">=3.8"
license = {text = "Apache-2.0"}
classifiers = [
"License :: OSI Approved :: Apache Software License",
"License :: OSI Approved :: MIT License",
"Programming Language :: Rust",
"Topic :: Security",
]
[project.urls]
repository = "https://github.com/AFLplusplus/LibAFL.git"
[tool.maturin]
bindings = "pylibafl"
manifest-path = "Cargo.toml"
python-source = "python"
all-features = true

View File

@ -26,7 +26,7 @@ class BaseFeedback:
pass
def is_interesting(self, state, mgr, input, observers, exit_kind) -> bool:
return False
def append_metadata(self, state, testcase):
def append_metadata(self, state, observers, testcase):
pass
def discard_metadata(self, state, input):
pass
@ -84,6 +84,8 @@ def feedback_or_fast(a, b):
#[pymodule]
#[pyo3(name = "pylibafl")]
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
pyo3_log::init();
let modules = py.import("sys")?.getattr("modules")?;
let sugar_module = PyModule::new(py, "sugar")?;

View File

@ -1,5 +1,8 @@
from pylibafl.libafl import *
import ctypes
import platform
MAP_SIZE = 4096
class FooObserver(BaseObserver):
@ -33,11 +36,16 @@ class FooExecutor(BaseExecutor):
return (self.h)(input)
libc = ctypes.cdll.LoadLibrary("libc.so.6")
if platform.system() == "Darwin":
libc = ctypes.cdll.LoadLibrary("libc.dylib")
else:
libc = ctypes.cdll.LoadLibrary("libc.so.6")
area_ptr = libc.calloc(1, 4096)
# Get a buffer to use for our map observer
libc.calloc.restype = ctypes.c_void_p
area_ptr = libc.calloc(1, MAP_SIZE)
observer = StdMapObserverI8("mymap", area_ptr, 4096)
observer = StdMapObserverI8("mymap", area_ptr, MAP_SIZE)
m = observer.as_map_observer()
@ -69,7 +77,12 @@ mgr = SimpleEventManager(monitor.as_monitor())
def harness(buf) -> ExitKind:
"""
The harness fn that the fuzzer will execute in a loop
"""
# print(buf)
# set the observer map byte from python
m[0] = 1
if len(buf) > 0 and buf[0] == ord("a"):
m[1] = 1
@ -91,4 +104,6 @@ stage_tuple_list = StagesTuple([stage.as_stage()])
fuzzer.add_input(state, executor.as_executor(), mgr.as_manager(), b"\0\0")
print("Starting to fuzz from python!")
fuzzer.fuzz_loop(executor.as_executor(), state, mgr.as_manager(), stage_tuple_list)

11
bindings/pylibafl/test.sh Executable file
View File

@ -0,0 +1,11 @@
#!/usr/bin/env bash
timeout 10 python3 ./test.py
export exit_code=$?
if [ $exit_code -eq 124 ]; then
# 124 = timeout happened. All good.
exit 0
else
exit $exit_code
fi

1
docs/.gitignore vendored
View File

@ -1 +1,2 @@
book
!listings/**/*

View File

@ -0,0 +1,9 @@
[package]
name = "baby_fuzzer"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

View File

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

View File

@ -0,0 +1,20 @@
[package]
name = "baby_fuzzer"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libafl = { path = "path/to/libafl/" }
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 3
debug = true

View File

@ -0,0 +1,3 @@
fn main() {
println!("Hello, world!");
}

View File

@ -0,0 +1,23 @@
[package]
name = "baby_fuzzer"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libafl = { path = "path/to/libafl/" }
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 3
debug = true
[features]
panic = []

View File

@ -0,0 +1,25 @@
extern crate libafl;
use libafl::{
bolts::AsSlice,
executors::ExitKind,
inputs::{BytesInput, HasTargetBytes},
};
fn main() {
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
if buf.len() > 0 && buf[0] == 'a' as u8 {
if buf.len() > 1 && buf[1] == 'b' as u8 {
if buf.len() > 2 && buf[2] == 'c' as u8 {
panic!("=)");
}
}
}
ExitKind::Ok
};
// To test the panic:
let input = BytesInput::new(Vec::from("abc"));
#[cfg(feature = "panic")]
harness(&input);
}

View File

@ -0,0 +1,23 @@
[package]
name = "baby_fuzzer"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libafl = { path = "path/to/libafl/" }
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 3
debug = true
[features]
panic = []

View File

@ -0,0 +1,85 @@
/* ANCHOR: use */
extern crate libafl;
use libafl::{
bolts::{current_nanos, rands::StdRand, AsSlice},
corpus::{InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{inprocess::InProcessExecutor, ExitKind},
fuzzer::StdFuzzer,
generators::RandPrintablesGenerator,
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
schedulers::QueueScheduler,
state::StdState,
};
use std::path::PathBuf;
/* ANCHOR_END: use */
fn main() {
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
if buf.len() > 0 && buf[0] == 'a' as u8 {
if buf.len() > 1 && buf[1] == 'b' as u8 {
if buf.len() > 2 && buf[2] == 'c' as u8 {
panic!("=)");
}
}
}
ExitKind::Ok
};
// To test the panic:
let input = BytesInput::new(Vec::from("abc"));
#[cfg(feature = "panic")]
harness(&input);
/* ANCHOR: state */
// create a State from scratch
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
&mut (),
&mut (),
)
.unwrap();
/* ANCHOR_END: state */
/* ANCHOR: event_manager */
// The Monitor trait defines how the fuzzer stats are displayed to the user
let mon = SimpleMonitor::new(|s| println!("{s}"));
// The event manager handles the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus
let mut mgr = SimpleEventManager::new(mon);
/* ANCHOR_END: event_manager */
/* ANCHOR: scheduler_fuzzer */
// A queue policy to get testcasess from the corpus
let scheduler = QueueScheduler::new();
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, (), ());
/* ANCHOR_END: scheduler_fuzzer */
/* ANCHOR: executor */
// Create the executor for an in-process function
let mut executor = InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut mgr)
.expect("Failed to create the Executor");
/* ANCHOR_END: executor */
/* ANCHOR: generator */
// Generator of printable bytearrays of max size 32
let mut generator = RandPrintablesGenerator::new(32);
// Generate 8 initial inputs
state
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
.expect("Failed to generate the initial corpus");
/* ANCHOR_END: generator */
}

View File

@ -0,0 +1,23 @@
[package]
name = "baby_fuzzer"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libafl = { path = "path/to/libafl/" }
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 3
debug = true
[features]
panic = []

View File

@ -0,0 +1,115 @@
/* ANCHOR: use */
extern crate libafl;
use libafl::{
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
corpus::{InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::StdFuzzer,
generators::RandPrintablesGenerator,
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
observers::StdMapObserver,
schedulers::QueueScheduler,
state::StdState,
};
use std::path::PathBuf;
/* ANCHOR_END: use */
/* ANCHOR: signals */
// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
}
fn main() {
// The closure that we want to fuzz
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
signals_set(0); // set SIGNALS[0]
if buf.len() > 0 && buf[0] == 'a' as u8 {
signals_set(1); // set SIGNALS[1]
if buf.len() > 1 && buf[1] == 'b' as u8 {
signals_set(2); // set SIGNALS[2]
if buf.len() > 2 && buf[2] == 'c' as u8 {
panic!("=)");
}
}
}
ExitKind::Ok
};
/* ANCHOR_END: signals */
// To test the panic:
let input = BytesInput::new(Vec::from("abc"));
#[cfg(feature = "panic")]
harness(&input);
/* ANCHOR: observer */
// Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", &mut SIGNALS) };
/* ANCHOR_END: observer */
/* ANCHOR: state_with_feedback_and_objective */
// Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer);
// A feedback to choose if an input is a solution or not
let mut objective = CrashFeedback::new();
// create a State from scratch
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
&mut feedback,
&mut objective,
)
.unwrap();
/* ANCHOR_END: state_with_feedback_and_objective */
// The Monitor trait defines how the fuzzer stats are displayed to the user
let mon = SimpleMonitor::new(|s| println!("{s}"));
// The event manager handles the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus
let mut mgr = SimpleEventManager::new(mon);
// A queue policy to get testcasess from the corpus
let scheduler = QueueScheduler::new();
/* ANCHOR: state_with_feedback_and_objective */
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
/* ANCHOR_END: state_with_feedback_and_objective */
/* ANCHOR: executor_with_observer */
// Create the executor for an in-process function with just one observer
let mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(observer),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor");
/* ANCHOR_END: executor_with_observer */
// Generator of printable bytearrays of max size 32
let mut generator = RandPrintablesGenerator::new(32);
// Generate 8 initial inputs
state
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
.expect("Failed to generate the initial corpus");
/* ANCHOR: signals */
}
/* ANCHOR_END: signals */

View File

@ -0,0 +1,23 @@
[package]
name = "baby_fuzzer"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libafl = { path = "path/to/libafl/" }
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 3
debug = true
[features]
panic = []

View File

@ -0,0 +1,115 @@
/* ANCHOR: use */
extern crate libafl;
use libafl::{
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
corpus::{InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Fuzzer, StdFuzzer},
generators::RandPrintablesGenerator,
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
observers::StdMapObserver,
schedulers::QueueScheduler,
stages::mutational::StdMutationalStage,
state::StdState,
};
use std::path::PathBuf;
/* ANCHOR_END: use */
// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
}
fn main() {
// The closure that we want to fuzz
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
signals_set(0); // set SIGNALS[0]
if buf.len() > 0 && buf[0] == 'a' as u8 {
signals_set(1); // set SIGNALS[1]
if buf.len() > 1 && buf[1] == 'b' as u8 {
signals_set(2); // set SIGNALS[2]
if buf.len() > 2 && buf[2] == 'c' as u8 {
panic!("=)");
}
}
}
ExitKind::Ok
};
// To test the panic:
let input = BytesInput::new(Vec::from("abc"));
#[cfg(feature = "panic")]
harness(&input);
// Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", &mut SIGNALS) };
// Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer);
// A feedback to choose if an input is a solution or not
let mut objective = CrashFeedback::new();
// create a State from scratch
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
&mut feedback,
&mut objective,
)
.unwrap();
// The Monitor trait defines how the fuzzer stats are displayed to the user
let mon = SimpleMonitor::new(|s| println!("{s}"));
// The event manager handles the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus
let mut mgr = SimpleEventManager::new(mon);
// A queue policy to get testcasess from the corpus
let scheduler = QueueScheduler::new();
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// Create the executor for an in-process function with just one observer
let mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(observer),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor");
// Generator of printable bytearrays of max size 32
let mut generator = RandPrintablesGenerator::new(32);
// Generate 8 initial inputs
state
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
.expect("Failed to generate the initial corpus");
/* ANCHOR: mutational_stage */
// Setup a mutational stage with a basic bytes mutator
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 the fuzzing loop");
/* ANCHOR_END: mutational_stage */
}

View File

@ -1,7 +1,8 @@
# Concolic Tracing and Hybrid Fuzzing
LibAFL has support for concolic tracing based on the [SymCC](https://github.com/eurecom-s3/symcc) instrumenting compiler.
For those uninitiated, the following attempts to describe concolic tracing from the ground up using an example.
For those uninitiated, the following text attempts to describe concolic tracing from the ground up using an example.
Then, we'll go through the relationship of SymCC and LibAFL concolic tracing.
Finally, we'll walk through building a basic hybrid fuzzer using LibAFL.
@ -92,18 +93,18 @@ In hybrid fuzzing, we combine this tracing + solving approach with more traditio
The concolic tracing support in LibAFL is implemented using SymCC.
SymCC is a compiler plugin for clang that can be used as a drop-in replacement for a normal C or C++ compiler.
SymCC will instrument the compiled code with callbacks into a runtime that can be supplied by the user.
These callbacks allow the runtime to construct a trace that similar to the previous example.
These callbacks allow the runtime to construct a trace that is similar to the previous example.
### SymCC and its Runtimes
SymCC ships with 2 runtimes:
SymCC ships with 2 runtimes:
* a 'simple' runtime that attempts to solve any branches it comes across using [Z3](https://github.com/Z3Prover/z3/wiki) and
* a [QSym](https://github.com/sslab-gatech/qsym)-based runtime, which does a bit more filtering on the expressions and also solves using Z3.
* A 'simple' runtime that attempts to negate and analytically solve any branch conditions it comes across using [Z3](https://github.com/Z3Prover/z3/wiki) and
* A [QSym](https://github.com/sslab-gatech/qsym)-based runtime, which does a bit more filtering on the expressions and also solves them using Z3.
The integration with LibAFL, however, requires you to **BYORT** (_bring your own runtime_) using the [`symcc_runtime`](https://docs.rs/symcc_runtime/0.1/symcc_runtime) crate.
This crate allows you to easily build a custom runtime out of the built-in building blocks or create entirely new runtimes with full flexibility.
Checkout out the `symcc_runtime` docs for more information on how to build your own runtime.
Check out the `symcc_runtime` docs for more information on how to build your own runtime.
### SymQEMU
@ -123,7 +124,7 @@ There are three main steps involved with building a hybrid fuzzer using LibAFL:
3. building the fuzzer.
Note that the order of these steps is important.
For example, we need to have runtime ready before we can do instrumentation with SymCC.
For example, we need to have a runtime ready before we can do instrumentation with SymCC.
### Building a Runtime
@ -134,10 +135,12 @@ Check out the [example hybrid fuzzer's runtime](https://github.com/AFLplusplus/L
### Instrumentation
There are two main instrumentation methods to make use of concolic tracing in LibAFL:
* Using an **compile-time** instrumented target with **SymCC**.
* Using a **compile-time** instrumented target with **SymCC**.
This only works when the source is available for the target and the target is reasonably easy to build using the SymCC compiler wrapper.
* Using **SymQEMU** to dynamically instrument the target at **runtime**.
This avoids a separate instrumented target with concolic tracing instrumentation and does not require source code.
This avoids building a separate instrumented target with concolic tracing instrumentation and so does not require source code.
It should be noted, however, that the 'quality' of the generated expressions can be significantly worse and SymQEMU generally produces significantly more and significantly more convoluted expressions than SymCC.
Therefore, it is recommended to use SymCC over SymQEMU when possible.
@ -158,23 +161,23 @@ Make sure you satisfy the [build requirements](https://github.com/eurecom-s3/sym
Build SymQEMU according to its [build instructions](https://github.com/eurecom-s3/symqemu#readme).
By default, SymQEMU looks for the runtime in a sibling directory.
Since we don't have a runtime there, we need to let it know the path to your runtime by setting `--symcc-build` argument of the `configure` script to the path of your runtime.
Since we don't have a runtime there, we need to explicitly set the `--symcc-build` argument of the `configure` script to the path of your runtime.
### Building the Fuzzer
No matter the instrumentation method, the interface between the fuzzer and the instrumented target should now be consistent.
The only difference between using SymCC and SymQEMU should be the binary that represents the target:
In the case of SymCC it will be the binary that was build with instrumentation and with SymQEMU it will be the emulator binary (eg. `x86_64-linux-user/symqemu-x86_64`), followed by your uninstrumented target binary and arguments.
In the case of SymCC it will be the binary that was build with instrumentation and with SymQEMU it will be the emulator binary (eg. `x86_64-linux-user/symqemu-x86_64`), followed by your uninstrumented target binary and its arguments.
You can use the [`CommandExecutor`](https://docs.rs/libafl/0.6.0/libafl/executors/command/struct.CommandExecutor.html) to execute your target ([example](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs#L244)).
When configuring the command, make sure you pass the `SYMCC_INPUT_FILE` environment variable the input file path, if your target reads input from a file (instead of standard input).
When configuring the command, make sure you pass the `SYMCC_INPUT_FILE` environment variable (set to the input file path), if your target reads input from a file (instead of standard input).
#### Serialization and Solving
While it is perfectly possible to build a custom runtime that also performs the solving step of hybrid fuzzing in the context of the target process, the intended use of the LibAFL concolic tracing support is to serialize the (filtered and pre-processed) branch conditions using the [`TracingRuntime`](https://docs.rs/symcc_runtime/0.1/symcc_runtime/tracing/struct.TracingRuntime.html).
This serialized representation can be deserialized in the fuzzer process for solving using a [`ConcolicObserver`](https://docs.rs/libafl/0.6.0/libafl/observers/concolic/struct.ConcolicObserver.html) wrapped in a [`ConcolicTracingStage`](https://docs.rs/libafl/0.6.0/libafl/stages/concolic/struct.ConcolicTracingStage.html), which will attach a [`ConcolicMetadata`](https://docs.rs/libafl/0.6.0/libafl/observers/concolic/struct.ConcolicMetadata.html) to every [`TestCase`](https://docs.rs/libafl/0.6.0/libafl/corpus/testcase/struct.Testcase.html).
The `ConcolicMetadata` can be used to replay the concolic trace and solved using an SMT-Solver.
The `ConcolicMetadata` can be used to replay the concolic trace and to solve the conditions using an SMT-Solver.
Most use-cases involving concolic tracing, however, will need to define some policy around which branches they want to solve.
The [`SimpleConcolicMutationalStage`](https://docs.rs/libafl/0.6.0//libafl/stages/concolic/struct.SimpleConcolicMutationalStage.html) can be used for testing purposes.
It will attempt to solve all branches, like the original simple backend from SymCC, using Z3.

View File

@ -17,7 +17,7 @@ If you are on Windows, you'll need to install llvm tools.
LibAFL uses Frida's [__Stalker__](https://frida.re/docs/stalker/) to trace the execution of your program and instrument your harness.
Thus, you have to compile your harness to a dynamic library. Frida instruments your PUT after dynamically loading it.
For example in our `frida_libpng` example, we load the dynamic library and find the symbol to harness as follows:
In our `frida_libpng` example, we load the dynamic library and find the symbol to harness as follows:
```rust,ignore
let lib = libloading::Library::new(module_name).unwrap();
@ -28,9 +28,9 @@ For example in our `frida_libpng` example, we load the dynamic library and find
## `FridaInstrumentationHelper` and Runtimes
To use functionalities that Frida offers, we'll first need to obtain `Gum` object by `Gum::obtain()`.
To use functionalities that Frida offers, we'll first need to obtain a `Gum` object by `Gum::obtain()`.
In LibAFL, we use the `FridaInstrumentationHelper` struct to manage frida-related state. `FridaInstrumentationHelper` is a key component that sets up the [__Transformer__](https://frida.re/docs/stalker/#transformer) that is used to generate the instrumented code. It also initializes the `Runtimes` that offer various instrumentation.
In LibAFL, we use the `FridaInstrumentationHelper` struct to manage frida-related state. `FridaInstrumentationHelper` is a key component that sets up the [__Transformer__](https://frida.re/docs/stalker/#transformer) that is used to generate the instrumented code. It also initializes the `Runtimes` that offer various instrumentations.
We have `CoverageRuntime` that can track the edge coverage, `AsanRuntime` for address sanitizer, `DrCovRuntime` that uses [__DrCov__](https://dynamorio.org/page_drcov.html) for coverage collection (to be imported in coverage tools like Lighthouse, bncov, dragondance,...), and `CmpLogRuntime` for cmplog instrumentation.
All of these runtimes can be slotted into `FridaInstrumentationHelper` at build time.
@ -53,12 +53,12 @@ Combined with any `Runtime` you'd like to use, you can initialize the `FridaInst
## Running the Fuzzer
After setting up the `FridaInstrumentationHelper`. You can obtain the pointer to the coverage map by calling `map_ptr_mut()`.
After setting up the `FridaInstrumentationHelper` you can obtain the pointer to the coverage map by calling `map_mut_ptr()`.
```rust,ignore
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr(
"edges",
frida_helper.map_ptr_mut().unwrap(),
frida_helper.map_mut_ptr().unwrap(),
MAP_SIZE,
));
```
@ -83,5 +83,5 @@ You can then link this observer to `FridaInProcessExecutor` as follows:
);
```
And, finally you can run the fuzzer.
See the `frida_` examples in [`./fuzzers`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/) for more information and, for linux or full-system, play around with `libafl_qemu`, another binary-only tracer.
And finally you can run the fuzzer.
See the `frida_` examples in [`./fuzzers`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/) for more information and, for linux or full-system, play around with `libafl_qemu`, another binary-only tracer.

View File

@ -1,6 +1,6 @@
# Using LibAFL in `no_std` environments
It is possible to use LibAFL in `no_std` environments e.g. custom platforms like microcontrollers, kernels, hypervisors, and more.
It is possible to use LibAFL in `no_std` environments e.g. on custom platforms like microcontrollers, kernels, hypervisors, and more.
You can simply add LibAFL to your `Cargo.toml` file:
@ -16,7 +16,7 @@ cargo build --no-default-features --target aarch64-unknown-none
## Use custom timing
The minimum amount of input LibAFL needs for `no_std` is a monotonically increasing timestamp.
The minimum amount of support LibAFL needs for a `no_std` environment is a monotonically increasing timestamp.
For this, anywhere in your project you need to implement the `external_current_millis` function, which returns the current time in milliseconds.
```c

View File

@ -2,12 +2,12 @@
NYX supports both source-based and binary-only fuzzing.
Currently, `libafl_nyx` only supports [afl++](https://github.com/AFLplusplus/AFLplusplus)'s instruction. To install it, you can use `sudo apt install aflplusplus`. Or compile from the source:
Currently, `libafl_nyx` only supports [afl++](https://github.com/AFLplusplus/AFLplusplus)'s instruction type. To install it, you can use `sudo apt install aflplusplus`. Or compile from the source:
```bash
git clone https://github.com/AFLplusplus/AFLplusplus
cd AFLplusplus
make all # this will not compile afl's additional extension
make all # this will not compile afl's additional extensions
```
Then you should compile the target with the afl++ compiler wrapper:
@ -20,9 +20,9 @@ export CXX=afl-clang-fast++
make
```
For binary-only fuzzing, Nyx uses intel-PT(Intel® Processor Trace). You can find the supported CPU at <https://www.intel.com/content/www/us/en/support/articles/000056730/processors.html>.
For binary-only fuzzing, Nyx uses intel-PT(Intel® Processor Trace). You can find the list of supported CPUs at <https://www.intel.com/content/www/us/en/support/articles/000056730/processors.html>.
## Preparing Nyx working directory
## Preparing the Nyx working directory
This step is used to pack the target into Nyx's kernel. Don't worry, we have a template shell script in our [example](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_parallel/setup_libxml2.sh):
@ -49,7 +49,7 @@ python3 ./packer/packer/nyx_config_gen.py /tmp/nyx_libxml2/ Kernel || exit
## Standalone fuzzing
In the [example fuzzer](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_standalone/src/main.rs). First you need to run `./setup_libxml2.sh`, It will prepare your target and create your nyx work directory in `/tmp/libxml2`. After that, you can start write your code.
In the [example fuzzer](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_standalone/src/main.rs) you first need to run `./setup_libxml2.sh`. It will prepare your target and create your nyx work directory in `/tmp/libxml2`. After that, you can start to write your code.
First, to create `Nyxhelper`:
@ -57,22 +57,21 @@ First, to create `Nyxhelper`:
let share_dir = Path::new("/tmp/nyx_libxml2/");
let cpu_id = 0; // use first cpu
let parallel_mode = false; // close parallel_mode
let mut helper = NyxHelper::new(share_dir, cpu_id, true, parallel_mode, None).unwrap(); // we don't the set the last parameter in standalone mode, we just use None, here
let mut helper = NyxHelper::new(share_dir, cpu_id, true, parallel_mode, None).unwrap(); // we don't need to set the last parameter in standalone mode, we just use None, here
```
Then, fetch `trace_bits`, create an observer and the `NyxExecutor`:
```rust,ignore
let trace_bits = unsafe { std::slice::from_raw_parts_mut(helper.trace_bits, helper.map_size) };
let observer = StdMapObserver::new("trace", trace_bits);
let observer = unsafe { StdMapObserver::from_mut_ptr("trace", helper.trace_bits, helper.map_size) };
let mut executor = NyxExecutor::new(&mut helper, tuple_list!(observer)).unwrap();
```
Finally, use them as normal and pass them into `fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)` to start fuzzing.
Finally, use them normally and pass them into `fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)` to start fuzzing.
## Parallel fuzzing
In the [example fuzzer](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_parallel/src/main.rs). First you need to run `./setup_libxml2.sh` as described before.
In the [example fuzzer](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/nyx_libxml2_parallel/src/main.rs) you first need to run `./setup_libxml2.sh` as described before.
Parallel fuzzing relies on [`Launcher`](../message_passing/spawn_instances.md), so spawn logic should be written in the scoop of anonymous function `run_client`:
@ -91,7 +90,7 @@ let mut helper = NyxHelper::new(
cpu_id, // current cpu id
true, // open snap_mode
parallel_mode, // open parallel mode
Some(parent_cpu_id.id as u32), // the cpu-id of master instance, there is only one master instance, other instances will be treated as slaved
Some(parent_cpu_id.id as u32), // the cpu-id of main instance, there is only one main instance, other instances will be treated as secondaries
)
.unwrap();
```
@ -99,13 +98,11 @@ let mut helper = NyxHelper::new(
Then you can fetch the trace_bits and create an observer and `NyxExecutor`
```rust,ignore
let trace_bits =
unsafe { std::slice::from_raw_parts_mut(helper.trace_bits, helper.map_size) };
let observer = StdMapObserver::new("trace", trace_bits);
let observer = unsafe { StdMapObserver::from_mut_ptr("trace", helper.trace_bits, helper.map_size) }
let mut executor = NyxExecutor::new(&mut helper, tuple_list!(observer)).unwrap();
```
Finally, open a `Launcher` as normal to start fuzzing:
Finally, open a `Launcher` as usual to start fuzzing:
```rust,ignore
match Launcher::builder()
@ -121,6 +118,6 @@ match Launcher::builder()
{
Ok(()) => (),
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
Err(err) => panic!("Failed to run launcher: {:?}", err),
Err(err) => panic!("Failed to run launcher: {err:?}"),
}
```

View File

@ -17,7 +17,7 @@ You can find a complete version of this tutorial as an example fuzzer in [`fuzze
We use cargo to create a new Rust project with LibAFL as a dependency.
```sh
```console
$ cargo new baby_fuzzer
$ cd baby_fuzzer
```
@ -25,18 +25,11 @@ $ cd baby_fuzzer
The generated `Cargo.toml` looks like the following:
```toml
[package]
name = "baby_fuzzer"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
{{#include ../../listings/baby_fuzzer/listing-01/Cargo.toml}}
```
In order to use LibAFl we must add it as dependency adding `libafl = { path = "path/to/libafl/" }` under `[dependencies]`.
That path actually needs to point to the `libafl` directory within the cloned repo, not the root of the repo itself.
You can use the LibAFL version from [crates.io](https://crates.io/crates/libafl) if you want, in this case, you have to use `libafl = "*"` to get the latest version (or set it to the current version).
As we are going to fuzz Rust code, we want that a panic does not simply cause the program to exit, but raise an `abort` that can then be caught by the fuzzer.
@ -47,28 +40,10 @@ Alongside this setting, we add some optimization flags for the compilation, when
The final `Cargo.toml` should look similar to the following:
```toml
[package]
name = "baby_fuzzer"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
libafl = { path = "path/to/libafl/" }
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
lto = true
codegen-units = 1
opt-level = 3
debug = true
{{#include ../../listings/baby_fuzzer/listing-02/Cargo.toml}}
```
## The function under test
Opening `src/main.rs`, we have an empty `main` function.
@ -76,52 +51,32 @@ To start, we create the closure that we want to fuzz. It takes a buffer as input
`ExitKind` is used to inform the fuzzer about the harness' exit status.
```rust
extern crate libafl;
use libafl::{
bolts::AsSlice,
inputs::{BytesInput, HasTargetBytes},
executors::ExitKind,
};
fn main(){
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
if buf.len() > 0 && buf[0] == 'a' as u8 {
if buf.len() > 1 && buf[1] == 'b' as u8 {
if buf.len() > 2 && buf[2] == 'c' as u8 {
panic!("=)");
}
}
}
ExitKind::Ok
};
// To test the panic:
let input = BytesInput::new(Vec::from("abc"));
#[cfg(feature = "panic")]
harness(&input);
}
{{#rustdoc_include ../../listings/baby_fuzzer/listing-03/src/main.rs}}
```
To test the crash manually, you can add a feature in `Cargo.toml` that enables the call that triggers the panic:
```toml
{{#include ../../listings/baby_fuzzer/listing-03/Cargo.toml:22:23}}
```
And then run the program with that feature activated:
```console
$ cargo run -F panic
```
And you should see the program crash as expected.
## Generating and running some tests
One of the main components that a LibAFL-based fuzzer uses is the State, a container of the data that is evolved during the fuzzing process.
Includes all State, such as the Corpus of inputs, the current RNG state, and potential Metadata for the testcases and run.
One of the main components that a LibAFL-based fuzzer uses is the State, a container of the data that will evolve during the fuzzing process.
It includes all state, such as the Corpus of inputs, the current RNG state, and potential Metadata for the testcases and run.
In our `main` we create a basic State instance like the following:
```rust,ignore
// create a State from scratch
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
&mut (),
&mut ()
).unwrap();
```rust
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:state}}
```
- The first parameter is a random number generator, that is part of the fuzzer state, in this case, we use the default one `StdRand`, but you can choose a different one. We seed it with the current nanoseconds.
@ -129,43 +84,26 @@ let mut state = StdState::new(
To avoid type annotation error, you can use `InMemoryCorpus::<BytesInput>::new()` to replace `InMemoryCorpus::new()`. If not, type annotation will be automatically inferred when adding `executor`.
- third parameter is another corpus that stores the "solution" testcases for the fuzzer. For our purpose, the solution is the input that triggers the panic. In this case, we want to store it to disk under the `crashes` directory, so we can inspect it.
- last two parameters are feedback and objective, we will discuss them later.
- The third parameter is another Corpus that stores the "solution" testcases for the fuzzer. For our purpose, the solution is the input that triggers the panic. In this case, we want to store it to disk under the `crashes` directory, so we can inspect it.
- The last two parameters are feedback and objective, we will discuss them later.
Another required component is the **EventManager**. It handles some events such as the addition of a testcase to the corpus during the fuzzing process. For our purpose, we use the simplest one that just displays the information about these events to the user using a `Monitor` instance.
```rust,ignore
// The Monitor trait defines how the fuzzer stats are displayed to the user
let mon = SimpleMonitor::new(|s| println!("{}", s));
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus
let mut mgr = SimpleEventManager::new(mon);
```rust
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:event_manager}}
```
In addition, we have the **Fuzzer**, an entity that contains some actions that alter the State. One of these actions is the scheduling of the testcases to the fuzzer using a **Scheduler**.
We create it as `QueueScheduler`, a scheduler that serves testcases to the fuzzer in a FIFO fashion.
```rust,ignore
// A queue policy to get testcasess from the corpus
let scheduler = QueueScheduler::new();
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, (), ());
```rust
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:scheduler_fuzzer}}
```
Last but not least, we need an **Executor** that is the entity responsible to run our program under test. In this example, we want to run the harness function in-process (without forking off a child, for example), and so we use the `InProcessExecutor`.
```rust,ignore
// Create the executor for an in-process function
let mut executor = InProcessExecutor::new(
&mut harness,
(),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor");
```rust
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:executor}}
```
It takes a reference to the harness, the state, and the event manager. We will discuss the second parameter later.
@ -175,41 +113,19 @@ Now we have the 4 major entities ready for running our tests, but we still canno
For this purpose, we use a **Generator**, `RandPrintablesGenerator` that generates a string of printable bytes.
```rust,ignore
use libafl::generators::RandPrintablesGenerator;
// Generator of printable bytearrays of max size 32
let mut generator = RandPrintablesGenerator::new(32);
// Generate 8 initial inputs
state
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
.expect("Failed to generate the initial corpus".into());
```rust
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:generator}}
```
Now you can prepend the necessary `use` directives to your main.rs and compile the fuzzer.
```rust
extern crate libafl;
use std::path::PathBuf;
use libafl::{
bolts::{AsSlice, current_nanos, rands::StdRand},
corpus::{InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{inprocess::InProcessExecutor, ExitKind},
fuzzer::StdFuzzer,
generators::RandPrintablesGenerator,
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
schedulers::QueueScheduler,
state::StdState,
};
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:use}}
```
When running, you should see something similar to:
```sh
```console
$ cargo run
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
Running `target/debug/baby_fuzzer`
@ -225,60 +141,22 @@ Now we want to turn our simple fuzzer into a feedback-based one and increase the
**Observer** can record the information about properties of a fuzzing run and then feeds the fuzzer. We use the `StdMapObserver`, the default observer that uses a map to keep track of covered elements. In our fuzzer, each condition is mapped to an entry of such map.
We represent such map as a `static mut` variable.
As we don't rely on any instrumentation engine, we have to manually track the satisfied conditions by `singals_set` in our harness:
As we don't rely on any instrumentation engine, we have to manually track the satisfied conditions by `signals_set` in our harness:
```rust
extern crate libafl;
use libafl::{
bolts::AsSlice,
inputs::{BytesInput, HasTargetBytes},
executors::ExitKind,
};
// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
}
// The closure that we want to fuzz
let mut harness = |input: &BytesInput| {
let target = input.target_bytes();
let buf = target.as_slice();
signals_set(0); // set SIGNALS[0]
if buf.len() > 0 && buf[0] == 'a' as u8 {
signals_set(1); // set SIGNALS[1]
if buf.len() > 1 && buf[1] == 'b' as u8 {
signals_set(2); // set SIGNALS[2]
if buf.len() > 2 && buf[2] == 'c' as u8 {
panic!("=)");
}
}
}
ExitKind::Ok
};
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:signals}}
```
The observer can be created directly from the `SIGNALS` map, in the following way:
```rust,ignore
// Create an observation channel using the signals map
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
```rust
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:observer}}
```
The observers are usually kept in the corresponding executor as they keep track of information that is valid for just one run. We have then to modify our InProcessExecutor creation to include the observer as follows:
```rust,ignore
// Create the executor for an in-process function with just one observer
let mut executor = InProcessExecutor::new(
&mut harness,
tuple_list!(observer),
&mut fuzzer,
&mut state,
&mut mgr,
)
.expect("Failed to create the Executor".into());
```rust
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:executor_with_observer}}
```
Now that the fuzzer can observe which condition is satisfied, we need a way to rate an input as interesting (i.e. worth of addition to the corpus) based on this observation. Here comes the notion of Feedback.
@ -287,49 +165,23 @@ Now that the fuzzer can observe which condition is satisfied, we need a way to r
We use `MaxMapFeedback`, a feedback that implements a novelty search over the map of the MapObserver. Basically, if there is a value in the observer's map that is greater than the maximum value registered so far for the same entry, it rates the input as interesting and updates its state.
**Objective Feedback** is another kind of Feedback which decide if an input is a "solution". It will save input to solutions(`./crashes` in our case) other than corpus when the input is rated interesting. We use `CrashFeedback` to tell the fuzzer that if an input causes the program to crash it is a solution for us.
**Objective Feedback** is another kind of Feedback which decides if an input is a "solution". It will save input to solutions(`./crashes` in our case) rather than corpus when the input is rated interesting. We use `CrashFeedback` to tell the fuzzer that if an input causes the program to crash it is a solution for us.
We need to update our State creation including the feedback state and the Fuzzer including the feedback and the objective:
```rust,ignore
extern crate libafl;
use libafl::{
bolts::{current_nanos, rands::StdRand, tuples::tuple_list},
corpus::{InMemoryCorpus, OnDiskCorpus},
feedbacks::{MaxMapFeedback, CrashFeedback},
fuzzer::StdFuzzer,
state::StdState,
observers::StdMapObserver,
};
```rust
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:state_with_feedback_and_objective}}
```
// Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer);
Once again, you need to add the necessary `use` directives for this to work properly:
// A feedback to choose if an input is a solution or not
let mut objective = CrashFeedback::new();
// create a State from scratch
let mut state = StdState::new(
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
&mut feedback,
&mut objective
).unwrap();
// ...
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
```rust
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:use}}
```
## The actual fuzzing
Now, after including the correct `use`, we can run the program, but the outcome is not so different from the previous one as the random generator does not take into account what we save as interesting in the corpus. To do that, we need to plug a Mutator.
Now, we can run the program, but the outcome is not so different from the previous one as the random generator does not take into account what we save as interesting in the corpus. To do that, we need to plug a Mutator.
**Stages** perform actions on individual inputs, taken from the corpus.
For instance, the `MutationalStage` executes the harness several times in a row, every time with mutated inputs.
@ -337,28 +189,20 @@ For instance, the `MutationalStage` executes the harness several times in a row,
As the last step, we create a MutationalStage that uses a mutator inspired by the havoc mutator of AFL.
```rust,ignore
use libafl::{
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
stages::mutational::StdMutationalStage,
fuzzer::Fuzzer,
};
// ...
// Setup a mutational stage with a basic bytes mutator
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 the fuzzing loop");
{{#rustdoc_include ../../listings/baby_fuzzer/listing-06/src/main.rs:mutational_stage}}
```
`fuzz_loop` will request a testcase for each iteration to the fuzzer using the scheduler and then it will invoke the stage.
After adding this code, we have a proper fuzzer, that can run a find the input that panics the function in less than a second.
Again, we need to add the new `use` directives:
```text
```rust,ignore
{{#rustdoc_include ../../listings/baby_fuzzer/listing-06/src/main.rs:use}}
```
After adding this code, we have a proper fuzzer, that can run and find the input that panics the function in less than a second.
```console
$ cargo run
Compiling baby_fuzzer v0.1.0 (/home/andrea/Desktop/baby_fuzzer)
Finished dev [unoptimized + debuginfo] target(s) in 1.56s

View File

@ -9,4 +9,4 @@ Examples can be found under `./fuzzer`.
| baby_fuzzer_nautilus | [nautilus](https://www.ndss-symposium.org/wp-content/uploads/2019/02/ndss2019_04A-3_Aschermann_paper.pdf) is a **coverage guided, grammar based** fuzzer|
|baby_fuzzer_tokens| basic **token level** fuzzer with token level mutations|
|baby_fuzzer_with_forkexecutor| example for **InProcessForkExecutor**|
|baby_no_std|a minimalistic example how to create a libafl based fuzzer that works on **`no_std`** environments like TEEs, Kernels or on barew metal|
|baby_no_std|a minimalistic example how to create a libafl based fuzzer that works on **`no_std`** environments like TEEs, Kernels or on bare metal|

View File

@ -4,8 +4,8 @@ The Corpus is where testcases are stored. We define a Testcase as an Input and a
A Corpus can store testcases in different ways, for example on disk, or in memory, or implement a cache to speedup on disk storage.
Usually, a testcase is added to the Corpus when it is considered as interesting, but a Corpus is used also to store testcases that fulfill an objective (like crashing the tested program for instance).
Usually, a testcase is added to the Corpus when it is considered as interesting, but a Corpus is used also to store testcases that fulfill an objective (like crashing the program under test for instance).
Related to the Corpus, there is the way in which the fuzzer should ask for the next testcase to fuzz picking it from the Corpus. The taxonomy for this in LibAFL is CorpusScheduler, the entity representing the policy to pop testcases from the Corpus, FIFO for instance.
Related to the Corpus is the way in which the next testcase (the fuzzer would ask for) is retrieved from the Corpus. The taxonomy for this handling in LibAFL is CorpusScheduler, the entity representing the policy to pop testcases from the Corpus, in a FIFO fashion for instance.
Speaking about the code, [`Corpus`](https://docs.rs/libafl/0/libafl/corpus/trait.Corpus.html) and [`CorpusScheduler`](https://docs.rs/libafl/0/libafl/corpus/trait.CorpusScheduler.html) are traits.

View File

@ -13,7 +13,7 @@ In Rust, we bind this concept to the [`Executor`](https://docs.rs/libafl/0/libaf
By default, we implement some commonly used Executors such as [`InProcessExecutor`](https://docs.rs/libafl/0/libafl/executors/inprocess/struct.InProcessExecutor.html) in which the target is a harness function providing in-process crash detection. Another Executor is the [`ForkserverExecutor`](https://docs.rs/libafl/0/libafl/executors/forkserver/struct.ForkserverExecutor.html) that implements an AFL-like mechanism to spawn child processes to fuzz.
A common pattern when creating an Executor is wrapping an existing one, for instance [`TimeoutExecutor`](https://docs.rs/libafl/0.6.1/libafl/executors/timeout/struct.TimeoutExecutor.html) wraps an executor and install a timeout callback before calling the original run function of the wrapped executor.
A common pattern when creating an Executor is wrapping an existing one, for instance [`TimeoutExecutor`](https://docs.rs/libafl/0.6.1/libafl/executors/timeout/struct.TimeoutExecutor.html) wraps an executor and installs a timeout callback before calling the original `run` function of the wrapped executor.
## InProcessExecutor
Let's begin with the base case; `InProcessExecutor`.
@ -24,7 +24,7 @@ When you want to execute the harness as fast as possible, you will most probably
One thing to note here is, when your harness is likely to have heap corruption bugs, you want to use another allocator so that corrupted heap does not affect the fuzzer itself. (For example, we adopt MiMalloc in some of our fuzzers.). Alternatively you can compile your harness with address sanitizer to make sure you can catch these heap bugs.
## ForkserverExecutor
Next, we'll take a look at the `ForkserverExecutor`. In this case, it is `afl-cc` (from AFLplusplus/AFLplusplus) that compiles the harness code, and therefore, we can't use `EDGES_MAP` anymore. Hopefully, we have [_a way_](https://github.com/AFLplusplus/AFLplusplus/blob/2e15661f184c77ac1fbb6f868c894e946cbb7f17/instrumentation/afl-compiler-rt.o.c#L270) to tell the forkserver which map to record the coverage.
Next, we'll take a look at the `ForkserverExecutor`. In this case, it is `afl-cc` (from AFLplusplus/AFLplusplus) that compiles the harness code, and therefore, we can't use `EDGES_MAP` anymore. Fortunately we have [_a way_](https://github.com/AFLplusplus/AFLplusplus/blob/2e15661f184c77ac1fbb6f868c894e946cbb7f17/instrumentation/afl-compiler-rt.o.c#L270) to tell the forkserver which map to record the coverage in.
As you can see from the forkserver example,
@ -48,7 +48,7 @@ See AFL++'s [_documentation_](https://github.com/AFLplusplus/AFLplusplus/blob/st
Finally, we'll talk about the `InProcessForkExecutor`.
`InProcessForkExecutor` has only one difference from `InprocessExecutor`; It forks before running the harness and that's it.
But why do we want to do so? well, under some circumstances, you may find your harness pretty unstable or your harness wreaks havoc on the global states. In this case, you want to fork it before executing the harness runs in the child process so that it doesn't break things.
But why do we want to do so? Well, under some circumstances, you may find your harness pretty unstable or your harness wreaks havoc on the global states. In this case, you want to fork it before executing the harness runs in the child process so that it doesn't break things.
However, we have to take care of the shared memory, it's the child process that runs the harness code and writes the coverage to the map.

View File

@ -11,16 +11,16 @@ The concept of "interestingness" is abstract, but typically it is related to a n
As an example, given an Observer that reports all the sizes of memory allocations, a maximization Feedback can be used to maximize these sizes to sport pathological inputs in terms of memory consumption.
In terms of code, the library offers the [`Feedback`](https://docs.rs/libafl/0/libafl/feedbacks/trait.Feedback.html) and the [`FeedbackState`](https://docs.rs/libafl/0/libafl/feedbacks/trait.FeedbackState.html) traits.
The first is used to implement functors that, given the state of the observers from the last execution, tells if the execution was interesting. The second is tied with `Feedback` and it is the state of the data that the feedback wants to persist in the fuzzers's state, for instance the cumulative map holding all the edges seen so far in the case of a feedback based on edge coverage.
The first is used to implement functors that, given the state of the observers from the last execution, tells if the execution was interesting. The second is tied with `Feedback` and is the state of the data that the feedback wants to persist in the fuzzers's state, for instance the cumulative map holding all the edges seen so far in the case of a feedback based on edge coverage.
Multiple Feedbacks can be combined into boolean formula, considering for instance an execution as interesting if it triggers new code paths or execute in less time compared to the average execution time using [`feedback_or`](https://docs.rs/libafl/*/libafl/macro.feedback_or.html).
Multiple Feedbacks can be combined into a boolean expression, considering for instance an execution as interesting if it triggers new code paths or execute in less time compared to the average execution time using [`feedback_or`](https://docs.rs/libafl/*/libafl/macro.feedback_or.html).
On top, logic operators like `feedback_or` and `feedback_and` have a `_fast` option (`feedback_or_fast` where the second feedback will not be evaluated, if the first part already answers the `interestingness` question, to save precious performance.
On top, logic operators like `feedback_or` and `feedback_and` have a `_fast` variant (e.g. `feedback_or_fast`) where the second feedback will not be evaluated, if the value of the first feedback operand already answers the `interestingness` question so as to save precious performance.
Using `feedback_and_fast` in combination with [`ConstFeedback`](https://docs.rs/libafl/*/libafl/feedbacks/enum.ConstFeedback.html#method.new), certain feedbacks can be disabled dynamically.
## Objectives
While feedbacks are commonly used to decide if an [`Input`](https://docs.rs/libafl/*/libafl/inputs/trait.Input.html) should be kept for future mutations, they serve a double-purpose, as so-called `Objective Feedbacks`.
In this case, the `interestingness` of a feedback indicates, if an `Objective` has been hit.
Commonly, these would be a`crash or a timeout, but they can also be used to find specific parts of the program, for sanitization, or a differential fuzzing success.
In this case, the `interestingness` of a feedback indicates if an `Objective` has been hit.
Commonly, these objectives would be a crash or a timeout, but they can also be used to detect if specific parts of the program have been reached, for sanitization, or a differential fuzzing success.

View File

@ -6,10 +6,10 @@ In our model of an abstract fuzzer, we define the Input as the internal represen
In the straightforward case, the input of the program is a byte array and in fuzzers such as AFL we store and manipulate exactly these byte arrays.
But it is not always the case. A program can expect inputs that are not byte arrays (e.g. a sequence of syscalls) and the fuzzer does not represent the Input in the same way that the program consumes it.
But it is not always the case. A program can expect inputs that are not linear byte arrays (e.g. a sequence of syscalls forming a use case or protocol) and the fuzzer does not represent the Input in the same way that the program consumes it.
In case of a grammar fuzzer for instance, the Input is generally an Abstract Syntax Tree because it is a data structure that can be easily manipulated while maintaining the validity, but the program expects a byte array as input, so just before the execution, the tree is serialized to a sequence of bytes.
In the Rust code, an [`Input`](https://docs.rs/libafl/*/libafl/inputs/trait.Input.html) is a trait that can be implemented only by structures that are serializable and have only owned data as fields.
While most fuzzer use a normal `BytesInput`], more advanced inputs like inputs include special inputs for grammar fuzzing ([GramatronInput](https://docs.rs/libafl/*/libafl/inputs/gramatron/struct.GramatronInput.html) or `NautilusInput` on nightly), as well as the token-level [EncodedInput](https://docs.rs/libafl/*/libafl/inputs/encoded/struct.EncodedInput.html).
While most fuzzers use a normal `BytesInput`, more advanced ones use inputs that include special inputs for grammar fuzzing ([GramatronInput](https://docs.rs/libafl/*/libafl/inputs/gramatron/struct.GramatronInput.html) or `NautilusInput` on Rust nightly), as well as the token-level [EncodedInput](https://docs.rs/libafl/*/libafl/inputs/encoded/struct.EncodedInput.html).

View File

@ -1,9 +1,9 @@
# Mutator
The Mutator is an entity that takes one or more Inputs and generates a new derived one.
The Mutator is an entity that takes one or more Inputs and generates a new instance of Input derived by its inputs.
Mutators can be composed, and they are generally linked to a specific Input type.
There can be, for instance, a Mutator that applies more than a single type of mutation on the input. Consider a generic Mutator for a byte stream, bit flip is just one of the possible mutations but not the only one, there is also, for instance, the random replacement of a byte of the copy of a chunk.
There can be, for instance, a Mutator that applies more than a single type of mutation to the input. Consider a generic Mutator for a byte stream, bit flip is just one of the possible mutations but not the only one, there is also, for instance, the random replacement of a byte of the copy of a chunk.
In LibAFL, [`Mutator`](https://docs.rs/libafl/*/libafl/mutators/trait.Mutator.html) is a trait.

View File

@ -4,8 +4,8 @@ An Observer is an entity that provides an information observed during the execut
The information contained in the Observer is not preserved across executions, but it may be serialized and passed on to other nodes if an `Input` is considered `interesting`, and added to the `Corpus`.
As an example, the coverage map, filled during the execution to report the executed edges used by fuzzers such as AFL and `HonggFuzz` can be considered an observation. Another `Observer` can be the time spent executing a run, the program output, or more advanced observation, like maximum stack depth at runtime.
This information is not preserved across runs, and it is an observation of a dynamic property of the program.
As an example, the coverage map, filled during the execution to report the executed edges used by fuzzers such as AFL and `HonggFuzz` can be considered an observation. Another `Observer` can collect the time spent executing a run, the program output, or a more advanced observation, like maximum stack depth at runtime.
This information is an observation of a dynamic property of the program.
In terms of code, in the library this entity is described by the [`Observer`](https://docs.rs/libafl/0/libafl/observers/trait.Observer.html) trait.

View File

@ -1,8 +1,8 @@
# Stage
A Stage is an entity that operates on a single Input got from the Corpus.
A Stage is an entity that operates on a single Input received from the Corpus.
For instance, a Mutational Stage, given an input of the corpus, applies a Mutator and executes the generated input one or more time. How many times this has to be done can be scheduled, AFL for instance uses a performance score of the input to choose how many times the havoc mutator should be invoked. This can depend also on other parameters, for instance, the length of the input if we want to just apply a sequential bitflip, or be a fixed value.
For instance, a Mutational Stage, given an input of the corpus, applies a Mutator and executes the generated input one or more times. How many times this has to be done can be scheduled, AFL for instance uses a performance score of the input to choose how many times the havoc mutator should be invoked. This can depend also on other parameters, for instance, the length of the input if we want to just apply a sequential bitflip, or a fixed value.
A stage can also be an analysis stage, for instance, the Colorization stage of Redqueen that aims to introduce more entropy in a testcase or the Trimming stage of AFL that aims to reduce the size of a testcase.

View File

@ -10,6 +10,6 @@ Thinking about similar fuzzers, you can observe that most of the time the data s
Beside the entities previously described, we introduce the [`Testcase`](https://docs.rs/libafl/0.6/libafl/corpus/testcase/struct.Testcase.html) and [`State`](https://docs.rs/libafl/0.6/libafl/state/struct.StdState.html) entities. The Testcase is a container for an Input stored in the Corpus and its metadata (so, in the implementation, the Corpus stores Testcases) and the State contains all the metadata that are evolved while running the fuzzer, Corpus included.
The State, in the implementation, contains only owned objects that are serializable, and it is serializable itself. Some fuzzers may want to serialize its state when pausing or just, when doing in-process fuzzing, serialize on crash and deserialize in the new process to continue to fuzz with all the metadata preserved.
The State, in the implementation, contains only owned objects that are serializable, and it is serializable itself. Some fuzzers may want to serialize their state when pausing or just, when doing in-process fuzzing, serialize on crash and deserialize in the new process to continue to fuzz with all the metadata preserved.
Additionally, we group the entities that are "actions", like the `CorpusScheduler` and the `Feedbacks`, in a common place, the [`Fuzzer'](https://docs.rs/libafl/*/libafl/fuzzer/struct.StdFuzzer.html).

View File

@ -19,7 +19,7 @@ pub struct MyMetadata {
The struct must be static, so it cannot hold references to borrowed objects.
As an alternative to `derive(SerdeAny)` that is a proc-macro in `libafl_derive` the user can use `libafl::impl_serdeany!(MyMetadata);`.
As an alternative to `derive(SerdeAny)` which is a proc-macro in `libafl_derive` the user can use `libafl::impl_serdeany!(MyMetadata);`.
## Usage

View File

@ -75,7 +75,7 @@ where
```
The executor is constrained to `EM` and `Z`, with each of their respective states being constrained to `E`'s state. It
is no longer necessary to explicitly defined a generic for the input type, the state type, or the generic type, as these
is no longer necessary to explicitly define a generic for the input type, the state type, or the generic type, as these
are all present as associated types for `E`. Additionally, we don't even need to specify any details about the observers
(`OT` in the previous version) as the type does not need to be constrained and is not shared by other types.
@ -101,7 +101,7 @@ See `fuzzers/` for examples of these changes.
If you implemented a Mutator, Executor, State, or another kind of component, you must update your implementation. The
main changes to the API are in the use of "Uses*" for associated types.
In many scenarios, Input, Observers, and State generics have been moved into traits with associated types (namely,
In many scenarios, Input, Observer, and State generics have been moved into traits with associated types (namely,
"UsesInput", "UsesObservers", and "UsesState". These traits are required for many existing traits now and are very
straightforward to implement. In a majority of cases, you will have generics on your custom implementation or a fixed
type to implement this with. Thankfully, Rust will let you know when you need to implement this type.
@ -127,7 +127,7 @@ where
}
```
After 0.9, all `Corpus` implementations are required to implement `UsesInput` and `Corpus` no longer has a generic for
After 0.9, all `Corpus` implementations are required to implement `UsesInput`. Also `Corpus` no longer has a generic for
the input type (as it is now provided by the UsesInput impl). The migrated implementation is shown below:
```rust,ignore
@ -160,3 +160,26 @@ Now, `Corpus` cannot be accidentally implemented for another type other than tha
is fixed to the associated type for `UsesInput`.
A more complex example of migration can be found in the "Reasons for this change" section of this document.
## Observer Changes
Additionally, we changed the Observer API, as the API in 0.8 led to undefined behavior.
At the same time, we used the change to simplify the common case: creating an `StdMapObserver`
from libafl_target's `EDGES_MAP`.
In the future, instead of using:
```rust,ignore
let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
let edges_observer = StdMapObserver::new("edges", edges);
```
creating the edges observer is as simple as using the new `std_edges_map_observer` function.
```rust,ignore
let edges_observer = unsafe { std_edges_map_observer("edges") };
```
Alternatively, `StdMapObserver::new` will still work, but now the whole method is marked as `unsafe`.
The reason is that the caller has to make sure `EDGES_MAP` (or other maps) are not moved or freed in memory,
for the lifetime of the `MapObserver`.
This means that the buffer should either be `static` or `Pin`.

View File

@ -6,7 +6,7 @@ LibAFL, as most of the Rust projects, can be built using `cargo` from the root d
$ cargo build --release
```
Note that the `--release` flag is optional for development, but you needed to add it to fuzzing at a decent speed.
Note that the `--release` flag is optional for development, but you need to add it to do fuzzing at a decent speed.
Slowdowns of 10x or more are not uncommon for Debug builds.
The LibAFL repository is composed of multiple crates.

View File

@ -10,7 +10,7 @@ libafl = { version = "*" }
## Crate List
For LibAFL, each crate has its self-contained purpose, and the user may not need to use all of them in its project.
For LibAFL, each crate has its self-contained purpose, and the user may not need to use all of them in their project.
Following the naming convention of the folders in the project's root, they are:
### [`libafl`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl)
@ -52,13 +52,13 @@ To enable and disable features at compile-time, the features are enabled and dis
Currently, the supported flags are:
- `pcguard_edges` defines the SanitizerCoverage trace-pc-guard hooks to track the executed edges in a map.
- `pcguard_hitcounts defines the SanitizerCoverage trace-pc-guard hooks to track the executed edges with the hitcounts (like AFL) in a map.
- `pcguard_hitcounts` defines the SanitizerCoverage trace-pc-guard hooks to track the executed edges with the hitcounts (like AFL) in a map.
- `libfuzzer` exposes a compatibility layer with libFuzzer style harnesses.
- `value_profile` defines the SanitizerCoverage trace-cmp hooks to track the matching bits of each comparison in a map.
### libafl_cc
This is a library that provides utils wrap compilers and create source-level fuzzers.
This is a library that provides utils to wrap compilers and create source-level fuzzers.
At the moment, only the Clang compiler is supported.
To understand it deeper, look through the tutorials and examples.

View File

@ -11,15 +11,15 @@ The first step is to download LibAFL and all dependencies that are not automatic
> previous command. Additionally, PowerShell-specific examples will use `>`
> rather than `$`.
While you technically do not need to install LibAFL, but can use the version from crates.io directly, we do recommend to download or clone the GitHub version.
While technically you do not need to install LibAFL, but can use the version from crates.io directly, we do recommend to download or clone the GitHub version.
This gets you the example fuzzers, additional utilities, and latest patches.
The easiest way to do this is to use `git`.
```sh
$ git clone git@github.com:AFLplusplus/LibAFL.git
$ git clone https://github.com/AFLplusplus/LibAFL.git
```
You can alternatively, on a UNIX-like machine, download a compressed archive and extract it with:
Alternatively, on a UNIX-like machine, you can download a compressed archive and extract it with:
```sh
wget https://github.com/AFLplusplus/LibAFL/archive/main.tar.gz
@ -31,7 +31,7 @@ $ ls LibAFL-main # this is the extracted folder
## Clang installation
One of the external dependencies of LibAFL is the Clang C/C++ compiler.
While most of the code is in pure Rust, we still need a C compiler because stable Rust still does not support features that some parts of LibAFL may need, such as weak linking, and LLVM builtins linking.
While most of the code is written in pure Rust, we still need a C compiler because stable Rust still does not support features that some parts of LibAFL may need, such as weak linking, and LLVM builtins linking.
For these parts, we use C to expose the missing functionalities to our Rust codebase.
In addition, if you want to perform source-level fuzz testing of C/C++ applications,

View File

@ -4,10 +4,10 @@ Fuzzers are important tools for security researchers and developers alike.
A wide range of state-of-the-art tools like [AFL++](https://github.com/AFLplusplus/AFLplusplus), [libFuzzer](https://llvm.org/docs/LibFuzzer.html) or [honggfuzz](https://github.com/google/honggfuzz) are available to users. They do their job in a very effective way, finding thousands of bugs.
From the perspective of a power user, however, these tools are limited.
Their design does not treat extensibility as a first-class citizen.
Their designs do not treat extensibility as a first-class citizen.
Usually, a fuzzer developer can choose to either fork one of these existing tools, or to create a new fuzzer from scratch.
In any case, researchers end up with tons of fuzzers, all of which are incompatible with each other.
Their outstanding features can not just be combined for new projects.
Their outstanding features cannot just be combined for new projects.
By reinventing the wheel over and over, we may completely miss out on features that are complex to reimplement.
To tackle this issue, we created LibAFL, a library that is _not just another fuzzer_, but a collection of reusable pieces for individual fuzzers.
@ -24,7 +24,7 @@ Some highlight features currently include:
This means it does not require a specific OS-dependent runtime to function.
Define an allocator and a way to map pages, and you are good to inject LibAFL in obscure targets like embedded devices, hypervisors, or maybe even WebAssembly?
- `adaptable`: Given years of experience fine-tuning *AFLplusplus* and our academic fuzzing background, we could incorporate recent fuzzing trends into LibAFL's design and make it future-proof.
To give an example, as opposed to old-skool fuzzers, a `BytesInput` is just one of the potential forms of inputs:
To give an example, as opposed to old-school fuzzers, a `BytesInput` is just one of the potential forms of inputs:
feel free to use and mutate an Abstract Syntax Tree instead, for structured fuzzing.
- `scalable`: As part of LibAFL, we developed `Low Level Message Passing`, `LLMP` for short, which allows LibAFL to scale almost linearly over cores. That is, if you chose to use this feature - it is your fuzzer, after all.
Scaling to multiple machines over TCP is also possible, using LLMP's `broker2broker` feature.

View File

@ -5,7 +5,7 @@
*by Andrea Fioraldi and Dominik Maier*
Welcome to LibAFL, the Advanced Fuzzing Library.
This book shall be a gentle introduction into the library.
This book shall be a gentle introduction to the library.
This version of the LibAFL book is coupled with the release 1.0 beta of the library.

View File

@ -1,8 +1,8 @@
# Message Passing
LibAFL offers a standard mechanism for message passing over processes and machines with a low overhead.
LibAFL offers a standard mechanism for message passing between processes and machines with a low overhead.
We use message passing to inform the other connected clients/fuzzers/nodes about new testcases, metadata, and statistics about the current run.
Depending on individual needs, LibAFL can also write testcase contents to disk, while still using events to notify other fuzzers, using an `OnDiskCorpus`.
Depending on individual needs, LibAFL can also write testcase contents to disk, while still using events to notify other fuzzers, using the `CachedOnDiskCorpus` or similar.
In our tests, message passing scales very well to share new testcases and metadata between multiple running fuzzer instances for multi-core fuzzing.
Specifically, it scales _a lot_ better than using memory locks on a shared corpus, and _a lot_ better than sharing the testcases via the filesystem, as AFL traditionally does.
@ -12,7 +12,7 @@ The `EventManager` interface is used to send Events over the wire using `Low Lev
## Low Level Message Passing (LLMP)
LibAFL comes with a reasonably lock-free message passing mechanism that scales well across cores and, using its *broker2broker* mechanism, even to connected machines via TCP.
LibAFL comes with a reasonably lock-free message passing mechanism that scales well across cores and, using its _broker2broker_ mechanism, even to connected machines via TCP.
Most example fuzzers use this mechanism, and it is the best `EventManager` if you want to fuzz on more than a single core.
In the following, we will describe the inner workings of `LLMP`.
@ -28,12 +28,12 @@ Shared maps, called shared memory for the sake of not colliding with Rust's `map
Each client, usually a fuzzer trying to share stats and new testcases, maps an outgoing `ShMem` map.
With very few exceptions, only this client writes to this map, therefore, we do not run in race conditions and can live without locks.
The broker reads from all client's `ShMem` maps.
It checks all incoming client maps periodically and then forwards new messages to its outgoing broadcast-`ShMem`, mapped by all connected clients.
It periodically checks all incoming client maps and then forwards new messages to its outgoing broadcast-`ShMem`, mapped by all connected clients.
To send new messages, a client places a new message at the end of their shared memory and then updates a static field to notify the broker.
Once the outgoing map is full, the sender allocates a new `ShMem` using the respective `ShMemProvider`.
It then sends the information needed to map the newly-allocated page in connected processes to the old page, using an end of page (`EOP`) message.
Once the receiver maps the new page, flags it as safe for unmapping from the sending process (to avoid race conditions if we have more than a single EOP in a short time), and then continues to read from the new `ShMem`.
Once the receiver maps the new page, it flags it as safe for unmapping by the sending process (to avoid race conditions if we have more than a single EOP in a short time), and then continues to read from the new `ShMem`.
The schema for client's maps to the broker is as follows:
@ -54,10 +54,10 @@ After the broker received a new message from clientN, (`clientN_out->current_id
The clients periodically, for example after finishing `n` mutations, check for new incoming messages by checking if (`current_broadcast_map->current_id != last_message->message_id`).
While the broker uses the same EOP mechanism to map new `ShMem`s for its outgoing map, it never unmaps old pages.
This additional memory overhead serves a good purpose: by keeping all broadcast pages around, we make sure that new clients can join in on a fuzzing campaign at a later point in time
This additional memory resources serve a good purpose: by keeping all broadcast pages around, we make sure that new clients can join in on a fuzzing campaign at a later point in time.
They just need to re-read all broadcasted messages from start to finish.
So the outgoing messages flow like this over the outgoing broadcast `Shmem`:
So the outgoing messages flow is like this over the outgoing broadcast `Shmem`:
```text
[broker]

View File

@ -8,14 +8,14 @@ The straightforward way to do Multi-Threading is to use the `LlmpRestartingEvent
It abstracts away all the pesky details about restarts on crash handling (for in-memory fuzzers) and multi-threading.
With it, every instance you launch manually tries to connect to a TCP port on the local machine.
If the port is not yet bound, this instance becomes the broker, itself binding to the port to await new clients.
If the port is not yet bound, this instance becomes the broker, binding itself to the port to await new clients.
If the port is already bound, the EventManager will try to connect to it.
The instance becomes a client and can now communicate with all other nodes.
Launching nodes manually has the benefit that you can have multiple nodes with different configurations, such as clients fuzzing with and without ASAN.
While it's called "restarting" manager, it uses `fork` on Unix operating systems as optimization and only actually restarts from scratch on Windows.
While it's called "restarting" manager, it uses `fork` on Unix-like operating systems as optimization and only actually restarts from scratch on Windows.
## Automated, with Launcher
@ -23,7 +23,7 @@ While it's called "restarting" manager, it uses `fork` on Unix operating systems
The Launcher is the lazy way to do multiprocessing.
You can use the Launcher builder to create a fuzzer that spawns multiple nodes with one click, all using restarting event managers and the same configuration.
To use launcher, first you need to write an anonymous function `let mut run_client = |state: Option<_>, mut mgr, _core_id|{}`, which uses three parameters to create individual fuzzer. Then you can specify the `shmem_provider`,`broker_port`,`monitor`,`cores` and other stuff through `Launcher::builder()`:
To use launcher, first you need to write an anonymous function `let mut run_client = |state: Option<_>, mut mgr, _core_id|{}`, which uses three parameters to create an individual fuzzer. Then you can specify the `shmem_provider`,`broker_port`,`monitor`,`cores` and other stuff through `Launcher::builder()`:
```rust,ignore
Launcher::builder()
@ -42,12 +42,12 @@ To use launcher, first you need to write an anonymous function `let mut run_clie
This first starts a broker, then spawns `n` clients, according to the value passed to `cores`.
The value is a string indicating the cores to bind to, for example, `0,2,5` or `0-3`.
For each client, `run_client` will be called.
On Windows, the Launcher will restart each client, while on Unix, it will use `fork`.
On Windows, the Launcher will restart each client, while on Unix-alikes, it will use `fork`.
Advanced use-cases:
1. To connect multiple nodes together via TCP, you can use the `remote_broker_addr`. this requires the `llmp_bind_public` compile-time feature for `LibAFL`.
2. To use multiple launchers for individual configurations, you can set `spawn_broker` to `false` on all but one.
2. To use multiple launchers for individual configurations, you can set `spawn_broker` to `false` on all instances but one.
3. Launcher will not select the cores automatically, so you need to specify the `cores` that you want.
For more examples, you can check out `qemu_launcher` and `libfuzzer_libpng_launcher` in [`./fuzzers/`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers).

View File

@ -2,4 +2,4 @@
In this chapter, we will build a custom fuzzer using the [Lain](https://github.com/microsoft/lain) mutator in Rust.
This tutorial will introduce you in writing extensions to LibAFL like Feedbacks and Testcase's metadata.
This tutorial will introduce you to writing extensions to LibAFL like Feedbacks and Testcase's metadata.

4
fuzzers/FRET/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
*.qcow2
corpus
*.axf
demo

41
fuzzers/FRET/Cargo.toml Normal file
View 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
View 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
View File

@ -0,0 +1,12 @@
*dump
timedump*
corpora
build
mnt
.R*
*.png
*.pdf
bins
.snakemake
*.zip
*.tar.*

View 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

View 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'])

View 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()

View 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))
}

View 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
1 kernel main_function input_symbol input_size return_function
2 mpeg2 mpeg2_main mpeg2_oldorgframe 90112 mpeg2_return
3 audiobeam audiobeam_main audiobeam_input 11520 audiobeam_return
4 epic epic_main epic_image 4096 epic_return
5 dijkstra dijkstra_main dijkstra_AdjMatrix 10000 dijkstra_return
6 fft fft_main fft_twidtable 2046 fft_return
7 bsort bsort_main bsort_Array 400 bsort_return
8 insertsort insertsort_main insertsort_a 400 insertsort_return
9 g723_enc g723_enc_main g723_enc_INPUT 1024 g723_enc_return
10 rijndael_dec rijndael_dec_main rijndael_dec_data 32768 rijndael_dec_return
11 rijndael_enc rijndael_enc_main rijndael_enc_data 31369 rijndael_enc_return
12 huff_dec huff_dec_main huff_dec_encoded 419 huff_dec_return
13 huff_enc huff_enc_main huff_enc_plaintext 600 huff_enc_return
14 gsm_enc gsm_enc_main gsm_enc_pcmdata 6400 gsm_enc_return
15 tmr main FUZZ_INPUT 32 trigger_Qemu_break
16 tacle_rtos prvStage0 FUZZ_INPUT 604 trigger_Qemu_break
17 lift main_lift FUZZ_INPUT 100 trigger_Qemu_break
18 waters main_waters FUZZ_INPUT 4096 trigger_Qemu_break
19 watersv2 main_waters FUZZ_INPUT 4096 trigger_Qemu_break
20 waters_int main_waters FUZZ_INPUT 4096 trigger_Qemu_break
21 watersv2_int main_waters FUZZ_INPUT 4096 trigger_Qemu_break
22 micro_branchless main_branchless FUZZ_INPUT 4 trigger_Qemu_break
23 micro_int main_int FUZZ_INPUT 16 trigger_Qemu_break
24 micro_longint main_micro_longint FUZZ_INPUT 16 trigger_Qemu_break

2
fuzzers/FRET/example/build.sh Executable file
View 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

View 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);
}

View 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")
}

View 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
View 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

345
fuzzers/FRET/src/clock.rs Normal file
View File

@ -0,0 +1,345 @@
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_map_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<OT>(
&mut self,
_state: &mut S,
observers: &OT,
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_map_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<OT>(&mut self, _state: &mut S, observers: &OT, 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")
}
}

717
fuzzers/FRET/src/fuzzer.rs Normal file
View File

@ -0,0 +1,717 @@
//! 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, ptr::addr_of_mut};
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, CorpusId}, Evaluator, stages::StdMutationalStage,
};
use libafl_qemu::{
edges::{self, edges_map_mut_slice, MAX_EDGES_NUM}, 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).expect("Emulator creation failed");
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_observer = unsafe { VariableMapObserver::from_mut_slice(
"edges",
edges_map_mut_slice(),
addr_of_mut!(MAX_EDGES_NUM)
)};
#[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::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 Ok(ichist) = state.metadata_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_map_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.into()).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_map_mut().get_mut::<TopRatedsMetadata>() {
let mut uniq: Vec<CorpusId> = 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_map().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_map().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.into()).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_map_mut().get_mut::<TopRatedsMetadata>() {
let mut uniq: Vec<CorpusId> = 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 Ok(ichist) = state.metadata_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.into()).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 Ok(md) = state.metadata_mut::<TopRatedsMetadata>() {
let mut uniq: Vec<CorpusId> = 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).expect("Emu creation failed");
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
View 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
View 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!");
}

View 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, CorpusId},
};
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: CorpusId,
) -> 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_map().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_map();
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;
}

View 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<OT>(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) {
// 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()) };
}
}

View 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_map_mut()
.get_mut::<SystemStateFeedbackState>("systemstate") {
Some(s) => s,
None => {
let n=SystemStateFeedbackState::default();
state.named_metadata_map_mut().insert(n, "systemstate");
state.named_metadata_map_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<OT>(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
let a = self.last_trace.take();
match a {
Some(s) => testcase.metadata_map_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<OT>(&mut self, _state: &mut S, observers: &OT, 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_map_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}
}
}

View 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);

View 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_map_mut()
.get_mut::<SysGraphFeedbackState>("SysMap") {
Some(s) => s,
None => {
let n=SysGraphFeedbackState::default();
state.named_metadata_map_mut().insert(n, "SysMap");
state.named_metadata_map_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<OT>(&mut self, _state: &mut S, observers: &OT, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
let a = self.last_trace.take();
match a {
Some(s) => testcase.metadata_map_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"
// }
// }

View File

@ -0,0 +1,210 @@
use std::cell::UnsafeCell;
use std::io::Write;
use std::ops::Range;
use libafl::prelude::ExitKind;
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<OT>(&mut self, emulator: &Emulator, _input: &S::Input, _observers: &mut OT, _exit_kind: &mut ExitKind) {
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(&current_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!("*");
}
}

View 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);

View 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;
}

View 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, CorpusId},
};
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(&mut self, state: &mut CS::State, idx: CorpusId) -> Result<(), Error> {
let l = state.corpus()
.get(idx)?
.borrow()
.metadata_map()
.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(
// &mut self,
// state: &mut CS::State,
// idx: CorpusId,
// 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(&mut self, state: &mut CS::State) -> Result<CorpusId, Error> {
let mut idx = self.base.next(state)?;
while {
let l = state.corpus()
.get(idx)?
.borrow()
.metadata_map()
.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_map_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(&mut self, state: &mut Self::State) -> Result<CorpusId, Error> {
let mut to_remove : Vec<(usize, f64)> = vec![];
let mut to_return : usize = 0;
let c = state.corpus().count();
let gm = state.metadata_map_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.into())
},
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.into()).unwrap();
}
// println!("switch next: {to_return}");
return Ok(to_return.into());
}
/// Add the new input to the next generation
fn on_add(
&mut self,
state: &mut Self::State,
idx: CorpusId
) -> Result<(), Error> {
// println!("On Add {idx}");
let mut tc = state.corpus_mut().get(idx).unwrap().borrow_mut().clone();
let ff = MaxTimeFavFactor::compute(state, &mut tc).unwrap();
if let Some(gm) = state.metadata_map_mut().get_mut::<GeneticMetadata>() {
gm.next_gen.push((idx.into(),ff));
} else {
state.add_metadata(GeneticMetadata::new(vec![], vec![(idx.into(),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,
}
}
}

382
fuzzers/FRET/src/worst.rs Normal file
View File

@ -0,0 +1,382 @@
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(state: &S, entry: &mut Testcase<<S as UsesInput>::Input>) -> 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( state: &S, entry: &mut Testcase<S::Input>) -> 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().unwrap() 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<OT>(
&mut self,
_state: &mut S,
observers: &OT,
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_map_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 {
}
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "baby_fuzzer"
version = "0.7.1"
version = "0.10.0"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2021"

View File

@ -5,4 +5,6 @@ This is a minimalistic example about how to create a libafl based fuzzer.
It runs on a single core until a crash occurs and then exits.
The tested program is a simple Rust function without any instrumentation.
For real fuzzing, you will want to add some sort to add coverage or other feedback.
For real fuzzing, you will want to add some sort to add coverage or other feedback.
You can run this example using `cargo run`, and you can enable the TUI feature by running `cargo run --features tui`.

View File

@ -4,67 +4,86 @@ from pylibafl import libafl
def map_observer_wrapper(map_observer):
if type(map_observer).__name__ == "OwnedMapObserverI32":
return libafl.MapObserverI32.new_from_owned(map_observer)
return libafl.MapObserverI32.new_owned(map_observer)
def executor_wrapper(executor):
if type(executor).__name__ == "OwnedInProcessExecutorI32":
return libafl.ExecutorI32.new_from_inprocess(executor)
if type(executor).__name__ == "InProcessExecutor":
return libafl.Executor.new_inprocess(executor)
def generator_wrapper(generator):
if type(generator).__name__ == "RandPrintablesGenerator":
return libafl.Generator.new_rand_printables(generator)
def monitor_wrapper(monitor):
if type(monitor).__name__ == "SimpleMonitor":
return libafl.Monitor.new_from_simple(monitor)
return monitor.as_monitor()
def event_manager_wrapper(event_manager):
if type(event_manager).__name__ == "SimpleEventManager":
return libafl.EventManagerI32.new_from_simple(event_manager)
return event_manager.as_manager()
def corpus_wrapper(corpus):
if type(corpus).__name__ == "InMemoryCorpus":
return libafl.Corpus.new_from_in_memory(corpus)
return libafl.Corpus.new_in_memory(corpus)
if type(corpus).__name__ == "OnDiskCorpus":
return libafl.Corpus.new_from_on_disk(corpus)
return libafl.Corpus.new_on_disk(corpus)
def rand_wrapper(rand):
if type(rand).__name__ == "StdRand":
return libafl.Rand.new_from_std(rand)
return libafl.Rand.new_std(rand)
def mutator_wrapper(mutator):
if type(mutator).__name__ == "StdHavocMutator":
return libafl.Mutator.new_std_havoc(mutator)
def stage_wrapper(stage):
if type(stage).__name__ == "StdScheduledHavocMutationsStageI32":
return libafl.StageI32.new_from_std_scheduled(stage)
if type(stage).__name__ == "StdMutationalStage":
return libafl.Stage.new_std_mutational(stage)
# CODE WRITTEN BY USER
def harness(inp):
if len(inp.hex()) >= 2 and inp.hex()[:2] == '61':
raise Exception("NOOOOOO =)")
import logging
logging.basicConfig(level=logging.INFO)
map_observer = libafl.OwnedMapObserverI32("signals", [0] * 16)
feedback_state = libafl.MapFeedbackStateI32.with_observer(map_observer_wrapper(map_observer))
def harness(inp):
#print(inp)
map_observer[0] = 1
if len(inp) > 0 and inp[0] == ord('a'):
map_observer[1] = 1
if len(inp) > 1 and inp[1] == ord('b'):
map_observer[2] = 1
if len(inp) > 2 and inp[2] == ord('c'):
map_observer[3] = 1
raise Exception("NOOOOOO =)")
feedback = libafl.MaxMapFeedbackI32(feedback_state, map_observer_wrapper(map_observer))
feedback = libafl.MaxMapFeedbackI32(map_observer_wrapper(map_observer))
objective = libafl.CrashFeedback()
state = libafl.StdStateI32(
state = libafl.StdState(
rand_wrapper(libafl.StdRand.with_current_nanos()),
corpus_wrapper(libafl.InMemoryCorpus()),
corpus_wrapper(libafl.OnDiskCorpus("./crashes")),
feedback_state
feedback.as_feedback(),
objective.as_feedback(),
)
monitor = libafl.SimpleMonitor()
monitor = libafl.SimpleMonitor(lambda x: print(x))
mgr = libafl.SimpleEventManager(monitor_wrapper(monitor))
fuzzer = libafl.StdFuzzerI32(feedback)
fuzzer = libafl.StdFuzzer(feedback.as_feedback(), objective.as_feedback())
executor = libafl.OwnedInProcessExecutorI32(harness, map_observer_wrapper(map_observer), fuzzer, state, event_manager_wrapper(mgr))
observers = libafl.ObserversTuple([libafl.Observer.new_map_i32(map_observer_wrapper(map_observer))])
generator = libafl.RandPrintablesGeneratorI32(32)
executor = libafl.InProcessExecutor(harness, observers, fuzzer, state, event_manager_wrapper(mgr))
state.generate_initial_inputs(fuzzer, executor_wrapper(executor), generator, event_manager_wrapper(mgr), 8)
generator = libafl.RandPrintablesGenerator(32)
stage = libafl.StdScheduledHavocMutationsStageI32.new_from_scheduled_havoc_mutations()
state.generate_initial_inputs(fuzzer, executor_wrapper(executor), generator_wrapper(generator), event_manager_wrapper(mgr), 3)
stage_tuple_list = libafl.StagesOwnedListI32(stage_wrapper(stage))
mutator = libafl.StdHavocMutator()
fuzzer.fuzz_loop(executor_wrapper(executor), state, event_manager_wrapper(mgr), stage_tuple_list)
stage = libafl.StdMutationalStage(mutator_wrapper(mutator))
stages = libafl.StagesTuple([stage_wrapper(stage)])
fuzzer.fuzz_loop(executor_wrapper(executor), state, event_manager_wrapper(mgr), stages)

View File

@ -1,9 +1,9 @@
use std::path::PathBuf;
#[cfg(windows)]
use std::ptr::write_volatile;
use std::{path::PathBuf, ptr::write};
#[cfg(feature = "tui")]
use libafl::monitors::tui::TuiMonitor;
use libafl::monitors::tui::{ui::TuiUI, TuiMonitor};
#[cfg(not(feature = "tui"))]
use libafl::monitors::SimpleMonitor;
use libafl::{
@ -24,13 +24,14 @@ use libafl::{
/// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
/// Assign a signal to the signals map
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
unsafe { write(SIGNALS_PTR.add(idx), 1) };
}
#[allow(clippy::similar_names)]
#[allow(clippy::similar_names, clippy::manual_assert)]
pub fn main() {
// The closure that we want to fuzz
let mut harness = |input: &BytesInput| {
@ -60,8 +61,7 @@ pub fn main() {
};
// Create an observation channel using the signals map
let observer =
unsafe { StdMapObserver::new_from_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) };
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
// Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer);
@ -88,9 +88,11 @@ pub fn main() {
// The Monitor trait define how the fuzzer stats are displayed to the user
#[cfg(not(feature = "tui"))]
let mon = SimpleMonitor::new(|s| println!("{}", s));
let mon = SimpleMonitor::new(|s| println!("{s}"));
#[cfg(feature = "tui")]
let mon = TuiMonitor::new(String::from("Baby Fuzzer"), false);
let ui = TuiUI::with_version(String::from("Baby Fuzzer"), String::from("0.0.1"), false);
#[cfg(feature = "tui")]
let mon = TuiMonitor::new(ui);
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus

View File

@ -1,6 +1,6 @@
[package]
name = "baby_fuzzer_gramatron"
version = "0.8.2"
version = "0.10.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2021"

View File

@ -28,10 +28,11 @@ use libafl::{
/// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
/*
/// Assign a signal to the signals map
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
unsafe { std::ptr::write(SIGNALS_PTR.add(idx), 1) };
}
*/
@ -57,7 +58,7 @@ pub fn main() {
};
// Create an observation channel using the signals map
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
// Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer);
@ -83,7 +84,7 @@ pub fn main() {
.unwrap();
// The Monitor trait define how the fuzzer stats are reported to the user
let monitor = SimpleMonitor::new(|s| println!("{}", s));
let monitor = SimpleMonitor::new(|s| println!("{s}"));
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus

View File

@ -1,6 +1,6 @@
[package]
name = "baby_fuzzer_grimoire"
version = "0.8.2"
version = "0.10.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2021"

View File

@ -1,6 +1,6 @@
#[cfg(windows)]
use std::ptr::write_volatile;
use std::{fs, io::Read, path::PathBuf};
use std::{fs, io::Read, path::PathBuf, ptr::write};
use libafl::{
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
@ -9,7 +9,7 @@ use libafl::{
executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Evaluator, Fuzzer, StdFuzzer},
inputs::{GeneralizedInput, HasTargetBytes},
inputs::{BytesInput, HasTargetBytes},
monitors::SimpleMonitor,
mutators::{
havoc_mutations, scheduled::StdScheduledMutator, GrimoireExtensionMutator,
@ -24,10 +24,10 @@ use libafl::{
/// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
/// Assign a signal to the signals map
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
unsafe { write(SIGNALS_PTR.add(idx), 1) };
}
fn is_sub<T: PartialEq>(mut haystack: &[T], needle: &[T]) -> bool {
@ -59,13 +59,13 @@ pub fn main() {
let mut file = fs::File::open(path).expect("no file found");
let mut buffer = vec![];
file.read_to_end(&mut buffer).expect("buffer overflow");
let input = GeneralizedInput::new(buffer);
let input = BytesInput::new(buffer);
initial_inputs.push(input);
}
}
// The closure that we want to fuzz
let mut harness = |input: &GeneralizedInput| {
let mut harness = |input: &BytesInput| {
let target_bytes = input.target_bytes();
let bytes = target_bytes.as_slice();
@ -77,21 +77,14 @@ pub fn main() {
signals_set(3);
}
unsafe {
if input.grimoire_mutated {
// println!(">>> {:?}", input.generalized());
println!(">>> {:?}", std::str::from_utf8_unchecked(bytes));
}
}
signals_set(1);
ExitKind::Ok
};
// Create an observation channel using the signals map
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
// Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new_tracking(&observer, false, true);
let mut feedback = MaxMapFeedback::tracking(&observer, false, true);
// A feedback to choose if an input is a solution or not
let mut objective = CrashFeedback::new();
@ -113,12 +106,12 @@ pub fn main() {
)
.unwrap();
if state.metadata().get::<Tokens>().is_none() {
if state.metadata_map().get::<Tokens>().is_none() {
state.add_metadata(Tokens::from([b"FOO".to_vec(), b"BAR".to_vec()]));
}
// The Monitor trait define how the fuzzer stats are reported to the user
let monitor = SimpleMonitor::new(|s| println!("{}", s));
let monitor = SimpleMonitor::new(|s| println!("{s}"));
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus
@ -158,7 +151,7 @@ pub fn main() {
let mut stages = tuple_list!(
generalization,
StdMutationalStage::new(mutator),
StdMutationalStage::new(grimoire_mutator)
StdMutationalStage::transforming(grimoire_mutator)
);
for input in initial_inputs {

View File

@ -1,6 +1,6 @@
[package]
name = "baby_fuzzer_minimizing"
version = "0.8.2"
version = "0.10.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>", "Addison Crump <research@addisoncrump.info>"]
edition = "2021"

View File

@ -1,15 +1,16 @@
use std::path::PathBuf;
#[cfg(windows)]
use std::ptr::write_volatile;
use std::{path::PathBuf, ptr::write};
use libafl::prelude::*;
/// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
/// Assign a signal to the signals map
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
unsafe { write(SIGNALS_PTR.add(idx), 1) };
}
#[allow(clippy::similar_names)]
@ -32,10 +33,9 @@ pub fn main() -> Result<(), Error> {
};
// Create an observation channel using the signals map
let observer =
unsafe { StdMapObserver::new_from_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) };
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
let factory = MapEqualityFactory::new_from_observer(&observer);
let factory = MapEqualityFactory::with_observer(&observer);
// Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer);
@ -44,7 +44,7 @@ pub fn main() -> Result<(), Error> {
let mut objective = CrashFeedback::new();
// The Monitor trait define how the fuzzer stats are displayed to the user
let mon = SimpleMonitor::new(|s| println!("{}", s));
let mon = SimpleMonitor::new(|s| println!("{s}"));
let mut mgr = SimpleEventManager::new(mon);
@ -56,7 +56,7 @@ pub fn main() -> Result<(), Error> {
// RNG
StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance
OnDiskCorpus::new(&corpus_dir).unwrap(),
InMemoryOnDiskCorpus::new(&corpus_dir).unwrap(),
// Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(&solution_dir).unwrap(),
@ -108,7 +108,7 @@ pub fn main() -> Result<(), Error> {
let mut state = StdState::new(
StdRand::with_seed(current_nanos()),
OnDiskCorpus::new(&minimized_dir).unwrap(),
InMemoryOnDiskCorpus::new(&minimized_dir).unwrap(),
InMemoryCorpus::new(),
&mut (),
&mut (),
@ -116,7 +116,7 @@ pub fn main() -> Result<(), Error> {
.unwrap();
// The Monitor trait define how the fuzzer stats are displayed to the user
let mon = SimpleMonitor::new(|s| println!("{}", s));
let mon = SimpleMonitor::new(|s| println!("{s}"));
let mut mgr = SimpleEventManager::new(mon);
@ -136,7 +136,13 @@ pub fn main() -> Result<(), Error> {
let mut executor = InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut mgr)?;
state.load_initial_inputs_forced(&mut fuzzer, &mut executor, &mut mgr, &[solution_dir])?;
stages.perform_all(&mut fuzzer, &mut executor, &mut state, &mut mgr, 0)?;
stages.perform_all(
&mut fuzzer,
&mut executor,
&mut state,
&mut mgr,
CorpusId::from(0_usize),
)?;
Ok(())
}

View File

@ -1,8 +1,8 @@
[package]
name = "baby_fuzzer_nautilus"
version = "0.8.2"
version = "0.10.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2018"
edition = "2021"
[features]
default = ["std"]

View File

@ -24,10 +24,11 @@ use libafl::{
/// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
/*
/// Assign a signal to the signals map
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
unsafe { str::ptr::write(SIGNALS_PTR.add(idx), 1) };
}
*/
@ -46,7 +47,7 @@ pub fn main() {
};
// Create an observation channel using the signals map
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
// Feedback to rate the interestingness of an input
let mut feedback = feedback_or!(
@ -74,12 +75,16 @@ pub fn main() {
)
.unwrap();
if state.metadata().get::<NautilusChunksMetadata>().is_none() {
if state
.metadata_map()
.get::<NautilusChunksMetadata>()
.is_none()
{
state.add_metadata(NautilusChunksMetadata::new("/tmp/".into()));
}
// The Monitor trait define how the fuzzer stats are reported to the user
let monitor = SimpleMonitor::new(|s| println!("{}", s));
let monitor = SimpleMonitor::new(|s| println!("{s}"));
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus

View File

@ -1,6 +1,6 @@
[package]
name = "baby_fuzzer_swap_differential"
version = "0.7.1"
version = "0.10.1"
authors = ["Addison Crump <research@addisoncrump.info>"]
edition = "2021"
default-run = "fuzzer_sd"
@ -21,7 +21,7 @@ debug = true
[build-dependencies]
anyhow = "1"
bindgen = "0.61"
bindgen = "0.63"
cc = "1.0"
[dependencies]

View File

@ -1,10 +1,10 @@
# Variables
[env]
FUZZER_NAME='fuzzer_sd'
PROJECT_DIR = { script = ["pwd"] }
CARGO_TARGET_DIR = { value = "target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
LIBAFL_CC = '${CARGO_TARGET_DIR}/release/libafl_cc'
FUZZER = '${CARGO_TARGET_DIR}/release/${FUZZER_NAME}'
PROJECT_DIR = { script = ["pwd"] }
# Compilers
[tasks.cc]
@ -31,7 +31,13 @@ windows_alias = "unsupported"
[tasks.test_unix]
script_runner = "@shell"
script='''
timeout 10s ${CARGO_TARGET_DIR}/release/${FUZZER_NAME}
timeout 10s ${CARGO_TARGET_DIR}/release/${FUZZER_NAME} >fuzz_stdout.log || true
if [ -z "$(grep "objectives: 1" fuzz_stdout.log)" ]; then
echo "Fuzzer does not generate any testcases or any crashes"
exit 1
else
echo "Fuzzer is working"
fi
'''
dependencies = [ "fuzzer" ]

View File

@ -27,9 +27,7 @@ fn main() -> anyhow::Result<()> {
})
.join("release/libafl_cc");
println!("cargo:rerun-if-changed={}", compiler.to_str().unwrap());
if !compiler.try_exists().unwrap_or(false) {
println!("cargo:warning=Can't find libafl_cc; assuming that we're building it.");
} else {
if compiler.try_exists().unwrap_or(false) {
cc::Build::new()
.compiler(compiler)
.file("first.c")
@ -38,6 +36,8 @@ fn main() -> anyhow::Result<()> {
.compile("diff-target");
println!("cargo:rustc-link-lib=diff-target");
} else {
println!("cargo:warning=Can't find libafl_cc; assuming that we're building it.");
}
}

View File

@ -11,7 +11,7 @@ pub fn main() {
let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() {
"cc" => false,
"++" | "pp" | "xx" => true,
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {:?} to end with c or cxx", dir),
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {dir:?} to end with c or cxx"),
};
dir.pop();

View File

@ -6,12 +6,12 @@ use std::{
};
#[cfg(feature = "tui")]
use libafl::monitors::tui::TuiMonitor;
use libafl::monitors::tui::{ui::TuiUI, TuiMonitor};
#[cfg(not(feature = "tui"))]
use libafl::monitors::SimpleMonitor;
use libafl::{
bolts::{current_nanos, rands::StdRand, tuples::tuple_list, AsSlice},
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
corpus::{Corpus, InMemoryCorpus, InMemoryOnDiskCorpus},
events::SimpleEventManager,
executors::{inprocess::InProcessExecutor, DiffExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback},
@ -24,18 +24,21 @@ use libafl::{
stages::mutational::StdMutationalStage,
state::{HasSolutions, StdState},
};
use libafl_targets::{DifferentialAFLMapSwapObserver, MAX_EDGES_NUM};
use libafl_targets::{edges_max_num, DifferentialAFLMapSwapObserver};
#[cfg(not(miri))]
use mimalloc::MiMalloc;
#[global_allocator]
#[cfg(not(miri))]
static GLOBAL: MiMalloc = MiMalloc;
// bindings to the functions defined in the target
mod bindings {
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)]
#![allow(unused)]
#![allow(clippy::unreadable_literal)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
@ -43,25 +46,25 @@ use bindings::{inspect_first, inspect_second};
#[cfg(feature = "multimap")]
mod multimap {
pub use libafl::observers::{HitcountsIterableMapObserver, MultiMapObserver};
pub static mut FIRST_EDGES: &'static mut [u8] = &mut [];
pub static mut SECOND_EDGES: &'static mut [u8] = &mut [];
pub static mut COMBINED_EDGES: [&'static mut [u8]; 2] = [&mut [], &mut []];
pub use libafl::{
bolts::ownedref::OwnedMutSlice,
observers::{HitcountsIterableMapObserver, MultiMapObserver},
};
}
#[cfg(feature = "multimap")]
use multimap::*;
use multimap::{HitcountsIterableMapObserver, MultiMapObserver, OwnedMutSlice};
#[cfg(not(feature = "multimap"))]
mod slicemap {
pub use libafl::observers::HitcountsMapObserver;
pub static mut EDGES: &'static mut [u8] = &mut [];
pub static mut EDGES: &mut [u8] = &mut [];
}
#[cfg(not(feature = "multimap"))]
use slicemap::*;
use slicemap::{HitcountsMapObserver, EDGES};
#[allow(clippy::similar_names)]
#[allow(clippy::too_many_lines)]
pub fn main() {
// The closure that we want to fuzz
let mut first_harness = |input: &BytesInput| {
@ -83,19 +86,35 @@ pub fn main() {
}
};
let num_edges: usize = edges_max_num();
#[cfg(feature = "multimap")]
let (first_map_observer, second_map_observer, map_swapper, map_observer) = {
let (
first_map_observer,
second_map_observer,
map_swapper,
map_observer,
layout,
first_edges,
second_edges,
) = {
// initialize the maps
unsafe {
let layout = Layout::from_size_align(MAX_EDGES_NUM, 64).unwrap();
FIRST_EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), MAX_EDGES_NUM);
SECOND_EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), MAX_EDGES_NUM);
COMBINED_EDGES = [&mut FIRST_EDGES, &mut SECOND_EDGES];
}
let layout = Layout::from_size_align(num_edges, 64).unwrap();
let first_edges = unsafe { (alloc_zeroed(layout), num_edges) };
let second_edges = unsafe { (alloc_zeroed(layout), num_edges) };
let combined_edges = unsafe {
vec![
OwnedMutSlice::from_raw_parts_mut(first_edges.0, first_edges.1),
OwnedMutSlice::from_raw_parts_mut(second_edges.0, second_edges.1),
]
};
// create the base maps used to observe the different executors from two independent maps
let mut first_map_observer = StdMapObserver::new("first-edges", unsafe { FIRST_EDGES });
let mut second_map_observer = StdMapObserver::new("second-edges", unsafe { SECOND_EDGES });
let mut first_map_observer =
unsafe { StdMapObserver::from_mut_ptr("first-edges", first_edges.0, first_edges.1) };
let mut second_map_observer =
unsafe { StdMapObserver::from_mut_ptr("second-edges", second_edges.0, second_edges.1) };
// create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!)
let map_swapper =
@ -106,28 +125,35 @@ pub fn main() {
// differential mode
let map_observer = HitcountsIterableMapObserver::new(MultiMapObserver::differential(
"combined-edges",
unsafe { &mut COMBINED_EDGES },
combined_edges,
));
(
first_map_observer,
second_map_observer,
map_swapper,
map_observer,
layout,
first_edges,
second_edges,
)
};
#[cfg(not(feature = "multimap"))]
let (first_map_observer, second_map_observer, map_swapper, map_observer) = {
// initialize the map
unsafe {
let layout = Layout::from_size_align(MAX_EDGES_NUM * 2, 64).unwrap();
EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), MAX_EDGES_NUM * 2);
let layout = Layout::from_size_align(num_edges * 2, 64).unwrap();
EDGES = core::slice::from_raw_parts_mut(alloc_zeroed(layout), num_edges * 2);
}
let edges_ptr = unsafe { EDGES.as_mut_ptr() };
// create the base maps used to observe the different executors by splitting a slice
let mut first_map_observer =
StdMapObserver::new("first-edges", unsafe { &mut EDGES[..MAX_EDGES_NUM] });
let mut second_map_observer =
StdMapObserver::new("second-edges", unsafe { &mut EDGES[MAX_EDGES_NUM..] });
unsafe { StdMapObserver::from_mut_ptr("first-edges", edges_ptr, num_edges) };
let mut second_map_observer = unsafe {
StdMapObserver::from_mut_ptr("second-edges", edges_ptr.add(num_edges), num_edges)
};
// create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!)
let map_swapper =
@ -136,10 +162,14 @@ pub fn main() {
// create a combined map observer, e.g. for calibration
// we use StdMapObserver::differential to indicate that we want to use the observer in
// differential mode
let map_observer =
HitcountsMapObserver::new(StdMapObserver::differential("combined-edges", unsafe {
EDGES
}));
let map_observer = unsafe {
HitcountsMapObserver::new(StdMapObserver::differential_from_mut_ptr(
"combined-edges",
edges_ptr,
num_edges * 2,
))
};
(
first_map_observer,
second_map_observer,
@ -163,7 +193,7 @@ pub fn main() {
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(PathBuf::from("./crashes")).unwrap(),
InMemoryOnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
// States of the feedbacks.
// The feedbacks can report the data that should persist in the State.
&mut feedback,
@ -174,9 +204,11 @@ pub fn main() {
// The Monitor trait define how the fuzzer stats are displayed to the user
#[cfg(not(feature = "tui"))]
let mon = SimpleMonitor::new(|s| println!("{}", s));
let mon = SimpleMonitor::new(|s| println!("{s}"));
#[cfg(feature = "tui")]
let mon = TuiMonitor::new(String::from("Baby Fuzzer"), false);
let ui = TuiUI::new(String::from("Baby Fuzzer"), false);
#[cfg(feature = "tui")]
let mon = TuiMonitor::new(ui);
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus
@ -243,4 +275,10 @@ pub fn main() {
)
.expect("Error in the fuzzing loop");
}
#[cfg(feature = "multimap")]
unsafe {
std::alloc::dealloc(first_edges.0, layout);
std::alloc::dealloc(second_edges.0, layout);
}
}

View File

@ -1,6 +1,6 @@
[package]
name = "baby_fuzzer_tokens"
version = "0.8.2"
version = "0.10.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2021"

View File

@ -1,6 +1,6 @@
#[cfg(windows)]
use std::ptr::write_volatile;
use std::{fs, io::Read, path::PathBuf};
use std::{fs, io::Read, path::PathBuf, ptr::write};
use libafl::{
bolts::{current_nanos, rands::StdRand, tuples::tuple_list},
@ -20,10 +20,12 @@ use libafl::{
/// Coverage map with explicit assignments due to the lack of instrumentation
static mut SIGNALS: [u8; 16] = [0; 16];
static mut SIGNALS_PTR: *mut u8 = unsafe { SIGNALS.as_mut_ptr() };
/*
/// Assign a signal to the signals map
fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 };
unsafe { write(SIGNALS_PTR.add(idx), 1) };
}
*/
@ -65,7 +67,7 @@ pub fn main() {
};
// Create an observation channel using the signals map
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
let observer = unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS_PTR, SIGNALS.len()) };
// Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer);
@ -91,7 +93,7 @@ pub fn main() {
.unwrap();
// The Monitor trait define how the fuzzer stats are reported to the user
let monitor = SimpleMonitor::new(|s| println!("{}", s));
let monitor = SimpleMonitor::new(|s| println!("{s}"));
// The event manager handle the various events generated during the fuzzing loop
// such as the notification of the addition of a new item to the corpus

View File

@ -0,0 +1,2 @@
[build]
target = "wasm32-unknown-unknown"

6
fuzzers/baby_fuzzer_wasm/.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
/target
**/*.rs.bk
Cargo.lock
bin/
wasm-pack.log
.idea/

View File

@ -0,0 +1,35 @@
[package]
name = "baby_fuzzer_wasm"
version = "0.1.0"
authors = ["Addison Crump <research@addisoncrump.info>"]
edition = "2018"
[lib]
crate-type = ["cdylib", "rlib"]
[features]
default = ["console_error_panic_hook"]
[dependencies]
js-sys = "0.3"
wasm-bindgen = "0.2.63"
libafl = { path = "../../libafl", default-features = false }
# The `console_error_panic_hook` crate provides better debugging of panics by
# logging them with `console.error`. This is great for development, but requires
# all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
# code size when deploying.
console_error_panic_hook = { version = "0.1.6", optional = true }
[dependencies.web-sys]
version = "0.3"
features = ['console', 'Window', 'Performance', 'PerformanceTiming']
[dev-dependencies]
wasm-bindgen-test = "0.3.13"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1

View File

@ -0,0 +1,29 @@
[env]
FUZZER_NAME="fuzzer"
PROJECT_DIR = { script = ["pwd"] }
[tasks.unsupported]
script_runner="@shell"
script='''
echo "Cargo-make not integrated yet on this"
'''
# Fuzzer
[tasks.build]
command = "wasm-pack"
args = ["build", "--target", "web"]
# Test
[tasks.test]
linux_alias = "test_unix"
mac_alias = "test_unix"
windows_alias = "unsupported"
[tasks.test_unix]
command = "wasm-pack"
args = ["test", "--chrome", "--headless"]
# Clean
[tasks.clean]
command = "cargo"
args = ["clean"]

View File

@ -0,0 +1,12 @@
# libafl-wasm
A brief demo demonstrating libafl's compatibility with WASM, and how to do it.
In this example, the entire LibAFL harness and target are present in a WASM binary, which is then loaded by [the example
webpage](pkg/index.html). To run this example, do `cargo make build`, then open [the example webpage](pkg/index.html) in
your browser (via something like `python3 -m http.server`). The fuzzer will execute until finding a solution and will
write the fuzzer log to your console.
In a real fuzzing campaign, you would likely need to also create a LibAFL Corpus implementation which was backed by
JavaScript, and restart the fuzzing campaign by re-invoking the fuzzer and providing the associated corpora. This is
not demonstrated in this barebones example.

Some files were not shown because too many files have changed in this diff Show More