Compare commits

..

86 Commits

Author SHA1 Message Date
798aa2ceb9 remove dead code 2023-06-02 10:00:13 +02:00
183ff32beb igonre archives 2023-06-02 08:32:23 +02:00
89979b64d9 eval script wrangeling 2023-05-27 13:19:19 +02:00
35da9fdf24 HACK: interrupt limit for random fuzzing 2023-05-25 08:40:43 +02:00
1bca346b39 plot enpoints 2023-05-25 08:39:47 +02:00
8b90886299 paralellize plots 2023-05-23 12:06:14 +02:00
1bd7d853ac update plot script 2023-05-11 12:56:12 +02:00
253048e534 tweak time outputs 2023-05-10 09:25:22 +02:00
52cc00fedc add run_until_saturation 2023-05-08 18:23:32 +02:00
eec998c426 update snakefile 2023-05-04 11:47:56 +02:00
a328ddfd5f fix empty iterator crash, restart 2023-05-02 09:41:53 +02:00
6a042da5c1 set up configurations 2023-04-28 13:11:48 +02:00
2e20a22dc6 add missing use 2023-04-27 13:36:01 +02:00
bbc83ef6be randomize interrupts until wort 2023-04-24 15:33:03 +02:00
48466ac2d7 Test: remove pc from hash 2023-04-24 12:52:29 +02:00
ad8cecdba4 Test: hash notification states 2023-04-24 12:51:09 +02:00
c2afc0186e allow plotting from remote mount 2023-04-24 11:16:10 +02:00
4df67db479 update snakefile 2023-04-24 11:12:38 +02:00
402eff7b47 small fixes 2023-04-21 17:22:22 +02:00
a8a6c175c8 WIP: add simple interrupt time randomizer 2023-04-21 17:11:18 +02:00
8a79e12f91 update target_symbols 2023-04-21 14:12:04 +02:00
a3e38b6abb skip unchanged interrupts 2023-04-20 16:50:23 +02:00
eb04325f09 fix staeg setup 2023-04-20 16:32:19 +02:00
cfb8fa2b32 fix use 2023-04-20 16:04:45 +02:00
2889e9bf61 WIP: move interrupt mutation to new stage 2023-04-20 15:50:22 +02:00
960764cf85 wip: interrupt placement 2023-04-17 17:33:21 +02:00
e6816cc2de add interrupt mutator 2023-04-17 09:50:18 +02:00
f3180a35cc plot min and max lines 2023-03-23 13:20:23 +01:00
54312b2577 plot lines instead of points 2023-03-22 16:10:19 +01:00
6d920fd962 fixes 2023-03-21 16:58:44 +01:00
281979ecd8 revert changes 2023-03-21 16:39:21 +01:00
c628afaa81 add generation based genetic testing 2023-03-21 16:34:05 +01:00
c548c6bc09 snakefile: dump cases, fix random fuzzing 2023-03-17 11:15:55 +01:00
6e8769907d add a new scheduler for systemtraces 2023-03-16 16:13:16 +01:00
bf639e42fa fix snakefile, symbols 2023-03-14 17:08:05 +01:00
a05ff97d0c seed rng from SEED_RANDOM 2023-03-13 14:45:21 +01:00
f09034b7fe determinism fixes, scheduler precision, restarts 2023-03-13 14:43:58 +01:00
d118eeacbd switch to native breakpoints 2023-03-13 12:19:24 +01:00
57fc441118 fix interrupt config 2023-03-09 17:21:26 +01:00
10b5fe8a74 fix rng seed 2023-03-09 10:53:40 +01:00
7f987b037d configure restarting manager 2023-03-09 10:16:08 +01:00
58be280a62 add micro_longint 2023-03-03 12:30:36 +01:00
3c586f5047 fuzz multiple interrupts 2023-03-02 15:30:53 +01:00
9336b932d0 rework plotting 2023-02-28 17:01:04 +01:00
e0f73778e2 add interrupt fuzzing 2023-02-27 10:39:52 +01:00
e5ac5ba825 dump time for showmap 2023-02-24 12:25:08 +01:00
2acf3ef301 add plotting to snakefile 2023-02-24 12:07:53 +01:00
28bac2a850 add feed_longest to record random cases 2023-02-23 22:33:13 +01:00
41586dd8b1 plotting: respect types 2023-02-23 22:28:25 +01:00
7420aabeeb change feedback order 2023-02-20 12:28:39 +01:00
d118ff0056 fix build 2023-02-19 19:25:43 +01:00
dfe4f713b9 fix feedbacks 2023-02-19 18:38:31 +01:00
f7a05d2a7c benchmark using snakemake 2023-02-16 22:56:43 +01:00
2593bdf42f trace_abbs and dump path 2023-02-15 09:17:48 +01:00
8c8ab7c44e add graph feedback 2023-02-10 13:46:07 +01:00
9cadc5d61c update input sizes, dump worstcase, benchmarking 2023-02-07 14:59:21 +01:00
594554eca0 remove address translations, extend plots 2023-01-26 14:03:18 +01:00
267309b954 add hists to plot script 2023-01-26 09:47:12 +01:00
35435fbd97 speed up random generation 2023-01-25 16:14:17 +01:00
8fcc54bbdd write out times over time 2023-01-25 14:55:04 +01:00
1f538f9834 add sytemstate sceduler, fuzz until time 2023-01-25 12:59:17 +01:00
ba01f600ee re-add system state fuzzing 2023-01-24 09:11:45 +01:00
2cb479581d add virtual edge to longest runs 2023-01-19 10:33:13 +01:00
1fbf948478 do not force generated inputs 2023-01-17 10:26:27 +01:00
6e1d5695e3 debug stuff 2023-01-17 10:18:24 +01:00
8d31196614 random seeds, better plots 2023-01-17 10:01:15 +01:00
4c90144db5 add more benchmarks 2023-01-13 16:05:43 +01:00
eeaf7eb43f exectime increase feedback 2023-01-11 16:09:06 +01:00
68c4887dad rename bin, allow random fuzzing 2023-01-09 13:53:32 +01:00
7ca2d43f3d benchmark with duration 2023-01-09 12:39:51 +01:00
9f97852e4a add benchmark scripts 2023-01-09 12:39:35 +01:00
f4e1990387 add systemstate feature and dump times 2023-01-05 17:34:53 +01:00
d936234976 fix multicore build 2023-01-05 13:35:51 +01:00
795fbff61a ignore artifacts 2023-01-05 13:31:33 +01:00
6a9df35e28 minimal changes 2023-01-05 13:30:24 +01:00
9b9fbc3677 add interrupt injection 2023-01-03 20:09:45 +01:00
decae09931 input length and read input pointer 2022-12-23 15:32:20 +01:00
b812e994a6 draft: add graph feedback 2022-12-19 18:14:52 +01:00
4587f442d0 add TimeMaximizerCorpusScheduler 2022-12-19 17:44:58 +01:00
c748fecbe2 add last api callsite to system state 2022-12-19 13:13:38 +01:00
7595d25192 libafl_qemu: add jmp instrumentation 2022-12-19 13:12:37 +01:00
79bca99cc7 WIP: add systemstate tracking 2022-12-15 15:23:07 +01:00
b07f7ccbca add arguments 2022-12-12 17:41:33 +01:00
e3f38edd0a get time from ClockTimeFeedback 2022-12-12 15:30:05 +01:00
6ad55e3b29 fixup 2022-12-12 15:16:45 +01:00
f7ee38ebb2 WIP: port fret 2022-12-12 14:58:28 +01:00
310 changed files with 4224 additions and 6843 deletions

View File

@ -61,12 +61,16 @@ jobs:
with: with:
profile: minimal profile: minimal
toolchain: stable toolchain: stable
- name: set mold linker as default linker
uses: rui314/setup-mold@v1
- name: Install and cache deps - name: Install and cache deps
uses: awalsh128/cache-apt-pkgs-action@v1.1.0 uses: awalsh128/cache-apt-pkgs-action@v1.1.0
with: 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
- name: get clang version - name: get clang version
run: command -v llvm-config && clang -v 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 - 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 --allow-downgrade
- uses: actions/checkout@v3 - uses: actions/checkout@v3
@ -91,35 +95,9 @@ jobs:
- name: Test Docs - name: Test Docs
run: cargo +nightly test --doc --all-features run: cargo +nightly test --doc --all-features
# ---- build normal and examples ---- # ---- build and feature check ----
- name: Run a normal build - name: Run a normal build
run: cargo build --verbose run: cargo build --verbose
- name: Build examples
run: cargo build --examples --verbose
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
- 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 # cargo-hack's --feature-powerset would be nice here but libafl has a too many knobs
- name: Check each feature - name: Check each feature
# Skipping `python` as it has to be built with the `maturin` tool # Skipping `python` as it has to be built with the `maturin` tool
@ -128,6 +106,8 @@ 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 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 - name: Check nightly features
run: cargo +nightly check --features=agpl && cargo +nightly check --features=nautilus run: cargo +nightly check --features=agpl && cargo +nightly check --features=nautilus
- name: Build examples
run: cargo build --examples --verbose
ubuntu-concolic: ubuntu-concolic:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -150,6 +130,8 @@ jobs:
with: with:
profile: minimal profile: minimal
toolchain: stable toolchain: stable
- name: set mold linker as default linker
uses: rui314/setup-mold@v1
- name: Install deps - 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
- name: Install maturin - name: Install maturin
@ -157,9 +139,7 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: Run a maturin build - name: Run a maturin build
run: cd ./bindings/pylibafl && python3 -m venv .env && . .env/bin/activate && maturin develop && ./test.sh run: cd ./bindings/pylibafl && maturin build
- name: Run python test
run: . ./bindings/pylibafl/.env/bin/activate && cd ./fuzzers/baby_fuzzer && python3 baby_fuzzer.py | grep "Bye"
fuzzers: fuzzers:
strategy: strategy:
@ -171,25 +151,24 @@ jobs:
with: with:
profile: minimal profile: minimal
toolchain: stable 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 - 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 --allow-downgrade
- name: Add no_std toolchain - 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 run: rustup toolchain install nightly-x86_64-unknown-linux-gnu ; rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
- name: Install cxxbridge - name: Install python
if: runner.os == 'macOS' if: runner.os == 'macOS'
run: cargo install cxxbridge-cmd run: brew install --force-bottle --overwrite python@3.11
- 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 - uses: lyricwulf/abc@v1
with: with:
# todo: remove afl++-clang when nyx support samcov_pcguard # 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
# update bash for macos to support `declare -A` command` # update bash for macos to support `declare -A` command`
macos: llvm libpng nasm coreutils z3 bash wget macos: llvm libpng nasm coreutils z3 bash
- name: pip install - name: pip install
run: python3 -m pip install msgpack jinja2 find_libpython run: python3 -m pip install msgpack jinja2
# Note that nproc needs to have coreutils installed on macOS, so the order of CI commands matters. # Note that nproc needs to have coreutils installed on macOS, so the order of CI commands matters.
- name: enable mult-thread for `make` - name: enable mult-thread for `make`
run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)" run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)"
@ -200,14 +179,13 @@ jobs:
- uses: actions/checkout@v3 - uses: actions/checkout@v3
with: with:
submodules: true # recursively checkout submodules submodules: true # recursively checkout submodules
fetch-depth: 0 # to diff with origin/main
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: Build and run example fuzzers (Linux) - name: Build and run example fuzzers (Linux)
if: runner.os == 'Linux' if: runner.os == 'Linux'
run: RUN_ON_CI=1 ./scripts/test_all_fuzzers.sh run: ./scripts/test_all_fuzzers.sh
- name: Build and run example fuzzers (macOS) - name: Build and run example fuzzers (macOS)
if: runner.os == 'macOS' # use bash v4 if: runner.os == 'macOS' # use bash v4
run: /usr/local/bin/bash -c 'RUN_ON_CI=1 ./scripts/test_all_fuzzers.sh' run: /usr/local/bin/bash ./scripts/test_all_fuzzers.sh
nostd-build: nostd-build:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -260,14 +238,10 @@ jobs:
- name: install cargo-make - name: install cargo-make
run: cargo install --force cargo-make run: cargo install --force cargo-make
- uses: ilammy/msvc-dev-cmd@v1 - uses: ilammy/msvc-dev-cmd@v1
- name: install cxx bridge
run: cargo install cxxbridge-cmd
- name: Build fuzzers/frida_libpng - name: Build fuzzers/frida_libpng
run: cd fuzzers/frida_libpng/ && cargo make test run: cd fuzzers/frida_libpng/ && cargo make test
- name: Build fuzzers/frida_gdiplus - name: Build fuzzers/frida_gdiplus
run: cd fuzzers/frida_gdiplus/ && cargo make test run: cd fuzzers/frida_gdiplus/ && cargo make test
- name: Build fuzzers/tinyinst_simple
run: cd fuzzers/tinyinst_simple/ && cargo make test
macos: macos:
runs-on: macOS-latest runs-on: macOS-latest
@ -280,8 +254,6 @@ jobs:
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
- name: Install deps - name: Install deps
run: brew install z3 gtk+3 run: brew install z3 gtk+3
- name: Install cxxbridge
run: cargo install cxxbridge-cmd
- uses: actions/checkout@v3 - uses: actions/checkout@v3
- uses: Swatinem/rust-cache@v2 - uses: Swatinem/rust-cache@v2
- name: MacOS Build - name: MacOS Build

View File

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

View File

@ -1,6 +1,6 @@
# LibAFL, the fuzzer library. # LibAFL, the fuzzer library.
<img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/main/static/libafl_logo.svg" alt="LibAFL logo" width="250" heigh="250"> <img align="right" src="https://github.com/AFLplusplus/Website/raw/master/static/logo_256x256.png" alt="AFL++ Logo">
Advanced Fuzzing Library - Slot your own fuzzers together and extend their features using Rust. Advanced Fuzzing Library - Slot your own fuzzers together and extend their features using Rust.

View File

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

View File

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

View File

@ -1,11 +0,0 @@
#!/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

View File

@ -1,8 +1,7 @@
# Concolic Tracing and Hybrid Fuzzing # Concolic Tracing and Hybrid Fuzzing
LibAFL has support for concolic tracing based on the [SymCC](https://github.com/eurecom-s3/symcc) instrumenting compiler. LibAFL has support for concolic tracing based on the [SymCC](https://github.com/eurecom-s3/symcc) instrumenting compiler.
For those uninitiated, the following text attempts to describe concolic tracing from the ground up using an example. For those uninitiated, the following 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. 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. Finally, we'll walk through building a basic hybrid fuzzer using LibAFL.
@ -93,18 +92,18 @@ In hybrid fuzzing, we combine this tracing + solving approach with more traditio
The concolic tracing support in LibAFL is implemented using SymCC. 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 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. 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 is similar to the previous example. These callbacks allow the runtime to construct a trace that similar to the previous example.
### SymCC and its Runtimes ### SymCC and its Runtimes
SymCC ships with 2 runtimes: SymCC ships with 2 runtimes:
* 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 '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 them using Z3. * a [QSym](https://github.com/sslab-gatech/qsym)-based runtime, which does a bit more filtering on the expressions and also solves 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. 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. 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.
Check out the `symcc_runtime` docs for more information on how to build your own runtime. Checkout out the `symcc_runtime` docs for more information on how to build your own runtime.
### SymQEMU ### SymQEMU
@ -124,7 +123,7 @@ There are three main steps involved with building a hybrid fuzzer using LibAFL:
3. building the fuzzer. 3. building the fuzzer.
Note that the order of these steps is important. Note that the order of these steps is important.
For example, we need to have a runtime ready before we can do instrumentation with SymCC. For example, we need to have runtime ready before we can do instrumentation with SymCC.
### Building a Runtime ### Building a Runtime
@ -135,12 +134,10 @@ Check out the [example hybrid fuzzer's runtime](https://github.com/AFLplusplus/L
### Instrumentation ### Instrumentation
There are two main instrumentation methods to make use of concolic tracing in LibAFL: 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. 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**. * Using **SymQEMU** to dynamically instrument the target at **runtime**.
This avoids building a separate instrumented target with concolic tracing instrumentation and so does not require source code. This avoids a separate instrumented target with concolic tracing instrumentation and 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. 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. Therefore, it is recommended to use SymCC over SymQEMU when possible.
@ -161,23 +158,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). 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. By default, SymQEMU looks for the runtime in a sibling directory.
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. 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.
### Building the Fuzzer ### Building the Fuzzer
No matter the instrumentation method, the interface between the fuzzer and the instrumented target should now be consistent. 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: 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 its 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 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)). 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 (set to 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 the input file path, if your target reads input from a file (instead of standard input).
#### Serialization and Solving #### 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). 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). 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 to solve the conditions using an SMT-Solver. The `ConcolicMetadata` can be used to replay the concolic trace and solved using an SMT-Solver.
Most use-cases involving concolic tracing, however, will need to define some policy around which branches they want to solve. 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. 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. 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. 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. Thus, you have to compile your harness to a dynamic library. Frida instruments your PUT after dynamically loading it.
In our `frida_libpng` example, we load the dynamic library and find the symbol to harness as follows: For example in our `frida_libpng` example, we load the dynamic library and find the symbol to harness as follows:
```rust,ignore ```rust,ignore
let lib = libloading::Library::new(module_name).unwrap(); let lib = libloading::Library::new(module_name).unwrap();
@ -28,9 +28,9 @@ In our `frida_libpng` example, we load the dynamic library and find the symbol t
## `FridaInstrumentationHelper` and Runtimes ## `FridaInstrumentationHelper` and Runtimes
To use functionalities that Frida offers, we'll first need to obtain a `Gum` object by `Gum::obtain()`. To use functionalities that Frida offers, we'll first need to obtain `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 instrumentations. 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.
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. 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. 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 ## Running the Fuzzer
After setting up the `FridaInstrumentationHelper` you can obtain the pointer to the coverage map by calling `map_mut_ptr()`. After setting up the `FridaInstrumentationHelper`. You can obtain the pointer to the coverage map by calling `map_ptr_mut()`.
```rust,ignore ```rust,ignore
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr( let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
"edges", "edges",
frida_helper.map_mut_ptr().unwrap(), frida_helper.map_ptr_mut().unwrap(),
MAP_SIZE, MAP_SIZE,
)); ));
``` ```
@ -83,5 +83,5 @@ You can then link this observer to `FridaInProcessExecutor` as follows:
); );
``` ```
And finally you can run the fuzzer. 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. 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 # Using LibAFL in `no_std` environments
It is possible to use LibAFL in `no_std` environments e.g. on custom platforms like microcontrollers, kernels, hypervisors, and more. It is possible to use LibAFL in `no_std` environments e.g. custom platforms like microcontrollers, kernels, hypervisors, and more.
You can simply add LibAFL to your `Cargo.toml` file: 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 ## Use custom timing
The minimum amount of support LibAFL needs for a `no_std` environment is a monotonically increasing timestamp. The minimum amount of input LibAFL needs for `no_std` 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. For this, anywhere in your project you need to implement the `external_current_millis` function, which returns the current time in milliseconds.
```c ```c

View File

@ -2,12 +2,12 @@
NYX supports both source-based and binary-only fuzzing. NYX supports both source-based and binary-only fuzzing.
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: 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:
```bash ```bash
git clone https://github.com/AFLplusplus/AFLplusplus git clone https://github.com/AFLplusplus/AFLplusplus
cd AFLplusplus cd AFLplusplus
make all # this will not compile afl's additional extensions make all # this will not compile afl's additional extension
``` ```
Then you should compile the target with the afl++ compiler wrapper: Then you should compile the target with the afl++ compiler wrapper:
@ -20,9 +20,9 @@ export CXX=afl-clang-fast++
make make
``` ```
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>. 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>.
## Preparing the Nyx working directory ## Preparing 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): 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 ## Standalone fuzzing
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. 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.
First, to create `Nyxhelper`: First, to create `Nyxhelper`:
@ -57,21 +57,22 @@ First, to create `Nyxhelper`:
let share_dir = Path::new("/tmp/nyx_libxml2/"); let share_dir = Path::new("/tmp/nyx_libxml2/");
let cpu_id = 0; // use first cpu let cpu_id = 0; // use first cpu
let parallel_mode = false; // close parallel_mode let parallel_mode = false; // close parallel_mode
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 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
``` ```
Then, fetch `trace_bits`, create an observer and the `NyxExecutor`: Then, fetch `trace_bits`, create an observer and the `NyxExecutor`:
```rust,ignore ```rust,ignore
let observer = unsafe { StdMapObserver::from_mut_ptr("trace", helper.trace_bits, helper.map_size) }; let trace_bits = unsafe { std::slice::from_raw_parts_mut(helper.trace_bits, helper.map_size) };
let observer = StdMapObserver::new("trace", trace_bits);
let mut executor = NyxExecutor::new(&mut helper, tuple_list!(observer)).unwrap(); let mut executor = NyxExecutor::new(&mut helper, tuple_list!(observer)).unwrap();
``` ```
Finally, use them normally and pass them into `fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)` to start fuzzing. Finally, use them as normal and pass them into `fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)` to start fuzzing.
## Parallel fuzzing ## Parallel fuzzing
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. 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.
Parallel fuzzing relies on [`Launcher`](../message_passing/spawn_instances.md), so spawn logic should be written in the scoop of anonymous function `run_client`: Parallel fuzzing relies on [`Launcher`](../message_passing/spawn_instances.md), so spawn logic should be written in the scoop of anonymous function `run_client`:
@ -90,7 +91,7 @@ let mut helper = NyxHelper::new(
cpu_id, // current cpu id cpu_id, // current cpu id
true, // open snap_mode true, // open snap_mode
parallel_mode, // open parallel mode parallel_mode, // open parallel mode
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 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
) )
.unwrap(); .unwrap();
``` ```
@ -98,11 +99,13 @@ let mut helper = NyxHelper::new(
Then you can fetch the trace_bits and create an observer and `NyxExecutor` Then you can fetch the trace_bits and create an observer and `NyxExecutor`
```rust,ignore ```rust,ignore
let observer = unsafe { StdMapObserver::from_mut_ptr("trace", helper.trace_bits, helper.map_size) } let trace_bits =
unsafe { std::slice::from_raw_parts_mut(helper.trace_bits, helper.map_size) };
let observer = StdMapObserver::new("trace", trace_bits);
let mut executor = NyxExecutor::new(&mut helper, tuple_list!(observer)).unwrap(); let mut executor = NyxExecutor::new(&mut helper, tuple_list!(observer)).unwrap();
``` ```
Finally, open a `Launcher` as usual to start fuzzing: Finally, open a `Launcher` as normal to start fuzzing:
```rust,ignore ```rust,ignore
match Launcher::builder() match Launcher::builder()

View File

@ -105,8 +105,8 @@ fn main(){
## Generating and running some tests ## 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 will evolve during the fuzzing process. 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.
It includes all state, such as the Corpus of inputs, the current RNG state, and potential Metadata for the testcases and run. 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: In our `main` we create a basic State instance like the following:
```rust,ignore ```rust,ignore
@ -129,8 +129,8 @@ 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`. 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`.
- 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. - 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. - 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. 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.
@ -225,7 +225,7 @@ 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. **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. 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 `signals_set` in our harness: As we don't rely on any instrumentation engine, we have to manually track the satisfied conditions by `singals_set` in our harness:
```rust ```rust
extern crate libafl; extern crate libafl;
@ -287,7 +287,7 @@ 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. 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 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. **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.
We need to update our State creation including the feedback state and the Fuzzer including the feedback and the objective: We need to update our State creation including the feedback state and the Fuzzer including the feedback and the objective:
@ -356,7 +356,7 @@ fuzzer
`fuzz_loop` will request a testcase for each iteration to the fuzzer using the scheduler and then it will invoke the 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 and find the input that panics the function in less than a second. 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.
```text ```text
$ cargo run $ cargo run

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_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_tokens| basic **token level** fuzzer with token level mutations|
|baby_fuzzer_with_forkexecutor| example for **InProcessForkExecutor**| |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 bare 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 barew 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. 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 program under test 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 tested program 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. 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.
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. 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. 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 installs 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 install a timeout callback before calling the original run function of the wrapped executor.
## InProcessExecutor ## InProcessExecutor
Let's begin with the base case; `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. 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 ## 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. 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. 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.
As you can see from the forkserver example, 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`. Finally, we'll talk about the `InProcessForkExecutor`.
`InProcessForkExecutor` has only one difference from `InprocessExecutor`; It forks before running the harness and that's it. `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. 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. 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. 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 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 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.
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). 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).
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. 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.
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. 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 ## 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`. 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. 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. 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.

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. 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 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. 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.
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 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. 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 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). 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).

View File

@ -1,9 +1,9 @@
# Mutator # Mutator
The Mutator is an entity that takes one or more Inputs and generates a new instance of Input derived by its inputs. The Mutator is an entity that takes one or more Inputs and generates a new derived one.
Mutators can be composed, and they are generally linked to a specific Input type. 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 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. 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.
In LibAFL, [`Mutator`](https://docs.rs/libafl/*/libafl/mutators/trait.Mutator.html) is a trait. 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`. 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 collect the time spent executing a run, the program output, or a more advanced observation, like maximum stack depth at runtime. 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 an observation of a dynamic property of the program. This information is not preserved across runs, and it 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. 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 # Stage
A Stage is an entity that operates on a single Input received from the Corpus. A Stage is an entity that operates on a single Input got 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 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. 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.
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. 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. 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 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. 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.
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). 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. The struct must be static, so it cannot hold references to borrowed objects.
As an alternative to `derive(SerdeAny)` which is a proc-macro in `libafl_derive` the user can use `libafl::impl_serdeany!(MyMetadata);`. As an alternative to `derive(SerdeAny)` that is a proc-macro in `libafl_derive` the user can use `libafl::impl_serdeany!(MyMetadata);`.
## Usage ## 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 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 define a generic for the input type, the state type, or the generic type, as these is no longer necessary to explicitly defined 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 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. (`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 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. main changes to the API are in the use of "Uses*" for associated types.
In many scenarios, Input, Observer, and State generics have been moved into traits with associated types (namely, In many scenarios, Input, Observers, 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 "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 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. 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`. Also `Corpus` no longer has a generic for After 0.9, all `Corpus` implementations are required to implement `UsesInput` and `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: the input type (as it is now provided by the UsesInput impl). The migrated implementation is shown below:
```rust,ignore ```rust,ignore
@ -160,26 +160,3 @@ Now, `Corpus` cannot be accidentally implemented for another type other than tha
is fixed to the associated type for `UsesInput`. 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. 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 $ cargo build --release
``` ```
Note that the `--release` flag is optional for development, but you need to add it to do fuzzing at a decent speed. Note that the `--release` flag is optional for development, but you needed to add it to fuzzing at a decent speed.
Slowdowns of 10x or more are not uncommon for Debug builds. Slowdowns of 10x or more are not uncommon for Debug builds.
The LibAFL repository is composed of multiple crates. The LibAFL repository is composed of multiple crates.

View File

@ -10,7 +10,7 @@ libafl = { version = "*" }
## Crate List ## Crate List
For LibAFL, each crate has its self-contained purpose, and the user may not need to use all of them in their project. For LibAFL, each crate has its self-contained purpose, and the user may not need to use all of them in its project.
Following the naming convention of the folders in the project's root, they are: Following the naming convention of the folders in the project's root, they are:
### [`libafl`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl) ### [`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: Currently, the supported flags are:
- `pcguard_edges` defines the SanitizerCoverage trace-pc-guard hooks to track the executed edges in a map. - `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. - `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. - `value_profile` defines the SanitizerCoverage trace-cmp hooks to track the matching bits of each comparison in a map.
### libafl_cc ### libafl_cc
This is a library that provides utils to wrap compilers and create source-level fuzzers. This is a library that provides utils wrap compilers and create source-level fuzzers.
At the moment, only the Clang compiler is supported. At the moment, only the Clang compiler is supported.
To understand it deeper, look through the tutorials and examples. 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 `>` > previous command. Additionally, PowerShell-specific examples will use `>`
> rather than `$`. > rather than `$`.
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. 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.
This gets you the example fuzzers, additional utilities, and latest patches. This gets you the example fuzzers, additional utilities, and latest patches.
The easiest way to do this is to use `git`. The easiest way to do this is to use `git`.
```sh ```sh
$ git clone https://github.com/AFLplusplus/LibAFL.git $ git clone git@github.com:AFLplusplus/LibAFL.git
``` ```
Alternatively, on a UNIX-like machine, you can download a compressed archive and extract it with: You can alternatively, on a UNIX-like machine, download a compressed archive and extract it with:
```sh ```sh
wget https://github.com/AFLplusplus/LibAFL/archive/main.tar.gz wget https://github.com/AFLplusplus/LibAFL/archive/main.tar.gz
@ -31,7 +31,7 @@ $ ls LibAFL-main # this is the extracted folder
## Clang installation ## Clang installation
One of the external dependencies of LibAFL is the Clang C/C++ compiler. One of the external dependencies of LibAFL is the Clang C/C++ compiler.
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. 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.
For these parts, we use C to expose the missing functionalities to our Rust codebase. 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, 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. 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. From the perspective of a power user, however, these tools are limited.
Their designs do not treat extensibility as a first-class citizen. Their design does 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. 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. In any case, researchers end up with tons of fuzzers, all of which are incompatible with each other.
Their outstanding features cannot just be combined for new projects. Their outstanding features can not 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. 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. 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. 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? 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. - `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-school fuzzers, a `BytesInput` is just one of the potential forms of inputs: To give an example, as opposed to old-skool 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. 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. - `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. 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* *by Andrea Fioraldi and Dominik Maier*
Welcome to LibAFL, the Advanced Fuzzing Library. Welcome to LibAFL, the Advanced Fuzzing Library.
This book shall be a gentle introduction to the library. This book shall be a gentle introduction into the library.
This version of the LibAFL book is coupled with the release 1.0 beta of the library. This version of the LibAFL book is coupled with the release 1.0 beta of the library.

View File

@ -1,6 +1,6 @@
# Message Passing # Message Passing
LibAFL offers a standard mechanism for message passing between processes and machines with a low overhead. LibAFL offers a standard mechanism for message passing over 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. 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 an `OnDiskCorpus`.
@ -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. 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. 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. The broker reads from all client's `ShMem` maps.
It periodically checks all incoming client maps and then forwards new messages to its outgoing broadcast-`ShMem`, mapped by all connected clients. It checks all incoming client maps periodically 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. 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`. 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. 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, 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`. 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`.
The schema for client's maps to the broker is as follows: 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`). 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. 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 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. 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
They just need to re-read all broadcasted messages from start to finish. They just need to re-read all broadcasted messages from start to finish.
So the outgoing messages flow is like this over the outgoing broadcast `Shmem`: So the outgoing messages flow like this over the outgoing broadcast `Shmem`:
```text ```text
[broker] [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. 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. 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, binding itself to the port to await new clients. 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 already bound, the EventManager will try to connect to it. 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. 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. 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-like operating systems as optimization and only actually restarts from scratch on Windows. While it's called "restarting" manager, it uses `fork` on Unix operating systems as optimization and only actually restarts from scratch on Windows.
## Automated, with Launcher ## Automated, with Launcher
@ -23,7 +23,7 @@ While it's called "restarting" manager, it uses `fork` on Unix-like operating sy
The Launcher is the lazy way to do multiprocessing. 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. 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 an 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 individual fuzzer. Then you can specify the `shmem_provider`,`broker_port`,`monitor`,`cores` and other stuff through `Launcher::builder()`:
```rust,ignore ```rust,ignore
Launcher::builder() 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`. 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`. 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. For each client, `run_client` will be called.
On Windows, the Launcher will restart each client, while on Unix-alikes, it will use `fork`. On Windows, the Launcher will restart each client, while on Unix, it will use `fork`.
Advanced use-cases: 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`. 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 instances but one. 2. To use multiple launchers for individual configurations, you can set `spawn_broker` to `false` on all but one.
3. Launcher will not select the cores automatically, so you need to specify the `cores` that you want. 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). 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. 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 to writing extensions to LibAFL like Feedbacks and Testcase's metadata. This tutorial will introduce you in writing extensions to LibAFL like Feedbacks and Testcase's metadata.

View File

@ -5,17 +5,26 @@ authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenuk
edition = "2021" edition = "2021"
[features] [features]
default = ["std", "snapshot_restore", "singlecore", "feed_longest", "feed_afl"] default = ["std", "snapshot_restore", "singlecore", "restarting", "feed_systemtrace", "fuzz_int" ]
std = [] std = []
snapshot_restore = [] snapshot_restore = []
snapshot_fast = [ "snapshot_restore" ] snapshot_fast = [ "snapshot_restore" ]
singlecore = [] singlecore = []
restarting = ['singlecore']
trace_abbs = [] trace_abbs = []
systemstate = [] systemstate = []
feed_systemgraph = [ "systemstate" ] feed_systemgraph = [ "systemstate" ]
feed_systemtrace = [ "systemstate" ] feed_systemtrace = [ "systemstate" ]
feed_longest = [ ] feed_longest = [ ]
feed_afl = [ ] feed_afl = [ ]
feed_genetic = [ ]
fuzz_int = [ ]
gensize_1 = [ ]
gensize_10 = [ ]
gensize_100 = [ ]
observer_hitcounts = []
no_hash_state = []
run_until_saturation = []
[profile.release] [profile.release]
lto = true lto = true

View File

@ -8,3 +8,5 @@ mnt
*.pdf *.pdf
bins bins
.snakemake .snakemake
*.zip
*.tar.*

View File

@ -1,5 +1,16 @@
import csv import csv
def_flags="--no-default-features --features std,snapshot_restore,singlecore" 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: rule build_showmap:
output: output:
@ -19,17 +30,17 @@ rule build_feedlongest:
shell: shell:
"cargo build --target-dir {output} {def_flags},feed_longest" "cargo build --target-dir {output} {def_flags},feed_longest"
rule build_feedaflnolongest: rule build_frafl:
output: output:
directory("bins/target_feedaflnolongest") directory("bins/target_frafl")
shell: shell:
"cargo build --target-dir {output} {def_flags},feed_afl" "cargo build --target-dir {output} {def_flags},feed_afl,feed_longest"
rule build_afl: rule build_afl:
output: output:
directory("bins/target_afl") directory("bins/target_afl")
shell: shell:
"cargo build --target-dir {output} {def_flags},feed_afl,feed_longest" "cargo build --target-dir {output} {def_flags},feed_afl,observer_hitcounts"
rule build_state: rule build_state:
output: output:
@ -37,18 +48,102 @@ rule build_state:
shell: shell:
"cargo build --target-dir {output} {def_flags},feed_systemtrace" "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: rule build_graph:
output: output:
directory("bins/target_graph") directory("bins/target_graph")
shell: shell:
"cargo build --target-dir {output} {def_flags},feed_systemgraph" "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: rule run_bench:
input: input:
"build/{target}.elf", "build/{target}.elf",
"bins/target_{fuzzer}" "bins/target_{fuzzer}"
output: output:
multiext("timedump/{fuzzer}/{target}.{num}", "", ".log", ".case") multiext("timedump/{fuzzer}/{target}.{num}", "", ".log") # , ".case"
run: run:
with open('target_symbols.csv') as csvfile: with open('target_symbols.csv') as csvfile:
reader = csv.DictReader(csvfile) reader = csv.DictReader(csvfile)
@ -67,26 +162,29 @@ rule run_bench:
export FUZZ_INPUT={fuzz_input} export FUZZ_INPUT={fuzz_input}
export FUZZ_INPUT_LEN={fuzz_len} export FUZZ_INPUT_LEN={fuzz_len}
export BREAKPOINT={bkp} export BREAKPOINT={bkp}
export SEED_RANDOM=1 export SEED_RANDOM={wildcards.num}
export TIME_DUMP=$(pwd)/{output[0]} export TIME_DUMP=$(pwd)/{output[0]}
export CASE_DUMP=$(pwd)/{output[2]} export CASE_DUMP=$(pwd)/{output[0]}.case
export FUZZ_ITERS=3600 export TRACE_DUMP=$(pwd)/{output[0]}.trace
export FUZZ_ITERS={RUNTIME}
export FUZZER=$(pwd)/{input[1]}/debug/fret export FUZZER=$(pwd)/{input[1]}/debug/fret
set +e set +e
../fuzzer.sh > {output[1]} 2>&1 ../fuzzer.sh > {output[1]} 2>&1
exit 0 exit 0
""" """
if wildcards.fuzzer == 'random': if wildcards.fuzzer.find('random') >= 0:
script="export FUZZ_RANDOM=1\n"+script script="export FUZZ_RANDOM={output[1]}\n"+script
shell(script) shell(script)
rule run_showmap: rule run_showmap:
input: input:
"build/{target}.elf", "{remote}build/{target}.elf",
"bins/target_showmap", "bins/target_showmap",
"timedump/{fuzzer}/{target}.{num}.case" "bins/target_showmap_int",
"{remote}timedump/{fuzzer}/{target}.{num}.case"
output: output:
"timedump/{fuzzer}/{target}.{num}.trace.ron" "{remote}timedump/{fuzzer}/{target}.{num}.trace.ron",
"{remote}timedump/{fuzzer}/{target}.{num}.case.time",
run: run:
with open('target_symbols.csv') as csvfile: with open('target_symbols.csv') as csvfile:
reader = csv.DictReader(csvfile) reader = csv.DictReader(csvfile)
@ -98,53 +196,86 @@ rule run_showmap:
fuzz_input=line['input_symbol'] fuzz_input=line['input_symbol']
fuzz_len=line['input_size'] fuzz_len=line['input_size']
bkp=line['return_function'] bkp=line['return_function']
script=""" 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}) mkdir -p $(dirname {output})
export KERNEL=$(pwd)/{input[0]} export KERNEL=$(pwd)/{input[0]}
export FUZZ_MAIN={fuzz_main} export FUZZ_MAIN={fuzz_main}
export FUZZ_INPUT={fuzz_input} export FUZZ_INPUT={fuzz_input}
export FUZZ_INPUT_LEN={fuzz_len} export FUZZ_INPUT_LEN={fuzz_len}
export BREAKPOINT={bkp} export BREAKPOINT={bkp}
export TRACE_DUMP=$(pwd)/{output} export TRACE_DUMP=$(pwd)/{output[0]}
export FUZZER=$(pwd)/{input[1]}/debug/fret export DO_SHOWMAP=$(pwd)/{input[3]}
export DO_SHOWMAP=$(pwd)/{input[2]} export TIME_DUMP=$(pwd)/{output[1]}
set +e set +e
../fuzzer.sh ../fuzzer.sh
exit 0 exit 0
""" """
if wildcards.fuzzer == 'random': if wildcards.fuzzer.find('random') >= 0:
script="export FUZZ_RANDOM=1\n"+script script="export FUZZ_RANDOM=1\n"+script
shell(script) shell(script)
rule tarnsform_trace: rule tarnsform_trace:
input: input:
"timedump/{fuzzer}/{target}.{num}.trace.ron" "{remote}timedump/{fuzzer}/{target}.{num}.trace.ron"
output: output:
"timedump/{fuzzer}/{target}.{num}.trace.csv" "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv"
shell: shell:
"$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} > {output[0]}" "$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} > {output[0]}"
rule trace2gantt: rule trace2gantt:
input: input:
"timedump/{fuzzer}/{target}.{num}.trace.csv" "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv"
output: output:
"timedump/{fuzzer}/{target}.{num}.trace.csv.png" "{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png"
shell: shell:
"Rscript --vanilla $(pwd)/../../../../state2gantt/gantt.R {input}" "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: rule all_bins:
input: input:
"bins/target_random", expand("bins/target_{target}{flag}",target=['random','afl','frafl','state','feedgeneration100'],flag=['','_int'])
"bins/target_feedlongest",
"bins/target_feedaflnolongest",
"bins/target_afl",
"bins/target_state",
"bins/target_graph"
rule all_periodic:
input:
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','state','graph'], target=['waters'],num=range(0,10))
rule all_compare_afl_longest:
input:
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['afl','feedlongest','feedaflnolongest'], target=['waters'],num=range(0,10))

View File

@ -1,38 +1,55 @@
library("mosaic") 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) args = commandArgs(trailingOnly=TRUE)
#myolors=c("#339933","#0066ff","#993300") # grün, balu, rot
myolors=c("green","blue","red", "yellow", "pink", "black") # grün, balu, rot
if (length(args)==0) { if (length(args)==0) {
runtype="timedump" runtype="timedump_253048_1873f6_all/timedump"
target="waters" target="waters_int"
filename_1=sprintf("%s.png",target) outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/"
filename_2=sprintf("%s_maxline.png",target) #MY_SELECTION <- c('state', 'afl', 'graph', 'random')
filename_3=sprintf("%s_hist.png",target) SAVE_FILE=TRUE
} else { } else {
runtype=args[1] runtype=args[1]
target=args[2] target=args[2]
filename_1=sprintf("%s.png",args[2]) outputpath=args[3]
filename_2=sprintf("%s_maxline.png",args[2]) MY_SELECTION <- args[4:length(args)]
filename_3=sprintf("%s_hist.png",args[2]) SAVE_FILE=TRUE
# filename_1=args[3] }
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
} }
library("dplyr") #MY_COLORS=c("green","blue","red", "orange", "pink", "black")
COLORS <- c("dark grey","blue", "red", "green", "magenta", "orange", "cyan", "pink", "black", "orange") 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) BENCHDIR=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s",runtype)
BASENAMES=Filter(function(x) x!="",list.dirs(BENCHDIR,full.names=FALSE)) BASENAMES=Filter(function(x) x!="" && substr(x,1,1)!='.',list.dirs(BENCHDIR,full.names=FALSE))
PATTERNS=c(".*.0", PATTERNS="%s.[0-9]*$"
".*.1", #RIBBON='sd'
".*.2", #RIBBON='span'
".*.3", RIBBON='both'
".*.4", DRAW_WC = worst_case > 0
".*.5", LEGEND_POS="topright"
".*.6", #LEGEND_POS="bottomright"
".*.7", CONTINUE_LINE_TO_END=FALSE
".*.8",
".*.9") # 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 # Trimm a list of data frames to common length
trim_data <- function(input,len=NULL) { trim_data <- function(input,len=NULL) {
@ -42,17 +59,19 @@ trim_data <- function(input,len=NULL) {
return(lapply(input, function(d) slice_head(d,n=len))) return(lapply(input, function(d) slice_head(d,n=len)))
} }
length_of_data <- function(input) { length_of_data <- function(input) {
min(sapply(input, function(v) dim(v)[1])) min(sapply(input, function(v) dim(v)[1]))
} }
# Takes a flat list # Takes a flat list
trace2maxline <- function(tr) { trace2maxline <- function(tr) {
maxline = tr maxline = tr
for (var in seq_len(length(maxline))[2:length(maxline)]) { 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]) maxline[var] = max(maxline[var],maxline[var-1])
#}
} }
#plot(seq_len(length(maxline)),maxline,"l",xlab="Index",ylab="WOET") #plot(seq_len(length(maxline)),maxline,"l",xlab="Index",ylab="WOET")
return(maxline) return(maxline)
@ -75,91 +94,234 @@ frame2maxlines <- function(tr) {
return(tr) return(tr)
} }
maxlines2plot <- function(maxlines) { trace2maxpoints <- function(tr) {
min_length <- min(sapply(maxlines, function(v) dim(v)[1])) minval = tr[1,1]
ml_cut <- lapply(maxlines, function(v) v[1:min_length,]) collect = tr[1,]
plot(seq_len(min_length),unlist(ml_cut[[1]]),"l",xlab="Index",ylab="WOET",col=COLORS[1],ylim=c(best, worst)) for (i in seq_len(dim(tr)[1])) {
for (ml in seq_len(length(maxlines))[2:length(maxlines)]) { if (minval < tr[i,1]) {
lines(seq_len(min_length),unlist(ml_cut[ml]),"l",col=COLORS[ml]) collect = rbind(collect,tr[i,])
minval = tr[i,1]
} }
lines(seq_len(min_length),rep_len(best, min_length),col="black") }
lines(seq_len(min_length),rep_len(best_success, min_length),col="black") tmp = tr[dim(tr)[1],]
lines(seq_len(min_length),rep_len(worst_trace, min_length),col="green") tmp[1] = minval[1]
lines(seq_len(min_length),rep_len(worst, min_length),col="black") collect = rbind(collect,tmp)
legend("bottomright",legend=lapply(maxlines,names),col = COLORS[1:length(maxlines)], lwd=2) return(collect)
} }
frame2plot <- function(maxlines,colors=NULL,draw_extreme=TRUE,doint=FALSE,over_name=NULL) { sample_maxpoints <- function(tr,po) {
if (is.null(colors)) { index = 1
colors <- COLORS collect=NULL
} endpoint = dim(tr)[1]
min_length <- dim(maxlines)[1] for (p in po) {
cur_worst <- worst if (p<=tr[1,2]) {
if (doint) { tmp = tr[index,]
cur_worst <- worst_int tmp[2] = p
} collect = rbind(collect, tmp)
plot(seq_len(min_length),unlist(maxlines[[1]]),"l",xlab="Execution",ylab="Worst observed response time",col=colors[1],ylim=c(min(best,min(maxlines)), max(cur_worst,max(maxlines)))) } else if (p>=tr[endpoint,2]) {
for (ml in seq_len(length(maxlines))[2:length(maxlines)]) { tmp = tr[endpoint,]
lines(seq_len(min_length),unlist(maxlines[ml]),"l",col=colors[ml]) tmp[2] = p
} collect = rbind(collect, tmp)
if (draw_extreme) {
if (!doint) {
nam <- c(names(maxlines),"worst case") #,"best case"
col <- c(colors[1:length(maxlines)],"black") #,"black"
ltp <- c(rep_len("solid",length(maxlines)),"longdash") # ,"longdash"
lines(seq_len(min_length),rep_len(worst, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1])
#nam <- c(names(maxlines),"worst trace","worst case") #,"best case"
#col <- c(colors[1:length(maxlines)],"black","black") #,"black"
#ltp <- c(rep_len("solid",length(maxlines)),"dotted","longdash") # ,"longdash"
#lines(seq_len(min_length),rep_len(best, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1])
#lines(seq_len(min_length),rep_len(best_success, min_length),col="black")
#lines(seq_len(min_length),rep_len(worst_trace, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1])
#lines(seq_len(min_length),rep_len(worst, min_length),col=col[length(maxlines)+2],lty=ltp[length(maxlines)+2])
} else { } else {
nam <- c(names(maxlines),"worst case") for (i in seq(index,endpoint)-1) {
col <- c(colors[1:length(maxlines)],"black") if (p >= tr[i,2] && p<tr[i+1,2]) {
ltp <- c(rep_len("solid",length(maxlines)),"longdash") tmp = tr[i,]
lines(seq_len(min_length),rep_len(worst_int, min_length),col=col[length(maxlines)+1],lty=ltp[length(maxlines)+1]) tmp[2] = p
collect = rbind(collect, tmp)
index = i
break
} }
} else {
nam <- names(maxlines)
col <- colors[1:length(maxlines)]
ltp <- rep_len("solid",length(maxlines))
} }
if (is.null(over_name)) {
legend("bottomright", legend=nam, col=col, lty=ltp)
} else {
legend("bottomright", legend=over_name, col=col, lty=ltp)
} }
}
return(collect)
} }
all_maxlines = c() #https://www.r-bloggers.com/2012/01/parallel-r-loops-for-windows-and-linux/
for (bn in BASENAMES) { all_runtypetables <- foreach (bn=BASENAMES) %do% {
runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern=sprintf("%s.[0-9]$",target),full.names = TRUE) runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern=sprintf(PATTERNS,target),full.names = TRUE)
if (length(runtypefiles) > 0) { if (length(runtypefiles) > 0) {
runtypetables = lapply(runtypefiles, function(x) read.table(x, quote="\"", comment.char="", col.names=c(bn))) runtypetables_reduced <- foreach(i=seq_len(length(runtypefiles))) %dopar% {
runtypetables = trim_data(runtypetables) rtable = read.csv(runtypefiles[[i]], col.names=c(sprintf("%s%d",bn,i),sprintf("times%d",i)))
list_of_maxlines = data2maxlines(runtypetables) trace2maxpoints(rtable)
mean_maxline<-Reduce(function(a,b) a+b,list_of_maxlines,0)/length(runtypetables) }
all_maxlines=append(all_maxlines,mean_maxline) #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))
} }
} }
min_length <- min(sapply(all_maxlines, length)) all_runtypetables = all_runtypetables[lapply(all_runtypetables, length) > 0]
all_maxlines=lapply(all_maxlines, function(v) v[1:min_length]) all_min_points = foreach(rtt=all_runtypetables,.combine = cbind) %do% {
one_frame<-data.frame(all_maxlines) bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1)
one_frame[length(one_frame)+1] <- seq_len(length(one_frame[[1]])) ret = data.frame(min(unlist(lapply(rtt, function(v) v[dim(v)[1],2]))))
names(one_frame)[length(one_frame)] <- 'iters' names(ret)[1] = bn
ret/(3600 * 1000)
ylow=min(one_frame[1:length(one_frame)-1]) }
yhigh=max(one_frame[1:length(one_frame)-1]) all_max_points = foreach(rtt=all_runtypetables,.combine = cbind) %do% {
bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1)
plot(c(1,length(one_frame[[1]])),c(ylow,yhigh), col='white', xlab="iters", ylab="wcet", pch='.') ret = data.frame(max(unlist(lapply(rtt, function(v) v[dim(v)[1],2]))))
names(ret)[1] = bn
typenames = names(one_frame)[which(names(one_frame) != 'iters')] ret/(3600 * 1000)
for (t in seq_len(length(typenames))) { }
points(one_frame[c('iters',typenames[t])], col=myolors[t], pch='.') 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))
} }
legend("bottomright", legend=typenames, col=myolors, lty="solid")

View File

@ -16,4 +16,9 @@ tmr,main,FUZZ_INPUT,32,trigger_Qemu_break
tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break
lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break
waters,main_waters,FUZZ_INPUT,4096,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_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
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

View File

@ -16,7 +16,7 @@ cd "$parent_path"
[ -z "$FUZZER" ] && export FUZZER=target/debug/fret [ -z "$FUZZER" ] && export FUZZER=target/debug/fret
set +e 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=off,target=native -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 $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" ] if [ "$exitcode" = "101" ]
then then
exit 101 exit 101

View File

@ -14,7 +14,7 @@ use libafl::{
observers::ObserversTuple, prelude::UsesInput, impl_serdeany, observers::ObserversTuple, prelude::UsesInput, impl_serdeany,
}; };
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{cell::UnsafeCell, cmp::max, env, fs::OpenOptions, io::Write}; use std::{cell::UnsafeCell, cmp::max, env, fs::OpenOptions, io::Write, time::Instant};
use libafl::bolts::tuples::Named; use libafl::bolts::tuples::Named;
use libafl_qemu::{ use libafl_qemu::{
@ -34,6 +34,9 @@ use core::{fmt::Debug, time::Duration};
// use libafl::feedbacks::FeedbackState; // use libafl::feedbacks::FeedbackState;
// use libafl::state::HasFeedbackStates; // use libafl::state::HasFeedbackStates;
use libafl::bolts::tuples::MatchName; use libafl::bolts::tuples::MatchName;
use std::time::{SystemTime, UNIX_EPOCH};
pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH;
//========== Metadata //========== Metadata
#[derive(Debug, SerdeAny, Serialize, Deserialize)] #[derive(Debug, SerdeAny, Serialize, Deserialize)]
@ -84,7 +87,7 @@ impl Default for MaxIcountMetadata {
/// A piece of metadata tracking all icounts /// A piece of metadata tracking all icounts
#[derive(Debug, SerdeAny, Serialize, Deserialize)] #[derive(Debug, SerdeAny, Serialize, Deserialize)]
pub struct IcHist(pub Vec<u64>); pub struct IcHist (pub Vec<(u64, u128)>, pub (u64,u128));
//========== Observer //========== Observer
@ -137,12 +140,17 @@ where
// println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick); // println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick);
let metadata =_state.metadata_mut(); let metadata =_state.metadata_mut();
let hist = metadata.get_mut::<IcHist>(); let hist = metadata.get_mut::<IcHist>();
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
match hist { match hist {
None => { None => {
metadata.insert(IcHist(vec![self.end_tick - self.start_tick])); metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)],
(self.end_tick - self.start_tick, timestamp)));
} }
Some(v) => { Some(v) => {
v.0.push(self.end_tick - self.start_tick); 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 v.0.len() >= 100 {
if let Ok(td) = env::var("TIME_DUMP") { if let Ok(td) = env::var("TIME_DUMP") {
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
@ -151,9 +159,9 @@ where
.create(true) .create(true)
.append(true) .append(true)
.open(td).expect("Could not open timedump"); .open(td).expect("Could not open timedump");
let newv : Vec<u64> = Vec::with_capacity(100); let newv : Vec<(u64, u128)> = Vec::with_capacity(100);
for i in std::mem::replace(&mut v.0, newv).into_iter() { for i in std::mem::replace(&mut v.0, newv).into_iter() {
writeln!(file, "{}", i).expect("Write to dump failed"); writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
} }
} else { } else {
// If we don't write out values we don't need to remember them at all // If we don't write out values we don't need to remember them at all
@ -212,7 +220,7 @@ where
{ {
// TODO Replace with match_name_type when stable // TODO Replace with match_name_type when stable
let observer = observers.match_name::<QemuClockObserver>(self.name()).unwrap(); let observer = observers.match_name::<QemuClockObserver>(self.name()).unwrap();
self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << 3)); // Assume a somewhat realistic multiplier of clock, it does not matter self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << 4)); // Assume a somewhat realistic multiplier of clock, it does not matter
Ok(false) Ok(false)
} }

View File

@ -1,7 +1,7 @@
//! A fuzzer using qemu in systemmode for binary-only coverage of kernels //! A fuzzer using qemu in systemmode for binary-only coverage of kernels
//! //!
use core::time::Duration; use core::time::Duration;
use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, ptr::addr_of_mut}; use std::{env, path::PathBuf, process::{self, abort}, io::{Read, Write}, fs::{self, OpenOptions}, cmp::{min, max}, mem::transmute_copy, collections::btree_map::Range};
use libafl::{ use libafl::{
bolts::{ bolts::{
@ -22,24 +22,33 @@ use libafl::{
fuzzer::{Fuzzer, StdFuzzer}, fuzzer::{Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes}, inputs::{BytesInput, HasTargetBytes},
monitors::MultiMonitor, monitors::MultiMonitor,
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
observers::{VariableMapObserver}, observers::{VariableMapObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler}, schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::StdMutationalStage,
state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata}, state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata},
Error, Error,
prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata}, Evaluator, prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver}, Evaluator, stages::StdMutationalStage,
}; };
use libafl_qemu::{ use libafl_qemu::{
edges, edges::{QemuEdgeCoverageHelper, edges_map_mut_slice, MAX_EDGES_NUM}, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor, edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor,
QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr, QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr,
emu::libafl_qemu_set_native_breakpoint, emu::libafl_qemu_remove_native_breakpoint,
}; };
use rand::{SeedableRng, StdRng, Rng};
use crate::{ use crate::{
clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist}, clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist, FUZZ_START_TIMESTAMP},
qemustate::QemuStateRestoreHelper, qemustate::QemuStateRestoreHelper,
systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler}, 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; pub static mut MAX_INPUT_SIZE: usize = 32;
/// Read ELF program headers to resolve physical load addresses. /// Read ELF program headers to resolve physical load addresses.
fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr { fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
@ -55,16 +64,18 @@ fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
} }
extern "C" { extern "C" {
static mut libafl_int_offset : u32; static mut libafl_interrupt_offsets : [u32; 32];
static mut libafl_num_interrupts : usize;
} }
pub fn fuzz() { pub fn fuzz() {
let starttime = std::time::Instant::now(); unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();}
let mut starttime = std::time::Instant::now();
if let Ok(s) = env::var("FUZZ_SIZE") { if let Ok(s) = env::var("FUZZ_SIZE") {
str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number"); str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number");
}; };
// Hardcoded parameters // Hardcoded parameters
let timeout = Duration::from_secs(3); let timeout = Duration::from_secs(10);
let broker_port = 1337; let broker_port = 1337;
let cores = Cores::from_cmdline("1").unwrap(); let cores = Cores::from_cmdline("1").unwrap();
let corpus_dirs = [PathBuf::from("./corpus")]; let corpus_dirs = [PathBuf::from("./corpus")];
@ -147,7 +158,7 @@ pub fn fuzz() {
.expect("Symbol or env BREAKPOINT not found"); .expect("Symbol or env BREAKPOINT not found");
println!("Breakpoint address = {:#x}", breakpoint); println!("Breakpoint address = {:#x}", breakpoint);
unsafe { unsafe {
libafl_int_offset = 0; libafl_num_interrupts = 0;
} }
if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") { if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") {
@ -155,6 +166,10 @@ pub fn fuzz() {
} }
unsafe {dbg!(MAX_INPUT_SIZE);} 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| { let mut run_client = |state: Option<_>, mut mgr, _core_id| {
// Initialize QEMU // Initialize QEMU
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
@ -162,14 +177,14 @@ pub fn fuzz() {
let emu = Emulator::new(&args, &env); let emu = Emulator::new(&args, &env);
if let Some(main_addr) = main_addr { if let Some(main_addr) = main_addr {
emu.set_breakpoint(main_addr);
unsafe { unsafe {
libafl_qemu_set_native_breakpoint(main_addr);
emu.run(); emu.run();
libafl_qemu_remove_native_breakpoint(main_addr);
} }
emu.remove_breakpoint(main_addr);
} }
emu.set_breakpoint(breakpoint); // BREAKPOINT unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT
// The wrapped harness function, calling out to the LLVM-style harness // The wrapped harness function, calling out to the LLVM-style harness
let mut harness = |input: &BytesInput| { let mut harness = |input: &BytesInput| {
@ -177,6 +192,30 @@ pub fn fuzz() {
let mut buf = target.as_slice(); let mut buf = target.as_slice();
let mut len = buf.len(); let mut len = buf.len();
unsafe { 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 { if len > MAX_INPUT_SIZE {
buf = &buf[0..MAX_INPUT_SIZE]; buf = &buf[0..MAX_INPUT_SIZE];
len = MAX_INPUT_SIZE; len = MAX_INPUT_SIZE;
@ -203,11 +242,11 @@ pub fn fuzz() {
}; };
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = unsafe {VariableMapObserver::from_mut_slice( let edges = unsafe { &mut edges::EDGES_MAP };
"edges", let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
edges_map_mut_slice(), let edges_observer = VariableMapObserver::new("edges", edges, edges_counter);
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 // Create an observation channel to keep track of the execution time
let clock_time_observer = QemuClockObserver::new("clocktime"); let clock_time_observer = QemuClockObserver::new("clocktime");
@ -220,6 +259,11 @@ pub fn fuzz() {
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
ClockTimeFeedback::new_with_observer(&clock_time_observer) ClockTimeFeedback::new_with_observer(&clock_time_observer)
); );
#[cfg(feature = "feed_genetic")]
let mut feedback = feedback_or!(
feedback,
AlwaysTrueFeedback::new()
);
#[cfg(feature = "feed_afl")] #[cfg(feature = "feed_afl")]
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
feedback, feedback,
@ -241,6 +285,7 @@ pub fn fuzz() {
#[cfg(feature = "feed_systemtrace")] #[cfg(feature = "feed_systemtrace")]
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
feedback, feedback,
// AlwaysTrueFeedback::new(),
NovelSystemStateFeedback::default() NovelSystemStateFeedback::default()
); );
#[cfg(feature = "feed_systemgraph")] #[cfg(feature = "feed_systemgraph")]
@ -256,7 +301,7 @@ pub fn fuzz() {
let mut state = state.unwrap_or_else(|| { let mut state = state.unwrap_or_else(|| {
StdState::new( StdState::new(
// RNG // RNG
StdRand::with_seed(current_nanos()), unsafe {StdRand::with_seed(RNG_SEED) },
// Corpus that will be evolved, we keep it in memory for performance // Corpus that will be evolved, we keep it in memory for performance
InMemoryCorpus::new(), InMemoryCorpus::new(),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
@ -272,14 +317,16 @@ pub fn fuzz() {
}); });
// A minimization+queue policy to get testcasess from the corpus // A minimization+queue policy to get testcasess from the corpus
#[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace")))] #[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace", feature = "feed_genetic")))]
let scheduler = QueueScheduler::new(); let scheduler = QueueScheduler::new();
#[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))] #[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))]
let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new()); let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new());
#[cfg(feature = "feed_systemtrace")] #[cfg(feature = "feed_systemtrace")]
let scheduler = TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()); let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()));
#[cfg(feature = "feed_systemgraph")] #[cfg(feature = "feed_systemgraph")]
let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new()); let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new());
#[cfg(feature = "feed_genetic")]
let scheduler = GenerationScheduler::new();
// A fuzzer with feedbacks and a corpus scheduler // A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective); let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
@ -315,8 +362,15 @@ pub fn fuzz() {
// Wrap the executor to keep track of the timeout // Wrap the executor to keep track of the timeout
let mut executor = TimeoutExecutor::new(executor, timeout); let mut executor = TimeoutExecutor::new(executor, timeout);
let mutations = havoc_mutations();
// Setup an havoc mutator with a mutational stage // Setup an havoc mutator with a mutational stage
let mutator = StdScheduledMutator::new(havoc_mutations()); 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)); let mut stages = tuple_list!(StdMutationalStage::new(mutator));
if env::var("DO_SHOWMAP").is_ok() { if env::var("DO_SHOWMAP").is_ok() {
@ -339,22 +393,20 @@ pub fn fuzz() {
.create(true) .create(true)
.append(true) .append(true)
.open(td).expect("Could not open timedump"); .open(td).expect("Could not open timedump");
if let Some(ichist) = state.metadata().get::<IcHist>() { if let Some(ichist) = state.metadata_mut().get_mut::<IcHist>() {
for i in ichist.0.iter() { for i in ichist.0.drain(..) {
writeln!(file, "{}", i).expect("Write to dump failed"); writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
} }
} }
} }
} else { } else {
if let Ok(_) = env::var("SEED_RANDOM") { if let Ok(_) = env::var("SEED_RANDOM") {
unsafe { unsafe {
let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); let mut rng = StdRng::seed_from_u64(RNG_SEED);
state for i in 0..100 {
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 100) let inp = BytesInput::new(vec![rng.gen::<u8>(); MAX_INPUT_SIZE]);
.unwrap_or_else(|_| { fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap();
println!("Failed to load initial corpus at {:?}", &corpus_dirs); }
process::exit(0);
});
} }
} }
else if let Ok(sf) = env::var("SEED_DIR") { else if let Ok(sf) = env::var("SEED_DIR") {
@ -384,15 +436,23 @@ pub fn fuzz() {
Ok(t) => { Ok(t) => {
println!("Iterations {}",t); println!("Iterations {}",t);
let num = str::parse::<u64>(&t).expect("FUZZ_ITERS was not a number"); let num = str::parse::<u64>(&t).expect("FUZZ_ITERS was not a number");
if let Ok(_) = env::var("FUZZ_RANDOM") { unsafe { 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"); println!("Random Fuzzing, ignore corpus");
// let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE); // let mut generator = RandBytesGenerator::new(MAX_INPUT_SIZE);
let target_duration = Duration::from_secs(num); let target_duration = Duration::from_secs(num);
let start_time = std::time::Instant::now(); let start_time = std::time::Instant::now();
let mut rng = StdRng::seed_from_u64(RNG_SEED);
while start_time.elapsed() < target_duration { while start_time.elapsed() < target_duration {
// let inp = generator.generate(&mut state).unwrap(); // let inp = generator.generate(&mut state).unwrap();
// libafl's generator is too slow // libafl's generator is too slow
let inp = BytesInput::new(vec![rand::random::<u8>(); MAX_INPUT_SIZE]); let inp = BytesInput::new(vec![rng.gen::<u8>(); MAX_INPUT_SIZE]);
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap(); fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap();
} }
}} else { }} else {
@ -402,7 +462,10 @@ pub fn fuzz() {
fuzzer fuzzer
.fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime.checked_add(Duration::from_secs(num)).unwrap()) .fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime.checked_add(Duration::from_secs(num)).unwrap())
.unwrap(); .unwrap();
} #[cfg(feature = "run_until_saturation")]
{
{
let mut dumper = |marker : String| {
if let Ok(td) = env::var("TIME_DUMP") { if let Ok(td) = env::var("TIME_DUMP") {
let mut file = OpenOptions::new() let mut file = OpenOptions::new()
.read(true) .read(true)
@ -410,9 +473,9 @@ pub fn fuzz() {
.create(true) .create(true)
.append(true) .append(true)
.open(td).expect("Could not open timedump"); .open(td).expect("Could not open timedump");
if let Some(ichist) = state.metadata().get::<IcHist>() { if let Some(ichist) = state.metadata_mut().get_mut::<IcHist>() {
for i in ichist.0.iter() { for i in ichist.0.drain(..) {
writeln!(file, "{}", i).expect("Write to dump failed"); writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
} }
} }
} }
@ -422,7 +485,116 @@ pub fn fuzz() {
let mut worst = Duration::new(0,0); let mut worst = Duration::new(0,0);
let mut worst_input = None; let mut worst_input = None;
for i in 0..corpus.count() { for i in 0..corpus.count() {
let tc = corpus.get(i.into()).expect("Could not get element from corpus").borrow(); let tc = corpus.get(i).expect("Could not get element from corpus").borrow();
if worst < tc.exec_time().expect("Testcase missing duration") {
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
worst = tc.exec_time().expect("Testcase missing duration");
}
}
match worst_input {
Some(wi) => {
// let cd = format!("{}.case",&td);
let mut cd = td.clone();
cd.push_str(&marker);
fs::write(&cd,wi).expect("Failed to write worst corpus element");
},
None => (),
}
#[cfg(feature = "feed_systemgraph")]
{
let mut gd = String::from(&td);
gd.push_str(&format!(".graph{}", marker));
if let Some(md) = state.named_metadata_mut().get_mut::<SysGraphFeedbackState>("SysMap") {
fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph");
}
}
{
let mut gd = String::from(&td);
if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
let mut uniq: Vec<usize> = md.map.values().map(|x| x.clone()).collect();
uniq.sort();
uniq.dedup();
gd.push_str(&format!(".{}.toprated{}", uniq.len(), marker));
fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
}
}
}
};
dumper(format!(".iter_{}",t));
}
println!("Start running until saturation");
let mut last = state.metadata().get::<IcHist>().unwrap().1;
while SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis() < last.1 + Duration::from_secs(10800).as_millis() {
starttime=starttime.checked_add(Duration::from_secs(30)).unwrap();
fuzzer
.fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime)
.unwrap();
let after = state.metadata().get::<IcHist>().unwrap().1;
if after.0 > last.0 {
last=after;
}
if let Ok(td) = env::var("CASE_DUMP") {
println!("Dumping worst case to {:?}", td);
let corpus = state.corpus();
let mut worst = Duration::new(0,0);
let mut worst_input = None;
for i in 0..corpus.count() {
let tc = corpus.get(i).expect("Could not get element from corpus").borrow();
if worst < tc.exec_time().expect("Testcase missing duration") {
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
worst = tc.exec_time().expect("Testcase missing duration");
}
}
match worst_input {
Some(wi) => {
// let cd = format!("{}.case",&td);
let cd = td.clone();
fs::write(&cd,wi).expect("Failed to write worst corpus element");
},
None => (),
}
#[cfg(feature = "feed_systemgraph")]
{
let mut gd = String::from(&td);
gd.push_str(".graph" );
if let Some(md) = state.named_metadata_mut().get_mut::<SysGraphFeedbackState>("SysMap") {
fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph");
}
}
{
let mut gd = String::from(&td);
if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
let mut uniq: Vec<usize> = md.map.values().map(|x| x.clone()).collect();
uniq.sort();
uniq.dedup();
gd.push_str(&format!(".{}.toprated", uniq.len()));
fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
}
}
}
}
}
}
if let Ok(td) = env::var("TIME_DUMP") {
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.append(true)
.open(td).expect("Could not open timedump");
if let Some(ichist) = state.metadata_mut().get_mut::<IcHist>() {
for i in ichist.0.drain(..) {
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
}
}
}
if let Ok(td) = env::var("CASE_DUMP") {
println!("Dumping worst case to {:?}", td);
let corpus = state.corpus();
let mut worst = Duration::new(0,0);
let mut worst_input = None;
for i in 0..corpus.count() {
let tc = corpus.get(i).expect("Could not get element from corpus").borrow();
if worst < tc.exec_time().expect("Testcase missing duration") { if worst < tc.exec_time().expect("Testcase missing duration") {
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned()); worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
worst = tc.exec_time().expect("Testcase missing duration"); worst = tc.exec_time().expect("Testcase missing duration");
@ -447,7 +619,7 @@ pub fn fuzz() {
{ {
let mut gd = String::from(&td); let mut gd = String::from(&td);
if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() { if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
let mut uniq: Vec<String> = md.map.values().map(|x| x.to_string()).collect(); let mut uniq: Vec<usize> = md.map.values().map(|x| x.clone()).collect();
uniq.sort(); uniq.sort();
uniq.dedup(); uniq.dedup();
gd.push_str(&format!(".{}.toprated", uniq.len())); gd.push_str(&format!(".{}.toprated", uniq.len()));
@ -470,7 +642,7 @@ pub fn fuzz() {
let emu = Emulator::new(&args, &env); let emu = Emulator::new(&args, &env);
if let Some(main_addr) = main_addr { if let Some(main_addr) = main_addr {
emu.set_breakpoint(main_addr); unsafe { libafl_qemu_set_native_breakpoint(main_addr); }// BREAKPOINT
} }
unsafe { unsafe {
emu.run(); emu.run();
@ -489,9 +661,14 @@ pub fn fuzz() {
#[cfg(feature = "singlecore")] #[cfg(feature = "singlecore")]
{ {
let monitor = SimpleMonitor::new(|s| println!("{}", s)); let monitor = SimpleMonitor::new(|s| println!("{}", s));
// let mgr = SimpleEventManager::new(monitor); #[cfg(not(feature = "restarting"))]
// run_client(None, mgr, 0); {
let mgr = SimpleEventManager::new(monitor);
run_client(None, mgr, 0);
}
#[cfg(feature = "restarting")]
{
let mut shmem_provider = StdShMemProvider::new().unwrap(); let mut shmem_provider = StdShMemProvider::new().unwrap();
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
{ {
@ -508,6 +685,7 @@ pub fn fuzz() {
}; };
run_client(state, mgr, 0); run_client(state, mgr, 0);
} }
}
// else -> multicore // else -> multicore
#[cfg(not(feature = "singlecore"))] #[cfg(not(feature = "singlecore"))]
{ {

View File

@ -8,4 +8,6 @@ mod qemustate;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub mod systemstate; pub mod systemstate;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
mod mutational;
#[cfg(target_os = "linux")]
mod worst; mod worst;

View File

@ -10,6 +10,8 @@ mod qemustate;
mod systemstate; mod systemstate;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
mod worst; mod worst;
#[cfg(target_os = "linux")]
mod mutational;
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
pub fn main() { pub fn main() {

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},
};
use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist};
pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4);
//======================= Custom mutator
/// The default mutational stage
#[derive(Clone, Debug, Default)]
pub struct MyStateStage<E, EM, Z> {
#[allow(clippy::type_complexity)]
phantom: PhantomData<(E, EM, Z)>,
}
impl<E, EM, Z> MyStateStage<E, EM, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
pub fn new() -> Self {
Self { phantom: PhantomData }
}
}
impl<E, EM, Z> Stage<E, EM, Z> for MyStateStage<E, EM, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
<Z::State as UsesInput>::Input: HasBytesVec
{
fn perform(
&mut self,
fuzzer: &mut Z,
executor: &mut E,
state: &mut Self::State,
manager: &mut EM,
corpus_idx: usize,
) -> Result<(), Error> {
let mut _input = state
.corpus()
.get(corpus_idx)?
.borrow_mut().clone();
let mut newinput = _input.input_mut().as_mut().unwrap().clone();
// let mut tmpinput = _input.input_mut().as_mut().unwrap().clone();
let mut do_rerun = false;
{
// need our own random generator, because borrowing rules
let mut myrand = StdRand::new();
let mut target_bytes : Vec<u8> = vec![];
{
let input = _input.input_mut().as_ref().unwrap();
let tmp = &mut state.rand_mut();
myrand.set_seed(tmp.next());
target_bytes = input.bytes().to_vec();
}
// produce a slice of absolute interrupt times
let mut interrupt_offsets : [u32; 32] = [0u32; 32];
let mut num_interrupts : usize = 0;
{
let mut start_tick : u32 = 0;
for i in 0..DO_NUM_INTERRUPT {
let mut t : [u8; 4] = [0,0,0,0];
if target_bytes.len() > (i+1)*4 {
for j in 0 as usize..4 as usize {
t[j]=target_bytes[i*4+j];
}
if i == 0 || true {
start_tick = u32::from_le_bytes(t);
} else {
start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t)));
}
interrupt_offsets[i] = start_tick;
num_interrupts = i+1;
}
}
}
interrupt_offsets.sort();
// println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec());
// let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT);
let mut suffix = target_bytes.split_off(4 * num_interrupts);
let mut prefix : Vec<[u8; 4]> = vec![];
// let mut suffix : Vec<u8> = vec![];
#[cfg(feature = "feed_systemtrace")]
{
let tmp = _input.metadata().get::<FreeRTOSSystemStateMetadata>();
if tmp.is_some() {
let trace = tmp.expect("FreeRTOSSystemStateMetadata not found");
// calculate hits and identify snippets
let mut last_m = false;
let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler
for i in 0..trace.inner.len() {
let curr = &trace.inner[i];
let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64)));
if m {
marks.push((curr, i, 1));
// println!("1: {}",curr.current_task.task_name);
} else if last_m {
marks.push((curr, i, 2));
// println!("2: {}",curr.current_task.task_name);
} else {
marks.push((curr, i, 0));
}
last_m = m;
}
for i in 0..num_interrupts {
// bounds based on minimum inter-arrival time
let mut lb = 0;
let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32");
if i > 0 {
lb = u32::saturating_add(interrupt_offsets[i-1],MINIMUM_INTER_ARRIVAL_TIME);
}
if i < num_interrupts-1 {
ub = u32::saturating_sub(interrupt_offsets[i+1],MINIMUM_INTER_ARRIVAL_TIME);
}
// get old hit and handler
let old_hit = marks.iter().filter(
|x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick
).next();
let old_handler = match old_hit {
Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 {
Some(marks[s.1+1])
} else {None},
None => None
};
// find reachable alternatives
let alternatives : Vec<_> = marks.iter().filter(|x|
x.2 != 2 &&
(
x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick
|| x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick )
).collect();
// in cases there are no alternatives
if alternatives.len() == 0 {
if old_hit.is_none() {
// choose something random
let untouched : Vec<_> = marks.iter().filter(
|x| x.2 == 0
).collect();
if untouched.len() > 0 {
let tmp = interrupt_offsets[i];
let choice = myrand.choose(untouched);
interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick)
.try_into().expect("tick > u32");
do_rerun = true;
}
// println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]);
continue;
} else {
// do nothing
// println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]);
continue;
}
}
let replacement = myrand.choose(alternatives);
if (old_hit.map_or(false, |x| x == replacement)) {
// use the old value
// println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]);
continue;
} else {
let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) {
// move futher back, respect old_handler
old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick)
} else { 0 };
let tmp = interrupt_offsets[i];
interrupt_offsets[i] = (myrand.between(replacement.0.start_tick,
replacement.0.end_tick) + extra).try_into().expect("ticks > u32");
// println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]);
do_rerun = true;
}
}
let mut numbers : Vec<u32> = interrupt_offsets[0..num_interrupts].to_vec();
numbers.sort();
// println!("Mutator: {:?}", numbers);
let mut start : u32 = 0;
// for i in 0..numbers.len() {
// let tmp = numbers[i];
// numbers[i] = numbers[i]-start;
// start = tmp;
// }
for i in 0..numbers.len() {
prefix.push(u32::to_le_bytes(numbers[i]));
}
}
}
#[cfg(not(feature = "feed_systemtrace"))]
{
let metadata = state.metadata();
let hist = metadata.get::<IcHist>().unwrap();
let maxtick : u64 = hist.1.0;
// let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap();
let mut numbers : Vec<u32> = vec![];
for i in 0..num_interrupts {
prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32")));
}
}
let mut n : Vec<u8> = vec![];
n = [prefix.concat(), suffix].concat();
newinput.bytes_mut().clear();
newinput.bytes_mut().append(&mut n);
}
// InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?;
if do_rerun {
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, newinput)?;
}
Ok(())
}
}
impl<E, EM, Z> UsesState for MyStateStage<E, EM, Z>
where
E: UsesState<State = Z::State>,
EM: UsesState<State = Z::State>,
Z: Evaluator<E, EM>,
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
{
type State = Z::State;
}

View File

@ -93,7 +93,7 @@ fn trigger_collection(emulator: &Emulator, h: &QemuSystemStateHelper) {
let mut systemstate = RawFreeRTOSSystemState::default(); let mut systemstate = RawFreeRTOSSystemState::default();
unsafe { unsafe {
// TODO: investigate why can_do_io is not set sometimes, as this is just a workaround // TODO: investigate why can_do_io is not set sometimes, as this is just a workaround
let c = emulator.current_cpu().unwrap(); let c = emulator.cpu_from_index(0);
let can_do_io = (*c.raw_ptr()).can_do_io; let can_do_io = (*c.raw_ptr()).can_do_io;
(*c.raw_ptr()).can_do_io = 1; (*c.raw_ptr()).can_do_io = 1;
systemstate.qemu_tick = emu::icount_get_raw(); systemstate.qemu_tick = emu::icount_get_raw();

View File

@ -14,13 +14,13 @@ pub mod helpers;
pub mod observers; pub mod observers;
pub mod feedbacks; pub mod feedbacks;
pub mod graph; pub mod graph;
// pub mod mutators; pub mod schedulers;
#[cfg(feature = "fuzz_interrupt")] // #[cfg(feature = "fuzz_interrupt")]
pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes // pub const IRQ_INPUT_BYTES_NUMBER : u32 = 2; // Offset for interrupt bytes
#[cfg(not(feature = "fuzz_interrupt"))] // #[cfg(not(feature = "fuzz_interrupt"))]
pub const IRQ_INPUT_BYTES_NUMBER : u32 = 0; // Offset for interrupt bytes // 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 // pub const IRQ_INPUT_OFFSET : u32 = 347780; // Tick offset for app code start
// Constants // Constants
const NUM_PRIOS: usize = 5; const NUM_PRIOS: usize = 5;
@ -53,9 +53,10 @@ pub struct RefinedTCB {
impl Hash for RefinedTCB { impl Hash for RefinedTCB {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.task_name.hash(state); self.task_name.hash(state);
// self.priority.hash(state); self.priority.hash(state);
// self.mutexes_held.hash(state); self.mutexes_held.hash(state);
// self.notify_state.hash(state); #[cfg(not(feature = "no_hash_state"))]
self.notify_state.hash(state);
// self.notify_value.hash(state); // self.notify_value.hash(state);
} }
} }
@ -112,7 +113,7 @@ impl Hash for RefinedFreeRTOSSystemState {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
self.current_task.hash(state); self.current_task.hash(state);
self.ready_list_after.hash(state); self.ready_list_after.hash(state);
self.last_pc.hash(state); // self.last_pc.hash(state);
} }
} }
impl RefinedFreeRTOSSystemState { impl RefinedFreeRTOSSystemState {
@ -124,14 +125,15 @@ impl RefinedFreeRTOSSystemState {
// Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata // Wrapper around Vec<RefinedFreeRTOSSystemState> to attach as Metadata
#[derive(Debug, Default, Serialize, Deserialize, Clone)] #[derive(Debug, Default, Serialize, Deserialize, Clone)]
pub struct FreeRTOSSystemStateMetadata { pub struct FreeRTOSSystemStateMetadata {
inner: Vec<RefinedFreeRTOSSystemState>, pub inner: Vec<RefinedFreeRTOSSystemState>,
trace_length: usize,
indices: Vec<usize>, // Hashed enumeration of States indices: Vec<usize>, // Hashed enumeration of States
tcref: isize, tcref: isize,
} }
impl FreeRTOSSystemStateMetadata { impl FreeRTOSSystemStateMetadata {
pub fn new(inner: Vec<RefinedFreeRTOSSystemState>) -> Self{ pub fn new(inner: Vec<RefinedFreeRTOSSystemState>) -> Self{
let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect(); let tmp = inner.iter().enumerate().map(|x| compute_hash(x) as usize).collect();
Self {inner: inner, indices: tmp, tcref: 0} Self {trace_length: inner.len(), inner: inner, indices: tmp, tcref: 0}
} }
} }
pub fn compute_hash<T>(obj: T) -> u64 pub fn compute_hash<T>(obj: T) -> u64

View File

@ -1,119 +0,0 @@
use crate::systemstate::graph::SysGraphMetadata;
use crate::systemstate::graph::SysGraphNode;
use crate::systemstate::IRQ_INPUT_OFFSET;
use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
use crate::systemstate::graph::SysGraphFeedbackState;
use libafl::inputs::HasBytesVec;
use libafl::bolts::rands::RandomSeed;
use libafl::bolts::rands::StdRand;
use libafl::mutators::Mutator;
use libafl::mutators::MutationResult;
use libafl::prelude::UsesInput;
use core::marker::PhantomData;
use libafl::state::HasCorpus;
use libafl::state::HasSolutions;
use libafl::state::HasRand;
use libafl::bolts::tuples::MatchName;
use libafl::bolts::tuples::Named;
use libafl::Error;
use libafl::{inputs::Input, state::HasMetadata};
use super::FreeRTOSSystemStateMetadata;
use libafl::bolts::rands::Rand;
//=============================== Interrupt
/// Sets up the interrupt to a random block in the trace. Works for both state and graph metadata
pub struct InterruptShifterMutator<S>
where
S: UsesInput,
{
phantom: PhantomData<S>,
}
impl<S> InterruptShifterMutator<S>
where
S: UsesInput,
{
pub fn new() -> Self {
InterruptShifterMutator{phantom: PhantomData}
}
}
impl<S> Mutator<S> for InterruptShifterMutator<S>
where
S: UsesInput,
{
fn mutate(
&mut self,
state: &mut S,
input: &mut S::Input,
_stage_idx: i32
) -> Result<MutationResult, Error>
{
// need our own random generator, because borrowing rules
let mut myrand = StdRand::new();
let tmp = &mut state.rand_mut();
myrand.set_seed(tmp.next());
drop(tmp);
let target_bytes = input.bytes_mut();
let mut target_tick = 0;
#[cfg(feature = "sched_state")]
{
let tmp = state.metadata().get::<FreeRTOSSystemStateMetadata>();
if tmp.is_none() { // if there are no metadata it was probably not interesting anyways
return Ok(MutationResult::Skipped);
}
let trace =tmp.expect("FreeRTOSSystemStateMetadata not found");
let target_block = myrand.choose(trace.inner.iter());
target_tick = myrand.between(target_block.start_tick,target_block.end_tick)-IRQ_INPUT_OFFSET as u64;
}
#[cfg(feature = "sched_state")]
{
let feedbackstate = state
.feedback_states()
.match_name::<SysGraphFeedbackState>("SysMap")
.unwrap();
let g = &feedbackstate.graph;
let tmp = state.metadata().get::<SysGraphMetadata>();
if tmp.is_none() { // if there are no metadata it was probably not interesting anyways
return Ok(MutationResult::Skipped);
}
let trace = tmp.expect("SysGraphMetadata not found");
let target_block : &SysGraphNode = &g[*myrand.choose(trace.inner.iter())];
target_tick = match target_block.variants.iter().find(|x| &x.input == target_bytes) {
Some(s) => myrand.between(s.start_tick,s.end_tick)-IRQ_INPUT_OFFSET as u64,
None => myrand.between(target_block.variants[0].start_tick,target_block.variants[0].end_tick)-IRQ_INPUT_OFFSET as u64,
};
}
if target_bytes.len() > IRQ_INPUT_BYTES_NUMBER as usize && IRQ_INPUT_BYTES_NUMBER > 0 {
for i in 0..IRQ_INPUT_BYTES_NUMBER as usize {
target_bytes[i] = u64::to_le_bytes(target_tick)[i];
}
return Ok(MutationResult::Mutated);
} else {
return Ok(MutationResult::Skipped);
}
}
fn post_exec(
&mut self,
_state: &mut S,
_stage_idx: i32,
_corpus_idx: Option<usize>
) -> Result<(), Error> {
Ok(())
}
}
impl<S> Named for InterruptShifterMutator<S>
where
S: UsesInput,
{
fn name(&self) -> &str {
"InterruptShifterMutator"
}
}

View File

@ -1,4 +1,4 @@
use crate::systemstate::IRQ_INPUT_BYTES_NUMBER; // use crate::systemstate::IRQ_INPUT_BYTES_NUMBER;
use libafl::prelude::{ExitKind, AsSlice}; use libafl::prelude::{ExitKind, AsSlice};
use libafl::{inputs::HasTargetBytes, prelude::UsesInput}; use libafl::{inputs::HasTargetBytes, prelude::UsesInput};
use libafl::bolts::HasLen; use libafl::bolts::HasLen;
@ -124,7 +124,7 @@ for mut i in input.drain(..) {
start_tick: start_tick, start_tick: start_tick,
end_tick: i.qemu_tick, end_tick: i.qemu_tick,
ready_list_after: collector, ready_list_after: collector,
input_counter: i.input_counter+IRQ_INPUT_BYTES_NUMBER, input_counter: i.input_counter,//+IRQ_INPUT_BYTES_NUMBER,
last_pc: i.last_pc, last_pc: i.last_pc,
}); });
start_tick=i.qemu_tick; start_tick=i.qemu_tick;

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,
};
use crate::worst::MaxTimeFavFactor;
use super::FreeRTOSSystemStateMetadata;
/// A state metadata holding a map of favoreds testcases for each map entry
#[derive(Debug, Serialize, Deserialize, SerdeAny, Default)]
pub struct LongestTracesMetadata {
/// map index -> corpus index
pub max_trace_length: usize,
}
impl LongestTracesMetadata {
fn new(l : usize) -> Self {
Self {max_trace_length: l}
}
}
/// The [`MinimizerScheduler`] employs a genetic algorithm to compute a subset of the
/// corpus that exercise all the requested features (e.g. all the coverage seen so far)
/// prioritizing [`Testcase`]`s` using [`TestcaseScore`]
#[derive(Debug, Clone)]
pub struct LongestTraceScheduler<CS> {
base: CS,
skip_non_favored_prob: u64,
}
impl<CS> UsesState for LongestTraceScheduler<CS>
where
CS: UsesState,
{
type State = CS::State;
}
impl<CS> Scheduler for LongestTraceScheduler<CS>
where
CS: Scheduler,
CS::State: HasCorpus + HasMetadata + HasRand,
{
/// Add an entry to the corpus and return its index
fn on_add(&self, state: &mut CS::State, idx: usize) -> Result<(), Error> {
let l = state.corpus()
.get(idx)?
.borrow()
.metadata()
.get::<FreeRTOSSystemStateMetadata>().map_or(0, |x| x.trace_length);
self.get_update_trace_length(state,l);
self.base.on_add(state, idx)
}
/// Replaces the testcase at the given idx
fn on_replace(
&self,
state: &mut CS::State,
idx: usize,
testcase: &Testcase<<CS::State as UsesInput>::Input>,
) -> Result<(), Error> {
let l = state.corpus()
.get(idx)?
.borrow()
.metadata()
.get::<FreeRTOSSystemStateMetadata>().map_or(0, |x| x.trace_length);
self.get_update_trace_length(state, l);
self.base.on_replace(state, idx, testcase)
}
/// Removes an entry from the corpus, returning M if M was present.
fn on_remove(
&self,
state: &mut CS::State,
idx: usize,
testcase: &Option<Testcase<<CS::State as UsesInput>::Input>>,
) -> Result<(), Error> {
self.base.on_remove(state, idx, testcase)?;
Ok(())
}
/// Gets the next entry
fn next(&self, state: &mut CS::State) -> Result<usize, Error> {
let mut idx = self.base.next(state)?;
while {
let l = state.corpus()
.get(idx)?
.borrow()
.metadata()
.get::<FreeRTOSSystemStateMetadata>().map_or(0, |x| x.trace_length);
let m = self.get_update_trace_length(state,l);
state.rand_mut().below(m) > l as u64
} && state.rand_mut().below(100) < self.skip_non_favored_prob
{
idx = self.base.next(state)?;
}
Ok(idx)
}
}
impl<CS> LongestTraceScheduler<CS>
where
CS: Scheduler,
CS::State: HasCorpus + HasMetadata + HasRand,
{
pub fn get_update_trace_length(&self, state: &mut CS::State, par: usize) -> u64 {
// Create a new top rated meta if not existing
if let Some(td) = state.metadata_mut().get_mut::<LongestTracesMetadata>() {
let m = max(td.max_trace_length, par);
td.max_trace_length = m;
m as u64
} else {
state.add_metadata(LongestTracesMetadata::new(par));
par as u64
}
}
pub fn new(base: CS) -> Self {
Self {
base,
skip_non_favored_prob: DEFAULT_SKIP_NON_FAVORED_PROB,
}
}
}
//==========================================================================================
/// A state metadata holding a map of favoreds testcases for each map entry
#[derive(Debug, Serialize, Deserialize, SerdeAny, Default)]
pub struct GeneticMetadata {
pub current_gen: Vec<(usize, f64)>,
pub current_cursor: usize,
pub next_gen: Vec<(usize, f64)>,
pub gen: usize
}
impl GeneticMetadata {
fn new(current_gen: Vec<(usize, f64)>, next_gen: Vec<(usize, f64)>) -> Self {
Self {current_gen, current_cursor: 0, next_gen, gen: 0}
}
}
#[derive(Debug, Clone)]
pub struct GenerationScheduler<S> {
phantom: PhantomData<S>,
gen_size: usize,
}
impl<S> UsesState for GenerationScheduler<S>
where
S: UsesInput,
{
type State = S;
}
impl<S> Scheduler for GenerationScheduler<S>
where
S: HasCorpus + HasMetadata,
S::Input: HasLen,
{
/// get first element in current gen,
/// if current_gen is empty, swap lists, sort by FavFactor, take top k and return first
fn next(&self, state: &mut Self::State) -> Result<usize, Error> {
let mut to_remove : Vec<(usize, f64)> = vec![];
let mut to_return : usize = 0;
let c = state.corpus().count();
let gm = state.metadata_mut().get_mut::<GeneticMetadata>().expect("Corpus Scheduler empty");
// println!("index: {} curr: {:?} next: {:?} gen: {} corp: {}", gm.current_cursor, gm.current_gen.len(), gm.next_gen.len(), gm.gen,
// c);
match gm.current_gen.get(gm.current_cursor) {
Some(c) => {
gm.current_cursor+=1;
// println!("normal next: {}", (*c).0);
return Ok((*c).0)
},
None => {
swap(&mut to_remove, &mut gm.current_gen);
swap(&mut gm.next_gen, &mut gm.current_gen);
gm.current_gen.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
// gm.current_gen.reverse();
if gm.current_gen.len() == 0 {panic!("Corpus is empty");}
let d : Vec<(usize, f64)> = gm.current_gen.drain(min(gm.current_gen.len(), self.gen_size)..).collect();
to_remove.extend(d);
// move all indices to the left, since all other indices will be deleted
gm.current_gen.sort_by(|a,b| a.0.cmp(&(*b).0)); // in order of the corpus index
for i in 0..gm.current_gen.len() {
gm.current_gen[i] = (i, gm.current_gen[i].1);
}
to_return = gm.current_gen.get(0).unwrap().0;
gm.current_cursor=1;
gm.gen+=1;
}
};
// removing these elements will move all indices left by to_remove.len()
to_remove.sort_by(|x,y| x.0.cmp(&(*y).0));
to_remove.reverse();
for i in to_remove {
state.corpus_mut().remove(i.0).unwrap();
}
// println!("switch next: {to_return}");
return Ok(to_return);
}
/// Add the new input to the next generation
fn on_add(
&self,
state: &mut Self::State,
idx: usize
) -> Result<(), Error> {
// println!("On Add {idx}");
let mut tc = state.corpus_mut().get(idx).unwrap().borrow_mut().clone();
let ff = MaxTimeFavFactor::compute(&mut tc, state).unwrap();
if let Some(gm) = state.metadata_mut().get_mut::<GeneticMetadata>() {
gm.next_gen.push((idx,ff));
} else {
state.add_metadata(GeneticMetadata::new(vec![], vec![(idx,ff)]));
}
Ok(())
}
fn on_replace(
&self,
_state: &mut Self::State,
_idx: usize,
_prev: &Testcase<<Self::State as UsesInput>::Input>
) -> Result<(), Error> {
// println!("On Replace {_idx}");
Ok(())
}
fn on_remove(
&self,
state: &mut Self::State,
idx: usize,
_testcase: &Option<Testcase<<Self::State as UsesInput>::Input>>
) -> Result<(), Error> {
// println!("On Remove {idx}");
if let Some(gm) = state.metadata_mut().get_mut::<GeneticMetadata>() {
gm.next_gen = gm.next_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::<Vec<(usize, f64)>>();
gm.current_gen = gm.current_gen.drain(..).into_iter().filter(|x| (*x).0 != idx).collect::<Vec<(usize, f64)>>();
} else {
state.add_metadata(GeneticMetadata::new(vec![], vec![]));
}
Ok(())
}
}
impl<S> GenerationScheduler<S>
{
pub fn new() -> Self {
Self {
phantom: PhantomData,
gen_size: 100,
}
}
}

View File

@ -54,10 +54,11 @@ where
S: HasCorpus + HasMetadata, S: HasCorpus + HasMetadata,
S::Input: HasLen, S::Input: HasLen,
{ {
fn compute(entry: &mut Testcase<S::Input>, state: &S) -> Result<f64, Error> { fn compute(entry: &mut Testcase<<S as UsesInput>::Input>, state: &S) -> Result<f64, Error> {
// TODO maybe enforce entry.exec_time().is_some() // TODO maybe enforce entry.exec_time().is_some()
let execs_per_hour = 3600.0/entry.exec_time().expect("testcase.exec_time is needed for scheduler").as_secs_f64(); let et = entry.exec_time().expect("testcase.exec_time is needed for scheduler");
Ok(execs_per_hour) let tns : i64 = et.as_nanos().try_into().expect("failed to convert time");
Ok(-tns as f64)
} }
} }
@ -332,3 +333,49 @@ where
Self {longest_time: 0, last_is_longest: false} 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] [package]
name = "baby_fuzzer" name = "baby_fuzzer"
version = "0.9.0" version = "0.7.1"
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"] authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
edition = "2021" edition = "2021"

View File

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

View File

@ -30,7 +30,7 @@ fn signals_set(idx: usize) {
unsafe { SIGNALS[idx] = 1 }; unsafe { SIGNALS[idx] = 1 };
} }
#[allow(clippy::similar_names, clippy::manual_assert)] #[allow(clippy::similar_names)]
pub fn main() { pub fn main() {
// The closure that we want to fuzz // The closure that we want to fuzz
let mut harness = |input: &BytesInput| { let mut harness = |input: &BytesInput| {
@ -61,7 +61,7 @@ pub fn main() {
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = let observer =
unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) }; unsafe { StdMapObserver::new_from_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) };
// Feedback to rate the interestingness of an input // Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer); let mut feedback = MaxMapFeedback::new(&observer);
@ -88,7 +88,7 @@ pub fn main() {
// The Monitor trait define how the fuzzer stats are displayed to the user // The Monitor trait define how the fuzzer stats are displayed to the user
#[cfg(not(feature = "tui"))] #[cfg(not(feature = "tui"))]
let mon = SimpleMonitor::new(|s| println!("{s}")); let mon = SimpleMonitor::new(|s| println!("{}", s));
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
let mon = TuiMonitor::new(String::from("Baby Fuzzer"), false); let mon = TuiMonitor::new(String::from("Baby Fuzzer"), false);

View File

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

View File

@ -57,7 +57,7 @@ pub fn main() {
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", &mut SIGNALS) }; let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
// Feedback to rate the interestingness of an input // Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer); let mut feedback = MaxMapFeedback::new(&observer);
@ -83,7 +83,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are reported to the user // 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 // 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 // such as the notification of the addition of a new item to the corpus

View File

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

View File

@ -9,7 +9,7 @@ use libafl::{
executors::{inprocess::InProcessExecutor, ExitKind}, executors::{inprocess::InProcessExecutor, ExitKind},
feedbacks::{CrashFeedback, MaxMapFeedback}, feedbacks::{CrashFeedback, MaxMapFeedback},
fuzzer::{Evaluator, Fuzzer, StdFuzzer}, fuzzer::{Evaluator, Fuzzer, StdFuzzer},
inputs::{BytesInput, HasTargetBytes}, inputs::{GeneralizedInput, HasTargetBytes},
monitors::SimpleMonitor, monitors::SimpleMonitor,
mutators::{ mutators::{
havoc_mutations, scheduled::StdScheduledMutator, GrimoireExtensionMutator, havoc_mutations, scheduled::StdScheduledMutator, GrimoireExtensionMutator,
@ -59,13 +59,13 @@ pub fn main() {
let mut file = fs::File::open(path).expect("no file found"); let mut file = fs::File::open(path).expect("no file found");
let mut buffer = vec![]; let mut buffer = vec![];
file.read_to_end(&mut buffer).expect("buffer overflow"); file.read_to_end(&mut buffer).expect("buffer overflow");
let input = BytesInput::new(buffer); let input = GeneralizedInput::new(buffer);
initial_inputs.push(input); initial_inputs.push(input);
} }
} }
// The closure that we want to fuzz // The closure that we want to fuzz
let mut harness = |input: &BytesInput| { let mut harness = |input: &GeneralizedInput| {
let target_bytes = input.target_bytes(); let target_bytes = input.target_bytes();
let bytes = target_bytes.as_slice(); let bytes = target_bytes.as_slice();
@ -77,12 +77,18 @@ pub fn main() {
signals_set(3); signals_set(3);
} }
unsafe {
if input.grimoire_mutated {
// println!(">>> {:?}", input.generalized());
println!(">>> {:?}", std::str::from_utf8_unchecked(bytes));
}
}
signals_set(1); signals_set(1);
ExitKind::Ok ExitKind::Ok
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", &mut SIGNALS) }; let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
// Feedback to rate the interestingness of an input // Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new_tracking(&observer, false, true); let mut feedback = MaxMapFeedback::new_tracking(&observer, false, true);
@ -112,7 +118,7 @@ pub fn main() {
} }
// The Monitor trait define how the fuzzer stats are reported to the user // 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 // 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 // such as the notification of the addition of a new item to the corpus
@ -152,7 +158,7 @@ pub fn main() {
let mut stages = tuple_list!( let mut stages = tuple_list!(
generalization, generalization,
StdMutationalStage::new(mutator), StdMutationalStage::new(mutator),
StdMutationalStage::transforming(grimoire_mutator) StdMutationalStage::new(grimoire_mutator)
); );
for input in initial_inputs { for input in initial_inputs {

View File

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

View File

@ -33,7 +33,7 @@ pub fn main() -> Result<(), Error> {
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = let observer =
unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) }; unsafe { StdMapObserver::new_from_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) };
let factory = MapEqualityFactory::new_from_observer(&observer); let factory = MapEqualityFactory::new_from_observer(&observer);
@ -44,7 +44,7 @@ pub fn main() -> Result<(), Error> {
let mut objective = CrashFeedback::new(); let mut objective = CrashFeedback::new();
// The Monitor trait define how the fuzzer stats are displayed to the user // 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); let mut mgr = SimpleEventManager::new(mon);
@ -116,7 +116,7 @@ pub fn main() -> Result<(), Error> {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are displayed to the user // 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); let mut mgr = SimpleEventManager::new(mon);
@ -136,13 +136,7 @@ pub fn main() -> Result<(), Error> {
let mut executor = InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut mgr)?; 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])?; state.load_initial_inputs_forced(&mut fuzzer, &mut executor, &mut mgr, &[solution_dir])?;
stages.perform_all( stages.perform_all(&mut fuzzer, &mut executor, &mut state, &mut mgr, 0)?;
&mut fuzzer,
&mut executor,
&mut state,
&mut mgr,
CorpusId::from(0_usize),
)?;
Ok(()) Ok(())
} }

View File

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

View File

@ -46,7 +46,7 @@ pub fn main() {
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", &mut SIGNALS) }; let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
// Feedback to rate the interestingness of an input // Feedback to rate the interestingness of an input
let mut feedback = feedback_or!( let mut feedback = feedback_or!(
@ -79,7 +79,7 @@ pub fn main() {
} }
// The Monitor trait define how the fuzzer stats are reported to the user // 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 // 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 // such as the notification of the addition of a new item to the corpus

View File

@ -1,6 +1,6 @@
[package] [package]
name = "baby_fuzzer_swap_differential" name = "baby_fuzzer_swap_differential"
version = "0.9.0" version = "0.7.1"
authors = ["Addison Crump <research@addisoncrump.info>"] authors = ["Addison Crump <research@addisoncrump.info>"]
edition = "2021" edition = "2021"
default-run = "fuzzer_sd" default-run = "fuzzer_sd"

View File

@ -27,7 +27,9 @@ fn main() -> anyhow::Result<()> {
}) })
.join("release/libafl_cc"); .join("release/libafl_cc");
println!("cargo:rerun-if-changed={}", compiler.to_str().unwrap()); println!("cargo:rerun-if-changed={}", compiler.to_str().unwrap());
if compiler.try_exists().unwrap_or(false) { if !compiler.try_exists().unwrap_or(false) {
println!("cargo:warning=Can't find libafl_cc; assuming that we're building it.");
} else {
cc::Build::new() cc::Build::new()
.compiler(compiler) .compiler(compiler)
.file("first.c") .file("first.c")
@ -36,8 +38,6 @@ fn main() -> anyhow::Result<()> {
.compile("diff-target"); .compile("diff-target");
println!("cargo:rustc-link-lib=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() { let is_cpp = match wrapper_name[wrapper_name.len()-2..].to_lowercase().as_str() {
"cc" => false, "cc" => false,
"++" | "pp" | "xx" => true, "++" | "pp" | "xx" => true,
_ => panic!("Could not figure out if c or c++ wrapper was called. Expected {dir:?} to end with c or cxx"), _ => panic!("Could not figure out if c or c++ wrapper was called. Expected {:?} to end with c or cxx", dir),
}; };
dir.pop(); dir.pop();

View File

@ -32,11 +32,10 @@ static GLOBAL: MiMalloc = MiMalloc;
// bindings to the functions defined in the target // bindings to the functions defined in the target
mod bindings { mod bindings {
#![allow(non_snake_case)]
#![allow(non_camel_case_types)] #![allow(non_camel_case_types)]
#![allow(non_upper_case_globals)] #![allow(non_upper_case_globals)]
#![allow(unused)] #![allow(unused)]
#![allow(clippy::unreadable_literal)]
include!(concat!(env!("OUT_DIR"), "/bindings.rs")); include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
} }
@ -46,26 +45,23 @@ use bindings::{inspect_first, inspect_second};
mod multimap { mod multimap {
pub use libafl::observers::{HitcountsIterableMapObserver, MultiMapObserver}; pub use libafl::observers::{HitcountsIterableMapObserver, MultiMapObserver};
pub static mut FIRST_EDGES: &mut [u8] = &mut []; pub static mut FIRST_EDGES: &'static mut [u8] = &mut [];
pub static mut SECOND_EDGES: &mut [u8] = &mut []; pub static mut SECOND_EDGES: &'static mut [u8] = &mut [];
pub static mut COMBINED_EDGES: [&mut [u8]; 2] = [&mut [], &mut []]; pub static mut COMBINED_EDGES: [&'static mut [u8]; 2] = [&mut [], &mut []];
} }
#[cfg(feature = "multimap")] #[cfg(feature = "multimap")]
use multimap::{ use multimap::*;
HitcountsIterableMapObserver, MultiMapObserver, COMBINED_EDGES, FIRST_EDGES, SECOND_EDGES,
};
#[cfg(not(feature = "multimap"))] #[cfg(not(feature = "multimap"))]
mod slicemap { mod slicemap {
pub use libafl::observers::HitcountsMapObserver; pub use libafl::observers::HitcountsMapObserver;
pub static mut EDGES: &mut [u8] = &mut []; pub static mut EDGES: &'static mut [u8] = &mut [];
} }
#[cfg(not(feature = "multimap"))] #[cfg(not(feature = "multimap"))]
use slicemap::{HitcountsMapObserver, EDGES}; use slicemap::*;
#[allow(clippy::similar_names)] #[allow(clippy::similar_names)]
#[allow(clippy::too_many_lines)]
pub fn main() { pub fn main() {
// The closure that we want to fuzz // The closure that we want to fuzz
let mut first_harness = |input: &BytesInput| { let mut first_harness = |input: &BytesInput| {
@ -98,8 +94,8 @@ pub fn main() {
} }
// create the base maps used to observe the different executors from two independent maps // create the base maps used to observe the different executors from two independent maps
let mut first_map_observer = unsafe { StdMapObserver::new("first-edges", FIRST_EDGES) }; let mut first_map_observer = StdMapObserver::new("first-edges", unsafe { FIRST_EDGES });
let mut second_map_observer = unsafe { StdMapObserver::new("second-edges", SECOND_EDGES) }; let mut second_map_observer = StdMapObserver::new("second-edges", unsafe { SECOND_EDGES });
// create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!) // create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!)
let map_swapper = let map_swapper =
@ -108,13 +104,10 @@ pub fn main() {
// create a combined map observer, e.g. for calibration // create a combined map observer, e.g. for calibration
// we use MultiMapObserver::differential to indicate that we want to use the observer in // we use MultiMapObserver::differential to indicate that we want to use the observer in
// differential mode // differential mode
let map_observer = unsafe { let map_observer = HitcountsIterableMapObserver::new(MultiMapObserver::differential(
HitcountsIterableMapObserver::new(MultiMapObserver::differential(
"combined-edges", "combined-edges",
&mut COMBINED_EDGES, unsafe { &mut COMBINED_EDGES },
)) ));
};
( (
first_map_observer, first_map_observer,
second_map_observer, second_map_observer,
@ -131,16 +124,10 @@ pub fn main() {
} }
// create the base maps used to observe the different executors by splitting a slice // create the base maps used to observe the different executors by splitting a slice
let mut first_map_observer = unsafe { let mut first_map_observer =
StdMapObserver::from_mut_ptr("first-edges", EDGES.as_mut_ptr(), MAX_EDGES_NUM) StdMapObserver::new("first-edges", unsafe { &mut EDGES[..MAX_EDGES_NUM] });
}; let mut second_map_observer =
let mut second_map_observer = unsafe { StdMapObserver::new("second-edges", unsafe { &mut EDGES[MAX_EDGES_NUM..] });
StdMapObserver::from_mut_ptr(
"second-edges",
EDGES.as_mut_ptr().add(MAX_EDGES_NUM),
MAX_EDGES_NUM,
)
};
// create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!) // create a map swapper so that we can replace the coverage map pointer (requires feature pointer_maps!)
let map_swapper = let map_swapper =
@ -149,14 +136,10 @@ pub fn main() {
// create a combined map observer, e.g. for calibration // create a combined map observer, e.g. for calibration
// we use StdMapObserver::differential to indicate that we want to use the observer in // we use StdMapObserver::differential to indicate that we want to use the observer in
// differential mode // differential mode
let map_observer = unsafe { let map_observer =
HitcountsMapObserver::new(StdMapObserver::differential_from_mut_ptr( HitcountsMapObserver::new(StdMapObserver::differential("combined-edges", unsafe {
"combined-edges", EDGES
EDGES.as_mut_ptr(), }));
MAX_EDGES_NUM * 2,
))
};
( (
first_map_observer, first_map_observer,
second_map_observer, second_map_observer,
@ -191,7 +174,7 @@ pub fn main() {
// The Monitor trait define how the fuzzer stats are displayed to the user // The Monitor trait define how the fuzzer stats are displayed to the user
#[cfg(not(feature = "tui"))] #[cfg(not(feature = "tui"))]
let mon = SimpleMonitor::new(|s| println!("{s}")); let mon = SimpleMonitor::new(|s| println!("{}", s));
#[cfg(feature = "tui")] #[cfg(feature = "tui")]
let mon = TuiMonitor::new(String::from("Baby Fuzzer"), false); let mon = TuiMonitor::new(String::from("Baby Fuzzer"), false);

View File

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

View File

@ -65,8 +65,7 @@ pub fn main() {
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
unsafe { StdMapObserver::from_mut_ptr("signals", SIGNALS.as_mut_ptr(), SIGNALS.len()) };
// Feedback to rate the interestingness of an input // Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer); let mut feedback = MaxMapFeedback::new(&observer);
@ -92,7 +91,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are reported to the user // 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 // 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 // such as the notification of the addition of a new item to the corpus

View File

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

View File

@ -64,7 +64,7 @@ pub fn main() {
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", signals_clone.as_mut_slice()) }; let observer = StdMapObserver::new("signals", signals_clone.as_mut_slice());
// Create a stacktrace observer to add the observers tuple // Create a stacktrace observer to add the observers tuple
// Feedback to rate the interestingness of an input, obtained by ANDing the interestingness of both feedbacks // Feedback to rate the interestingness of an input, obtained by ANDing the interestingness of both feedbacks
@ -91,7 +91,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are displayed to the user // 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));
// The event manager handle the various events generated during the fuzzing loop // 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 // such as the notification of the addition of a new item to the corpus

View File

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

View File

@ -87,7 +87,7 @@ pub extern "C" fn main(_argc: isize, _argv: *const *const u8) -> isize {
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", &mut SIGNALS) }; let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
// Feedback to rate the interestingness of an input // Feedback to rate the interestingness of an input
let mut feedback = MaxMapFeedback::new(&observer); let mut feedback = MaxMapFeedback::new(&observer);

View File

@ -45,7 +45,7 @@ pub fn main() {
libafl::executors::ExitKind::Ok libafl::executors::ExitKind::Ok
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { ConstMapObserver::<u8, 3>::from_mut_ptr("signals", map_ptr) }; let observer = unsafe { ConstMapObserver::<u8, 3>::new_from_ptr("signals", map_ptr) };
// Create a stacktrace observer // Create a stacktrace observer
let mut bt = shmem_provider.new_shmem_object::<Option<u64>>().unwrap(); let mut bt = shmem_provider.new_shmem_object::<Option<u64>>().unwrap();
let bt_observer = BacktraceObserver::new( let bt_observer = BacktraceObserver::new(
@ -78,7 +78,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are displayed to the user // 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));
// The event manager handle the various events generated during the fuzzing loop // 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 // such as the notification of the addition of a new item to the corpus

View File

@ -35,7 +35,7 @@ pub fn main() {
libafl::executors::ExitKind::Ok libafl::executors::ExitKind::Ok
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { ConstMapObserver::<u8, 3>::from_mut_ptr("signals", array_ptr) }; let observer = unsafe { ConstMapObserver::<u8, 3>::new_from_ptr("signals", array_ptr) };
// Create a stacktrace observer // Create a stacktrace observer
let mut bt = None; let mut bt = None;
let bt_observer = BacktraceObserver::new( let bt_observer = BacktraceObserver::new(
@ -68,7 +68,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are displayed to the user // 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));
// The event manager handle the various events generated during the fuzzing loop // 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 // such as the notification of the addition of a new item to the corpus

View File

@ -38,7 +38,7 @@ pub fn main() {
let shmem_id = signals.id(); let shmem_id = signals.id();
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", signals.as_mut_slice()) }; let observer = StdMapObserver::new("signals", signals.as_mut_slice());
// Create a stacktrace observer // Create a stacktrace observer
let bt_observer = AsanBacktraceObserver::new("AsanBacktraceObserver"); let bt_observer = AsanBacktraceObserver::new("AsanBacktraceObserver");
@ -67,7 +67,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are displayed to the user // 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));
// The event manager handle the various events generated during the fuzzing loop // 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 // such as the notification of the addition of a new item to the corpus

View File

@ -78,7 +78,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are reported to the user // 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 // 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 // such as the notification of the addition of a new item to the corpus

View File

@ -65,7 +65,7 @@ pub fn main() {
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", signals_clone.as_mut_slice()) }; let observer = StdMapObserver::new("signals", signals_clone.as_mut_slice());
// Create a stacktrace observer // Create a stacktrace observer
let bt_observer = BacktraceObserver::new( let bt_observer = BacktraceObserver::new(
"BacktraceObserver", "BacktraceObserver",
@ -97,7 +97,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are displayed to the user // 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));
// The event manager handle the various events generated during the fuzzing loop // 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 // such as the notification of the addition of a new item to the corpus

View File

@ -58,7 +58,7 @@ pub fn main() {
}; };
// Create an observation channel using the signals map // Create an observation channel using the signals map
let observer = unsafe { StdMapObserver::new("signals", &mut SIGNALS) }; let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
// Create a stacktrace observer to add the observers tuple // Create a stacktrace observer to add the observers tuple
let mut bt = None; let mut bt = None;
let bt_observer = BacktraceObserver::new( let bt_observer = BacktraceObserver::new(
@ -91,7 +91,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are displayed to the user // 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));
// The event manager handle the various events generated during the fuzzing loop // 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 // such as the notification of the addition of a new item to the corpus

View File

@ -1,32 +0,0 @@
[package]
name = "forkserver_libafl_cc"
version = "0.8.2"
authors = ["ergrelet <ergrelet@users.noreply.github.com>"]
edition = "2021"
[features]
default = ["std"]
std = []
# Forces a crash
crash = []
[profile.release]
lto = true
codegen-units = 1
opt-level = 3
debug = true
[build-dependencies]
cc = { version = "1.0", features = ["parallel"] }
which = { version = "4.0.2" }
[dependencies]
libafl = { path = "../../libafl/", features = ["default"] }
clap = { version = "4.0", features = ["derive"] }
nix = "0.25"
libafl_targets = { path = "../../libafl_targets/" }
libafl_cc = { path = "../../libafl_cc/" }
[lib]
name = "libforkserver_libafl_cc"
crate-type = ["staticlib"]

View File

@ -1,115 +0,0 @@
# Variables
[env]
FUZZER_NAME='fuzzer_libafl_cc'
CARGO_TARGET_DIR = { value = "${PROJECT_DIR}/target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
LIBAFL_CC = '${CARGO_TARGET_DIR}/release/libafl_cc'
LIBAFL_CXX = '${CARGO_TARGET_DIR}/release/libafl_cxx'
FUZZER = '${CARGO_TARGET_DIR}/release/${FUZZER_NAME}'
PROJECT_DIR = { script = ["pwd"] }
[tasks.unsupported]
script_runner="@shell"
script='''
echo "Cargo-make not integrated yet on this"
'''
# Compilers
[tasks.cxx]
linux_alias = "cxx_unix"
mac_alias = "cxx_unix"
windows_alias = "unsupported"
[tasks.cxx_unix]
command = "cargo"
args = ["build" , "--release"]
[tasks.cc]
linux_alias = "cc_unix"
mac_alias = "cc_unix"
windows_alias = "unsupported"
[tasks.cc_unix]
command = "cargo"
args = ["build" , "--release"]
[tasks.crash_cxx]
linux_alias = "crash_cxx_unix"
mac_alias = "crash_cxx_unix"
windows_alias = "unsupported"
[tasks.crash_cxx_unix]
command = "cargo"
args = ["build" , "--release", "--features=crash"]
[tasks.crash_cc]
linux_alias = "crash_cc_unix"
mac_alias = "crash_cc_unix"
windows_alias = "unsupported"
[tasks.crash_cc_unix]
command = "cargo"
args = ["build" , "--release", "--features=crash"]
# Harness
[tasks.fuzzer]
linux_alias = "fuzzer_unix"
mac_alias = "fuzzer_unix"
windows_alias = "unsupported"
[tasks.fuzzer_unix]
command = "${CARGO_TARGET_DIR}/release/libafl_cc"
args = ["${PROJECT_DIR}/src/program.c", "-o", "${FUZZER_NAME}", "-lm"]
dependencies = [ "cxx", "cc" ]
# Crashing Harness
[tasks.fuzzer_crash]
linux_alias = "fuzzer_crash_unix"
mac_alias = "fuzzer_crash_unix"
windows_alias = "unsupported"
[tasks.fuzzer_crash_unix]
command = "${CARGO_TARGET_DIR}/release/libafl_cc"
args = ["${PROJECT_DIR}/src/program.c", "-o", "${FUZZER_NAME}_crash", "-lm"]
dependencies = [ "crash_cxx", "crash_cc" ]
# Run the fuzzer
[tasks.run]
linux_alias = "run_unix"
mac_alias = "run_unix"
windows_alias = "unsupported"
[tasks.run_unix]
script_runner = "@shell"
script='''
taskset -c 1 ${CARGO_TARGET_DIR}/release/${CARGO_MAKE_PROJECT_NAME} ./${FUZZER_NAME} ./corpus/ -t 1000
'''
dependencies = [ "fuzzer" ]
# Run the fuzzer with a crash
[tasks.crash]
linux_alias = "crash_unix"
mac_alias = "crash_unix"
windows_alias = "unsupported"
[tasks.crash_unix]
script_runner = "@shell"
script='''
taskset -c 1 ${CARGO_TARGET_DIR}/release/${CARGO_MAKE_PROJECT_NAME} ./${FUZZER_NAME}_crash ./corpus/ -t 1000
'''
dependencies = [ "fuzzer_crash" ]
# Clean up
[tasks.clean]
linux_alias = "clean_unix"
mac_alias = "clean_unix"
windows_alias = "unsupported"
[tasks.clean_unix]
# Disable default `clean` definition
clear = true
script_runner="@shell"
script='''
rm -f ./${FUZZER_NAME}
cargo clean
'''

View File

@ -1,13 +0,0 @@
# Simple Forkserver Fuzzer
This is a simple example fuzzer to fuzz an executable instrumented by libafl_cc.
## Usage
You can build this example by running `cargo make fuzzer`.
This compiles, libafl_cc, the fuzzer and the example harness program in
`src/program.c` with libafl_cc.
## Run
You can run this example by running `cargo make run`.

View File

@ -1 +0,0 @@
aaa

View File

@ -1,45 +0,0 @@
use std::env;
use libafl_cc::{ClangWrapper, CompilerWrapper, LLVMPasses};
pub fn main() {
let args: Vec<String> = env::args().collect();
if args.len() > 1 {
let mut dir = env::current_exe().unwrap();
let wrapper_name = dir.file_name().unwrap().to_str().unwrap();
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 {dir:?} to end with c or cxx"),
};
dir.pop();
let mut cc = ClangWrapper::new();
if let Some(code) = cc
.cpp(is_cpp)
// silence the compiler wrapper output, needed for some configure scripts.
.silence(true)
.parse_args(&args)
.expect("Failed to parse the command line")
// Enable libafl's coverage instrumentation
.add_pass(LLVMPasses::AFLCoverage)
.add_arg("-mllvm")
.add_arg("-ctx") // Context sensitive coverage
// Imitate afl-cc's compile definitions
.add_arg("-D__AFL_FUZZ_INIT()=int __afl_sharedmem_fuzzing = 1;extern unsigned int *__afl_fuzz_len;extern unsigned char *__afl_fuzz_ptr;unsigned char __afl_fuzz_alt[1048576];unsigned char *__afl_fuzz_alt_ptr = __afl_fuzz_alt;void libafl_start_forkserver(void)")
.add_arg("-D__AFL_FUZZ_TESTCASE_BUF=(__afl_fuzz_ptr ? __afl_fuzz_ptr : __afl_fuzz_alt_ptr)")
.add_arg("-D__AFL_FUZZ_TESTCASE_LEN=(__afl_fuzz_ptr ? *__afl_fuzz_len : (*__afl_fuzz_len = read(0, __afl_fuzz_alt_ptr, 1048576)) == 0xffffffff ? 0 : *__afl_fuzz_len)")
.add_arg("-D__AFL_INIT()=libafl_start_forkserver()")
// Link with libafl's forkserver implementation
.link_staticlib(&dir, "libforkserver_libafl_cc")
.run()
.expect("Failed to run the wrapped compiler")
{
std::process::exit(code);
}
} else {
panic!("LibAFL CC: No Arguments given");
}
}

View File

@ -1,5 +0,0 @@
pub mod libafl_cc;
fn main() {
libafl_cc::main();
}

View File

@ -1,9 +0,0 @@
use libafl_targets::{map_shared_memory, start_forkserver};
#[no_mangle]
pub fn libafl_start_forkserver() {
// Map shared memory region for the edge coverage map
map_shared_memory();
// Start the forkserver
start_forkserver();
}

View File

@ -1,215 +0,0 @@
use core::time::Duration;
use std::path::PathBuf;
use clap::{self, Parser};
use libafl::{
bolts::{
current_nanos,
rands::StdRand,
shmem::{ShMem, ShMemProvider, UnixShMemProvider},
tuples::{tuple_list, MatchName, Merge},
AsMutSlice,
},
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
events::SimpleEventManager,
executors::{
forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
HasObservers,
},
feedback_and_fast, feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer},
inputs::BytesInput,
monitors::SimpleMonitor,
mutators::{scheduled::havoc_mutations, tokens_mutations, StdScheduledMutator, Tokens},
observers::{HitcountsMapObserver, MapObserver, StdMapObserver, TimeObserver},
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
stages::mutational::StdMutationalStage,
state::{HasCorpus, HasMetadata, StdState},
};
use nix::sys::signal::Signal;
/// The commandline args this fuzzer accepts
#[derive(Debug, Parser)]
#[command(
name = "forkserver_libafl_cc",
about = "This is a simple example fuzzer to fuzz a executable instrumented by libafl_cc.",
author = "ergrelet <ergrelet@users.noreply.github.com>"
)]
struct Opt {
#[arg(
help = "The instrumented binary we want to fuzz",
name = "EXEC",
required = true
)]
executable: String,
#[arg(
help = "The directory to read initial inputs from ('seeds')",
name = "INPUT_DIR",
required = true
)]
in_dir: PathBuf,
#[arg(
help = "Timeout for each individual execution, in milliseconds",
short = 't',
long = "timeout",
default_value = "1200"
)]
timeout: u64,
#[arg(
help = "If not set, the child's stdout and stderror will be redirected to /dev/null",
short = 'd',
long = "debug-child",
default_value = "false"
)]
debug_child: bool,
#[arg(
help = "Arguments passed to the target",
name = "arguments",
num_args(1..),
allow_hyphen_values = true,
)]
arguments: Vec<String>,
#[arg(
help = "Signal used to stop child",
short = 's',
long = "signal",
value_parser = str::parse::<Signal>,
default_value = "SIGKILL"
)]
signal: Signal,
}
#[allow(clippy::similar_names)]
pub fn main() {
const MAP_SIZE: usize = 65536;
let opt = Opt::parse();
let corpus_dirs: Vec<PathBuf> = [opt.in_dir].to_vec();
// The unix shmem provider supported by LibAFL for shared memory
let mut shmem_provider = UnixShMemProvider::new().unwrap();
// The coverage map shared between observer and executor
let mut shmem = shmem_provider.new_shmem(MAP_SIZE).unwrap();
// let the forkserver know the shmid
shmem.write_to_env("__AFL_SHM_ID").unwrap();
let shmem_buf = shmem.as_mut_slice();
// Create an observation channel using the signals map
let edges_observer =
unsafe { HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf)) };
// Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time");
// Feedback to rate the interestingness of an input
// This one is composed by two Feedbacks in OR
let mut feedback = feedback_or!(
// New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer)
);
// A feedback to choose if an input is a solution or not
// We want to do the same crash deduplication that AFL does
let mut objective = feedback_and_fast!(
// Must be a crash
CrashFeedback::new(),
// Take it onlt if trigger new coverage over crashes
MaxMapFeedback::new(&edges_observer)
);
// 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::<BytesInput>::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(),
// 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();
// The Monitor trait define how the fuzzer stats are reported to the user
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
let mut mgr = SimpleEventManager::new(monitor);
// A minimization+queue policy to get testcasess from the corpus
let scheduler = IndexesLenTimeMinimizerScheduler::new(QueueScheduler::new());
// A fuzzer with feedbacks and a corpus scheduler
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
// If we should debug the child
let debug_child = opt.debug_child;
// Create the executor for the forkserver
let args = opt.arguments;
let mut tokens = Tokens::new();
let mut forkserver = ForkserverExecutor::builder()
.program(opt.executable)
.debug_child(debug_child)
.shmem_provider(&mut shmem_provider)
.autotokens(&mut tokens)
.parse_afl_cmdline(args)
.coverage_map_size(MAP_SIZE)
.build(tuple_list!(time_observer, edges_observer))
.unwrap();
if let Some(dynamic_map_size) = forkserver.coverage_map_size() {
forkserver
.observers_mut()
.match_name_mut::<HitcountsMapObserver<StdMapObserver<'_, u8, false>>>("shared_mem")
.unwrap()
.downsize_map(dynamic_map_size);
}
let mut executor = TimeoutForkserverExecutor::with_signal(
forkserver,
Duration::from_millis(opt.timeout),
opt.signal,
)
.expect("Failed to create the executor.");
// In case the corpus is empty (on first run), reset
if state.corpus().count() < 1 {
state
.load_initial_inputs(&mut fuzzer, &mut executor, &mut mgr, &corpus_dirs)
.unwrap_or_else(|err| {
panic!(
"Failed to load initial corpus at {:?}: {:?}",
&corpus_dirs, err
)
});
println!("We imported {} inputs from disk.", state.corpus().count());
}
state.add_metadata(tokens);
// Setup a mutational stage with a basic bytes mutator
let mutator =
StdScheduledMutator::with_max_stack_pow(havoc_mutations().merge(tokens_mutations()), 6);
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");
}

View File

@ -1,37 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// The following line is needed for shared memory testcase fuzzing
__AFL_FUZZ_INIT();
void vuln(char *buf) {
if (strcmp(buf, "vuln") == 0) { abort(); }
}
int main(int argc, char **argv) {
// Start the forkserver at this point (i.e., forks will happen here)
__AFL_INIT();
// The following five lines are for normal fuzzing.
/*
FILE *file = stdin;
if (argc > 1) { file = fopen(argv[1], "rb"); }
char buf[16];
char *p = fgets(buf, 16, file);
buf[15] = 0;
*/
// The following line is also needed for shared memory testcase fuzzing
unsigned char *buf = __AFL_FUZZ_TESTCASE_BUF; // must be after __AFL_INIT
// printf("input: %s\n", buf);
if (buf[0] == 'b') {
if (buf[1] == 'a') {
if (buf[2] == 'd') { abort(); }
}
}
vuln((char *)buf);
return 0;
}

View File

@ -1,6 +1,6 @@
[package] [package]
name = "forkserver_simple" name = "forkserver_simple"
version = "0.9.0" version = "0.8.2"
authors = ["tokatoka <tokazerkje@outlook.com>"] authors = ["tokatoka <tokazerkje@outlook.com>"]
edition = "2021" edition = "2021"

View File

@ -103,8 +103,7 @@ pub fn main() {
let shmem_buf = shmem.as_mut_slice(); let shmem_buf = shmem.as_mut_slice();
// Create an observation channel using the signals map // Create an observation channel using the signals map
let edges_observer = let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf));
unsafe { HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf)) };
// Create an observation channel to keep track of the execution time // Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time"); let time_observer = TimeObserver::new("time");
@ -115,7 +114,7 @@ pub fn main() {
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&edges_observer, true, false), MaxMapFeedback::new_tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
// A feedback to choose if an input is a solution or not // A feedback to choose if an input is a solution or not
@ -145,7 +144,7 @@ pub fn main() {
.unwrap(); .unwrap();
// The Monitor trait define how the fuzzer stats are reported to the user // 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 // 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 // such as the notification of the addition of a new item to the corpus

View File

@ -1,6 +1,6 @@
[package] [package]
name = "frida_gdiplus" name = "frida_gdiplus"
version = "0.9.0" version = "0.7.0"
authors = ["Richard Johnson <richinseattle@gmail.com>"] authors = ["Richard Johnson <richinseattle@gmail.com>"]
edition = "2021" edition = "2021"

View File

@ -1,10 +1,10 @@
//! A `libfuzzer`-like fuzzer with `llmp`-multithreading support and restarts //! A libfuzzer-like fuzzer with llmp-multithreading support and restarts
//! The example harness is built for `gdiplus`. //! The example harness is built for gdiplus.
//! NOTE: This file is 1-to-1 copy of `../../frida_libpng/fuzzer.rs`, which //! NOTE: This file is 1-to-1 copy of the ../../frida_libpng/fuzzer.rs, which
//! is platform independent. Hence, this file contains code for other platforms //! is platform independent. Hence, this file contains code for other platforms
//! but it's only meaningful for Windows because of the `gdiplus` target. If you //! but it's only meaningful for Windows because of the gdiplus target. If you
//! going to make it compilable only for Windows, don't forget to modify the //! going to make it compilable only for windows, don't foret to modify the
//! `scripts/test_all_fuzzers.sh` to opt-out this fuzzer from that test. //! scripts/test_all_fuzzers.sh to opt-out this fuzzer from that test.
use mimalloc::MiMalloc; use mimalloc::MiMalloc;
#[global_allocator] #[global_allocator]
@ -23,7 +23,7 @@ use libafl::{
tuples::{tuple_list, Merge}, tuples::{tuple_list, Merge},
AsSlice, AsSlice,
}, },
corpus::{CachedOnDiskCorpus, Corpus, OnDiskCorpus}, corpus::{ondisk::OnDiskMetadataFormat, CachedOnDiskCorpus, Corpus, OnDiskCorpus},
events::{llmp::LlmpRestartingEventManager, EventConfig}, events::{llmp::LlmpRestartingEventManager, EventConfig},
executors::{inprocess::InProcessExecutor, ExitKind, ShadowExecutor}, executors::{inprocess::InProcessExecutor, ExitKind, ShadowExecutor},
feedback_and_fast, feedback_or, feedback_or_fast, feedback_and_fast, feedback_or, feedback_or_fast,
@ -51,7 +51,7 @@ use libafl_frida::{
executor::FridaInProcessExecutor, executor::FridaInProcessExecutor,
helper::FridaInstrumentationHelper, helper::FridaInstrumentationHelper,
}; };
use libafl_targets::cmplog::CmpLogObserver; use libafl_targets::cmplog::{CmpLogObserver, CMPLOG_MAP};
/// The main fn, usually parsing parameters, and starting the fuzzer /// The main fn, usually parsing parameters, and starting the fuzzer
pub fn main() { pub fn main() {
@ -60,18 +60,18 @@ pub fn main() {
let options = parse_args(); let options = parse_args();
unsafe { unsafe {
match fuzz(&options) { match fuzz(options) {
Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."), Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."),
Err(e) => panic!("Error during fuzzing: {e:?}"), Err(e) => panic!("Error during fuzzing: {:?}", e),
} }
} }
} }
/// The actual fuzzer /// The actual fuzzer
#[allow(clippy::too_many_lines, clippy::too_many_arguments)] #[allow(clippy::too_many_lines, clippy::too_many_arguments)]
unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> { unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let monitor = MultiMonitor::new(|s| println!("{s}")); let monitor = MultiMonitor::new(|s| println!("{}", s));
let shmem_provider = StdShMemProvider::new()?; let shmem_provider = StdShMemProvider::new()?;
@ -102,15 +102,15 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
#[cfg(unix)] #[cfg(unix)]
let mut frida_helper = let mut frida_helper =
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, asan)); FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage, asan));
#[cfg(windows)] #[cfg(windows)]
let mut frida_helper = let mut frida_helper =
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage)); FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage));
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr( let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
"edges", "edges",
frida_helper.map_mut_ptr().unwrap(), frida_helper.map_ptr_mut().unwrap(),
MAP_SIZE, MAP_SIZE,
)); ));
@ -123,7 +123,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&edges_observer, true, false), MaxMapFeedback::new_tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
// Feedbacks to recognize an input as solution // Feedbacks to recognize an input as solution
@ -146,7 +146,11 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(), CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(&options.output).unwrap(), OnDiskCorpus::new_save_meta(
options.output.to_path_buf(),
Some(OnDiskMetadataFormat::JsonPretty),
)
.unwrap(),
&mut feedback, &mut feedback,
&mut objective, &mut objective,
) )
@ -221,12 +225,12 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
let cmplog = CmpLogRuntime::new(); let cmplog = CmpLogRuntime::new();
let mut frida_helper = let mut frida_helper =
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, cmplog)); FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage, cmplog));
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr( let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
"edges", "edges",
frida_helper.map_mut_ptr().unwrap(), frida_helper.map_ptr_mut().unwrap(),
MAP_SIZE, MAP_SIZE,
)); ));
@ -239,7 +243,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&edges_observer, true, false), MaxMapFeedback::new_tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
#[cfg(unix)] #[cfg(unix)]
@ -257,11 +261,14 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// RNG // RNG
StdRand::with_seed(current_nanos()), StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance // Corpus that will be evolved, we keep it in memory for performance
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64) CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
.unwrap(),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(&options.output).unwrap(), OnDiskCorpus::new_save_meta(
options.output.to_path_buf(),
Some(OnDiskMetadataFormat::JsonPretty),
)
.unwrap(),
&mut feedback, &mut feedback,
&mut objective, &mut objective,
) )
@ -323,7 +330,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
} }
// Create an observation channel using cmplog map // Create an observation channel using cmplog map
let cmplog_observer = CmpLogObserver::new("cmplog", true); let cmplog_observer = CmpLogObserver::new("cmplog", &mut CMPLOG_MAP, true);
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer)); let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
@ -351,12 +358,12 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
let coverage = CoverageRuntime::new(); let coverage = CoverageRuntime::new();
let mut frida_helper = let mut frida_helper =
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage)); FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage));
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr( let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
"edges", "edges",
frida_helper.map_mut_ptr().unwrap(), frida_helper.map_ptr_mut().unwrap(),
MAP_SIZE, MAP_SIZE,
)); ));
@ -369,7 +376,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&edges_observer, true, false), MaxMapFeedback::new_tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
#[cfg(unix)] #[cfg(unix)]
@ -387,11 +394,14 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// RNG // RNG
StdRand::with_seed(current_nanos()), StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance // Corpus that will be evolved, we keep it in memory for performance
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64) CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
.unwrap(),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(&options.output).unwrap(), OnDiskCorpus::new_save_meta(
options.output.to_path_buf(),
Some(OnDiskMetadataFormat::JsonPretty),
)
.unwrap(),
&mut feedback, &mut feedback,
&mut objective, &mut objective,
) )

View File

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

View File

@ -1,7 +1,10 @@
# Variables # Variables
[env] [env]
CARGO_TARGET_DIR = { value = "target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } } CARGO_TARGET_DIR = { value = "${CARGO_MAKE_WORKING_DIRECTORY}${SEP}target", condition = { env_not_set = ["CARGO_TARGET_DIR"] } }
SEP={ source = "${CARGO_MAKE_RUST_TARGET_OS}", default_value = "/", mapping = {"linux" = "/", "macos" = "/", "windows" = "\\"} }
FUZZER_NAME={ source = "${CARGO_MAKE_RUST_TARGET_OS}", default_value = "frida_fuzzer", mapping = {"linux" = "frida_fuzzer", "macos" = "frida_fuzzer", "windows" = "frida_fuzzer.exe"} } FUZZER_NAME={ source = "${CARGO_MAKE_RUST_TARGET_OS}", default_value = "frida_fuzzer", mapping = {"linux" = "frida_fuzzer", "macos" = "frida_fuzzer", "windows" = "frida_fuzzer.exe"} }
FUZZER = '${CARGO_TARGET_DIR}${SEP}release${SEP}${FUZZER_NAME}'
[tasks.unsupported] [tasks.unsupported]
@ -69,7 +72,7 @@ windows_alias = "fuzzer_windows"
script_runner="@shell" script_runner="@shell"
script=''' script='''
cargo build --release cargo build --release
cp ${CARGO_TARGET_DIR}/release/${FUZZER_NAME} . cp ${CARGO_TARGET_DIR}${SEP}release${SEP}${FUZZER_NAME} .
''' '''
[tasks.fuzzer_windows] [tasks.fuzzer_windows]

View File

@ -22,7 +22,7 @@
#include <vector> #include <vector>
#define PNG_INTERNAL #define PNG_INTERNAL
#include "libpng-1.6.37/png.h" #include "png.h"
#define PNG_CLEANUP \ #define PNG_CLEANUP \
if (png_handler.png_ptr) { \ if (png_handler.png_ptr) { \

View File

@ -45,7 +45,7 @@ use libafl_frida::{
executor::FridaInProcessExecutor, executor::FridaInProcessExecutor,
helper::FridaInstrumentationHelper, helper::FridaInstrumentationHelper,
}; };
use libafl_targets::cmplog::CmpLogObserver; use libafl_targets::cmplog::{CmpLogObserver, CMPLOG_MAP};
/// The main fn, usually parsing parameters, and starting the fuzzer /// The main fn, usually parsing parameters, and starting the fuzzer
pub fn main() { pub fn main() {
@ -54,18 +54,18 @@ pub fn main() {
let options = parse_args(); let options = parse_args();
unsafe { unsafe {
match fuzz(&options) { match fuzz(options) {
Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."), Ok(()) | Err(Error::ShuttingDown) => println!("\nFinished fuzzing. Good bye."),
Err(e) => panic!("Error during fuzzing: {e:?}"), Err(e) => panic!("Error during fuzzing: {:?}", e),
} }
} }
} }
/// The actual fuzzer /// The actual fuzzer
#[allow(clippy::too_many_lines, clippy::too_many_arguments)] #[allow(clippy::too_many_lines, clippy::too_many_arguments)]
unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> { unsafe fn fuzz(options: FuzzerOptions) -> Result<(), Error> {
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let monitor = MultiMonitor::new(|s| println!("{s}")); let monitor = MultiMonitor::new(|s| println!("{}", s));
let shmem_provider = StdShMemProvider::new()?; let shmem_provider = StdShMemProvider::new()?;
@ -96,15 +96,15 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
#[cfg(unix)] #[cfg(unix)]
let mut frida_helper = let mut frida_helper =
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, asan)); FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage, asan));
#[cfg(windows)] #[cfg(windows)]
let mut frida_helper = let mut frida_helper =
FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage)); FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage));
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr( let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
"edges", "edges",
frida_helper.map_mut_ptr().unwrap(), frida_helper.map_ptr_mut().unwrap(),
MAP_SIZE, MAP_SIZE,
)); ));
@ -117,7 +117,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&edges_observer, true, false), MaxMapFeedback::new_tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
// Feedbacks to recognize an input as solution // Feedbacks to recognize an input as solution
@ -137,11 +137,14 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// RNG // RNG
StdRand::with_seed(current_nanos()), StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance // Corpus that will be evolved, we keep it in memory for performance
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64) CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
.unwrap(),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(options.output.clone()).unwrap(), OnDiskCorpus::new_save_meta(
options.output.to_path_buf(),
Some(OnDiskMetadataFormat::JsonPretty),
)
.unwrap(),
&mut feedback, &mut feedback,
&mut objective, &mut objective,
) )
@ -216,12 +219,12 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
let cmplog = CmpLogRuntime::new(); let cmplog = CmpLogRuntime::new();
let mut frida_helper = let mut frida_helper =
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage, cmplog)); FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage, cmplog));
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr( let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
"edges", "edges",
frida_helper.map_mut_ptr().unwrap(), frida_helper.map_ptr_mut().unwrap(),
MAP_SIZE, MAP_SIZE,
)); ));
@ -234,7 +237,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&edges_observer, true, false), MaxMapFeedback::new_tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
#[cfg(unix)] #[cfg(unix)]
@ -252,11 +255,14 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// RNG // RNG
StdRand::with_seed(current_nanos()), StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance // Corpus that will be evolved, we keep it in memory for performance
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64) CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
.unwrap(),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(options.output.clone()).unwrap(), OnDiskCorpus::new_save_meta(
options.output.to_path_buf(),
Some(OnDiskMetadataFormat::JsonPretty),
)
.unwrap(),
&mut feedback, &mut feedback,
&mut objective, &mut objective,
) )
@ -318,7 +324,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
} }
// Create an observation channel using cmplog map // Create an observation channel using cmplog map
let cmplog_observer = CmpLogObserver::new("cmplog", true); let cmplog_observer = CmpLogObserver::new("cmplog", &mut CMPLOG_MAP, true);
let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer)); let mut executor = ShadowExecutor::new(executor, tuple_list!(cmplog_observer));
@ -346,12 +352,12 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
let coverage = CoverageRuntime::new(); let coverage = CoverageRuntime::new();
let mut frida_helper = let mut frida_helper =
FridaInstrumentationHelper::new(&gum, options, tuple_list!(coverage)); FridaInstrumentationHelper::new(&gum, &options, tuple_list!(coverage));
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = HitcountsMapObserver::new(StdMapObserver::from_mut_ptr( let edges_observer = HitcountsMapObserver::new(StdMapObserver::new_from_ptr(
"edges", "edges",
frida_helper.map_mut_ptr().unwrap(), frida_helper.map_ptr_mut().unwrap(),
MAP_SIZE, MAP_SIZE,
)); ));
@ -364,7 +370,7 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
MaxMapFeedback::new_tracking(&edges_observer, true, false), MaxMapFeedback::new_tracking(&edges_observer, true, false),
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
#[cfg(unix)] #[cfg(unix)]
@ -382,11 +388,14 @@ unsafe fn fuzz(options: &FuzzerOptions) -> Result<(), Error> {
// RNG // RNG
StdRand::with_seed(current_nanos()), StdRand::with_seed(current_nanos()),
// Corpus that will be evolved, we keep it in memory for performance // Corpus that will be evolved, we keep it in memory for performance
CachedOnDiskCorpus::no_meta(PathBuf::from("./corpus_discovered"), 64) CachedOnDiskCorpus::new(PathBuf::from("./corpus_discovered"), 64).unwrap(),
.unwrap(),
// Corpus in which we store solutions (crashes in this example), // Corpus in which we store solutions (crashes in this example),
// on disk so the user can get them after stopping the fuzzer // on disk so the user can get them after stopping the fuzzer
OnDiskCorpus::new(options.output.clone()).unwrap(), OnDiskCorpus::new_save_meta(
options.output.to_path_buf(),
Some(OnDiskMetadataFormat::JsonPretty),
)
.unwrap(),
&mut feedback, &mut feedback,
&mut objective, &mut objective,
) )

View File

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

View File

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

View File

@ -1,5 +1,5 @@
pub mod libafl_cc; pub mod libafl_cc;
fn main() { fn main() {
libafl_cc::main(); libafl_cc::main()
} }

View File

@ -36,7 +36,7 @@ use libafl::{
scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations, scheduled::havoc_mutations, token_mutations::I2SRandReplace, tokens_mutations,
StdMOptMutator, StdScheduledMutator, Tokens, StdMOptMutator, StdScheduledMutator, Tokens,
}, },
observers::{HitcountsMapObserver, TimeObserver}, observers::{HitcountsMapObserver, StdMapObserver, TimeObserver},
schedulers::{ schedulers::{
powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler, powersched::PowerSchedule, IndexesLenTimeMinimizerScheduler, StdWeightedScheduler,
}, },
@ -50,7 +50,8 @@ use libafl::{
#[cfg(any(target_os = "linux", target_vendor = "apple"))] #[cfg(any(target_os = "linux", target_vendor = "apple"))]
use libafl_targets::autotokens; use libafl_targets::autotokens;
use libafl_targets::{ use libafl_targets::{
libfuzzer_initialize, libfuzzer_test_one_input, std_edges_map_observer, CmpLogObserver, libfuzzer_initialize, libfuzzer_test_one_input, CmpLogObserver, CMPLOG_MAP, EDGES_MAP,
MAX_EDGES_NUM,
}; };
#[cfg(unix)] #[cfg(unix)]
use nix::{self, unistd::dup}; use nix::{self, unistd::dup};
@ -120,7 +121,7 @@ pub fn libafl_main() {
); );
if let Some(filenames) = res.get_many::<String>("remaining") { if let Some(filenames) = res.get_many::<String>("remaining") {
let filenames: Vec<&str> = filenames.map(String::as_str).collect(); let filenames: Vec<&str> = filenames.map(|v| v.as_str()).collect();
if !filenames.is_empty() { if !filenames.is_empty() {
run_testcases(&filenames); run_testcases(&filenames);
return; return;
@ -166,7 +167,7 @@ pub fn libafl_main() {
.expect("Could not parse timeout in milliseconds"), .expect("Could not parse timeout in milliseconds"),
); );
fuzz(out_dir, crashes, &in_dir, tokens, &logfile, timeout) fuzz(out_dir, crashes, in_dir, tokens, logfile, timeout)
.expect("An error occurred while fuzzing"); .expect("An error occurred while fuzzing");
} }
@ -175,7 +176,7 @@ fn run_testcases(filenames: &[&str]) {
// Call LLVMFUzzerInitialize() if present. // Call LLVMFUzzerInitialize() if present.
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if libfuzzer_initialize(&args) == -1 { if libfuzzer_initialize(&args) == -1 {
println!("Warning: LLVMFuzzerInitialize failed with -1"); println!("Warning: LLVMFuzzerInitialize failed with -1")
} }
println!( println!(
@ -183,7 +184,7 @@ fn run_testcases(filenames: &[&str]) {
filenames.len() filenames.len()
); );
for fname in filenames { for fname in filenames {
println!("Executing {fname}"); println!("Executing {}", fname);
let mut file = File::open(fname).expect("No file found"); let mut file = File::open(fname).expect("No file found");
let mut buffer = vec![]; let mut buffer = vec![];
@ -194,16 +195,20 @@ fn run_testcases(filenames: &[&str]) {
} }
/// The actual fuzzer /// The actual fuzzer
#[allow(clippy::too_many_lines)]
fn fuzz( fn fuzz(
corpus_dir: PathBuf, corpus_dir: PathBuf,
objective_dir: PathBuf, objective_dir: PathBuf,
seed_dir: &PathBuf, seed_dir: PathBuf,
tokenfile: Option<PathBuf>, tokenfile: Option<PathBuf>,
logfile: &PathBuf, logfile: PathBuf,
timeout: Duration, timeout: Duration,
) -> Result<(), Error> { ) -> Result<(), Error> {
let log = RefCell::new(OpenOptions::new().append(true).create(true).open(logfile)?); let log = RefCell::new(
OpenOptions::new()
.append(true)
.create(true)
.open(&logfile)?,
);
#[cfg(unix)] #[cfg(unix)]
let mut stdout_cpy = unsafe { let mut stdout_cpy = unsafe {
@ -216,10 +221,10 @@ fn fuzz(
// 'While the monitor are state, they are usually used in the broker - which is likely never restarted // 'While the monitor are state, they are usually used in the broker - which is likely never restarted
let monitor = SimpleMonitor::new(|s| { let monitor = SimpleMonitor::new(|s| {
#[cfg(unix)] #[cfg(unix)]
writeln!(&mut stdout_cpy, "{s}").unwrap(); writeln!(&mut stdout_cpy, "{}", s).unwrap();
#[cfg(windows)] #[cfg(windows)]
println!("{s}"); println!("{}", s);
writeln!(log.borrow_mut(), "{:?} {s}", current_time()).unwrap(); writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap();
}); });
// We need a shared map to store our state before a crash. // We need a shared map to store our state before a crash.
@ -235,19 +240,21 @@ fn fuzz(
return Ok(()); return Ok(());
} }
_ => { _ => {
panic!("Failed to setup the restarter: {err}"); panic!("Failed to setup the restarter: {}", err);
} }
}, },
}; };
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
// We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges) // We don't use the hitcounts (see the Cargo.toml, we use pcguard_edges)
let edges_observer = HitcountsMapObserver::new(unsafe { std_edges_map_observer("edges") }); let edges = unsafe { &mut EDGES_MAP[0..MAX_EDGES_NUM] };
let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("edges", edges));
// Create an observation channel to keep track of the execution time // Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time"); let time_observer = TimeObserver::new("time");
let cmplog_observer = CmpLogObserver::new("cmplog", true); let cmplog = unsafe { &mut CMPLOG_MAP };
let cmplog_observer = CmpLogObserver::new("cmplog", cmplog, true);
let map_feedback = MaxMapFeedback::new_tracking(&edges_observer, true, false); let map_feedback = MaxMapFeedback::new_tracking(&edges_observer, true, false);
@ -259,7 +266,7 @@ fn fuzz(
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
map_feedback, map_feedback,
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
// A feedback to choose if an input is a solution or not // A feedback to choose if an input is a solution or not
@ -290,7 +297,7 @@ fn fuzz(
// Call LLVMFUzzerInitialize() if present. // Call LLVMFUzzerInitialize() if present.
let args: Vec<String> = env::args().collect(); let args: Vec<String> = env::args().collect();
if libfuzzer_initialize(&args) == -1 { if libfuzzer_initialize(&args) == -1 {
println!("Warning: LLVMFuzzerInitialize failed with -1"); println!("Warning: LLVMFuzzerInitialize failed with -1")
} }
// Setup a randomic Input2State stage // Setup a randomic Input2State stage
@ -387,7 +394,12 @@ fn fuzz(
dup2(null_fd, io::stderr().as_raw_fd())?; dup2(null_fd, io::stderr().as_raw_fd())?;
} }
// reopen file to make sure we're at the end // reopen file to make sure we're at the end
log.replace(OpenOptions::new().append(true).create(true).open(logfile)?); log.replace(
OpenOptions::new()
.append(true)
.create(true)
.open(&logfile)?,
);
fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?; fuzzer.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)?;

View File

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

View File

@ -45,7 +45,7 @@ use libafl::{
Error, Error,
}; };
use libafl_qemu::{ use libafl_qemu::{
cmplog::{CmpLogMap, CmpLogObserver, QemuCmpLogChildHelper}, cmplog::{CmpLogMap, CmpLogObserver, QemuCmpLogChildHelper, CMPLOG_MAP_PTR},
edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE}, edges::{QemuEdgeCoverageChildHelper, EDGES_MAP_PTR, EDGES_MAP_SIZE},
elf::EasyElf, elf::EasyElf,
emu::Emulator, emu::Emulator,
@ -195,16 +195,13 @@ fn fuzz(
let file_null = File::open("/dev/null")?; let file_null = File::open("/dev/null")?;
// 'While the stats are state, they are usually used in the broker - which is likely never restarted // 'While the stats are state, they are usually used in the broker - which is likely never restarted
let monitor = SimpleMonitor::with_user_monitor( let monitor = SimpleMonitor::new(|s| {
|s| {
#[cfg(unix)] #[cfg(unix)]
writeln!(&mut stdout_cpy, "{s}").unwrap(); writeln!(&mut stdout_cpy, "{}", s).unwrap();
#[cfg(windows)] #[cfg(windows)]
println!("{s}"); println!("{}", s);
writeln!(log.borrow_mut(), "{:?} {s}", current_time()).unwrap(); writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap();
}, });
true,
);
let mut shmem_provider = StdShMemProvider::new()?; let mut shmem_provider = StdShMemProvider::new()?;
@ -216,10 +213,7 @@ fn fuzz(
.new_shmem(core::mem::size_of::<CmpLogMap>()) .new_shmem(core::mem::size_of::<CmpLogMap>())
.unwrap(); .unwrap();
let cmplog = cmp_shmem.as_mut_slice(); let cmplog = cmp_shmem.as_mut_slice();
unsafe { CMPLOG_MAP_PTR = cmplog.as_mut_ptr() as *mut CmpLogMap };
// Beginning of a page should be properly aligned.
#[allow(clippy::cast_ptr_alignment)]
let cmplog_map_ptr = cmplog.as_mut_ptr().cast::<libafl_qemu::cmplog::CmpLogMap>();
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider) let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
{ {
@ -230,24 +224,21 @@ fn fuzz(
return Ok(()); return Ok(());
} }
_ => { _ => {
panic!("Failed to setup the restarter: {err}"); panic!("Failed to setup the restarter: {}", err);
} }
}, },
}; };
// Create an observation channel using the coverage map // Create an observation channel using the coverage map
let edges_observer = unsafe { let edges_observer =
HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_SIZE>::from_mut_ptr( HitcountsMapObserver::new(ConstMapObserver::<_, EDGES_MAP_SIZE>::new("edges", edges));
"edges",
edges.as_mut_ptr(),
))
};
// Create an observation channel to keep track of the execution time // Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time"); let time_observer = TimeObserver::new("time");
// Create an observation channel using cmplog map // Create an observation channel using cmplog map
let cmplog_observer = unsafe { CmpLogObserver::with_map_ptr("cmplog", cmplog_map_ptr, true) }; let cmplog_observer =
CmpLogObserver::new("cmplog", unsafe { CMPLOG_MAP_PTR.as_mut().unwrap() }, true);
let map_feedback = MaxMapFeedback::new_tracking(&edges_observer, true, false); let map_feedback = MaxMapFeedback::new_tracking(&edges_observer, true, false);
@ -259,7 +250,7 @@ fn fuzz(
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
map_feedback, map_feedback,
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
// A feedback to choose if an input is a solution or not // A feedback to choose if an input is a solution or not

View File

@ -3,5 +3,5 @@ pub mod fuzzer;
fn main() { fn main() {
#[cfg(target_os = "linux")] #[cfg(target_os = "linux")]
fuzzer::main(); fuzzer::main()
} }

View File

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

View File

@ -19,7 +19,7 @@ use libafl::{
corpus::{Corpus, OnDiskCorpus}, corpus::{Corpus, OnDiskCorpus},
events::SimpleEventManager, events::SimpleEventManager,
executors::forkserver::{ForkserverExecutor, TimeoutForkserverExecutor}, executors::forkserver::{ForkserverExecutor, TimeoutForkserverExecutor},
feedback_or, feedback_and_fast, feedback_or,
feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback}, feedbacks::{CrashFeedback, MaxMapFeedback, TimeFeedback},
fuzzer::{Fuzzer, StdFuzzer}, fuzzer::{Fuzzer, StdFuzzer},
inputs::BytesInput, inputs::BytesInput,
@ -225,7 +225,7 @@ fn fuzz(
// 'While the monitor are state, they are usually used in the broker - which is likely never restarted // 'While the monitor are state, they are usually used in the broker - which is likely never restarted
let monitor = SimpleMonitor::new(|s| { let monitor = SimpleMonitor::new(|s| {
println!("{s}"); println!("{}", s);
writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap(); writeln!(log.borrow_mut(), "{:?} {}", current_time(), s).unwrap();
}); });
@ -241,12 +241,9 @@ fn fuzz(
// let the forkserver know the shmid // let the forkserver know the shmid
shmem.write_to_env("__AFL_SHM_ID").unwrap(); shmem.write_to_env("__AFL_SHM_ID").unwrap();
let shmem_buf = shmem.as_mut_slice(); let shmem_buf = shmem.as_mut_slice();
// To let know the AFL++ binary that we have a big map
std::env::set_var("AFL_MAP_SIZE", format!("{}", MAP_SIZE));
// Create an observation channel using the hitcounts map of AFL++ // Create an observation channel using the hitcounts map of AFL++
let edges_observer = let edges_observer = HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf));
unsafe { HitcountsMapObserver::new(StdMapObserver::new("shared_mem", shmem_buf)) };
// Create an observation channel to keep track of the execution time // Create an observation channel to keep track of the execution time
let time_observer = TimeObserver::new("time"); let time_observer = TimeObserver::new("time");
@ -261,11 +258,17 @@ fn fuzz(
// New maximization map feedback linked to the edges observer and the feedback state // New maximization map feedback linked to the edges observer and the feedback state
map_feedback, map_feedback,
// Time feedback, this one does not need a feedback state // Time feedback, this one does not need a feedback state
TimeFeedback::with_observer(&time_observer) TimeFeedback::new_with_observer(&time_observer)
); );
// A feedback to choose if an input is a solution or not // A feedback to choose if an input is a solution or not
let mut objective = CrashFeedback::new(); // We want to do the same crash deduplication that AFL does
let mut objective = feedback_and_fast!(
// Must be a crash
CrashFeedback::new(),
// Take it onlt if trigger new coverage over crashes
MaxMapFeedback::new(&edges_observer)
);
// create a State from scratch // create a State from scratch
let mut state = StdState::new( let mut state = StdState::new(

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