Compare commits
86 Commits
Author | SHA1 | Date | |
---|---|---|---|
798aa2ceb9 | |||
183ff32beb | |||
89979b64d9 | |||
35da9fdf24 | |||
1bca346b39 | |||
8b90886299 | |||
1bd7d853ac | |||
253048e534 | |||
52cc00fedc | |||
eec998c426 | |||
a328ddfd5f | |||
6a042da5c1 | |||
2e20a22dc6 | |||
bbc83ef6be | |||
48466ac2d7 | |||
ad8cecdba4 | |||
c2afc0186e | |||
4df67db479 | |||
402eff7b47 | |||
a8a6c175c8 | |||
8a79e12f91 | |||
a3e38b6abb | |||
eb04325f09 | |||
cfb8fa2b32 | |||
2889e9bf61 | |||
960764cf85 | |||
e6816cc2de | |||
f3180a35cc | |||
54312b2577 | |||
6d920fd962 | |||
281979ecd8 | |||
c628afaa81 | |||
c548c6bc09 | |||
6e8769907d | |||
bf639e42fa | |||
a05ff97d0c | |||
f09034b7fe | |||
d118eeacbd | |||
57fc441118 | |||
10b5fe8a74 | |||
7f987b037d | |||
58be280a62 | |||
3c586f5047 | |||
9336b932d0 | |||
e0f73778e2 | |||
e5ac5ba825 | |||
2acf3ef301 | |||
28bac2a850 | |||
41586dd8b1 | |||
7420aabeeb | |||
d118ff0056 | |||
dfe4f713b9 | |||
f7a05d2a7c | |||
2593bdf42f | |||
8c8ab7c44e | |||
9cadc5d61c | |||
594554eca0 | |||
267309b954 | |||
35435fbd97 | |||
8fcc54bbdd | |||
1f538f9834 | |||
ba01f600ee | |||
2cb479581d | |||
1fbf948478 | |||
6e1d5695e3 | |||
8d31196614 | |||
4c90144db5 | |||
eeaf7eb43f | |||
68c4887dad | |||
7ca2d43f3d | |||
9f97852e4a | |||
f4e1990387 | |||
d936234976 | |||
795fbff61a | |||
6a9df35e28 | |||
9b9fbc3677 | |||
decae09931 | |||
b812e994a6 | |||
4587f442d0 | |||
c748fecbe2 | |||
7595d25192 | |||
79bca99cc7 | |||
b07f7ccbca | |||
e3f38edd0a | |||
6ad55e3b29 | |||
f7ee38ebb2 |
@ -6,30 +6,22 @@
|
|||||||
"context": "..",
|
"context": "..",
|
||||||
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
|
// Update the 'dockerFile' property if you aren't using the standard 'Dockerfile' filename.
|
||||||
"dockerFile": "../Dockerfile",
|
"dockerFile": "../Dockerfile",
|
||||||
"customizations": {
|
// Set *default* container specific settings.json values on container create.
|
||||||
"vscode": {
|
"settings": {},
|
||||||
// Add the IDs of extensions you want installed when the container is created.
|
// Add the IDs of extensions you want installed when the container is created.
|
||||||
"extensions": [
|
"extensions": [
|
||||||
"rust-lang.rust-analyzer",
|
"matklad.rust-analyzer"
|
||||||
"microsoft.Docker"
|
|
||||||
],
|
],
|
||||||
// Set *default* container specific settings.json values on container create.
|
|
||||||
"settings": {
|
|
||||||
"rust-analyzer.cargo.noDefaultFeatures": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
// "forwardPorts": [],
|
// "forwardPorts": [],
|
||||||
// Uncomment the next line to run commands after the container is created - for example installing curl.
|
// Uncomment the next line to run commands after the container is created - for example installing curl.
|
||||||
// Install development components that shouldn't be in the main Dockerfile
|
// "postCreateCommand": "apt-get update && apt-get install -y curl",
|
||||||
"postCreateCommand": "rustup component add --toolchain nightly rustfmt clippy llvm-tools-preview && cargo binstall --locked cargo-make",
|
|
||||||
// Uncomment when using a ptrace-based debugger like C++, Go, and Rust
|
// Uncomment when using a ptrace-based debugger like C++, Go, and Rust
|
||||||
"runArgs": [
|
"runArgs": [
|
||||||
"--cap-add=SYS_PTRACE",
|
"--cap-add=SYS_PTRACE",
|
||||||
"--security-opt",
|
"--security-opt",
|
||||||
"seccomp=unconfined"
|
"seccomp=unconfined"
|
||||||
]
|
],
|
||||||
// Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker.
|
// Uncomment to use the Docker CLI from inside the container. See https://aka.ms/vscode-remote/samples/docker-from-docker.
|
||||||
// "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ],
|
// "mounts": [ "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" ],
|
||||||
// Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
|
// Uncomment to connect as a non-root user if you've added one. See https://aka.ms/vscode-remote/containers/non-root.
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
**/target
|
target
|
||||||
**/.git
|
|
||||||
Cargo.lock
|
Cargo.lock
|
||||||
|
|
||||||
*.o
|
*.o
|
||||||
|
14
.github/.linkspector.yml
vendored
14
.github/.linkspector.yml
vendored
@ -1,14 +0,0 @@
|
|||||||
dirs:
|
|
||||||
- .
|
|
||||||
|
|
||||||
useGitIgnore: true
|
|
||||||
|
|
||||||
ignorePatterns:
|
|
||||||
- pattern: "^https://crates.io"
|
|
||||||
- pattern: "^https://github.com/AFLplusplus/linux-qemu-image-builder"
|
|
||||||
- pattern: "https://www.romu-random.org/"
|
|
||||||
|
|
||||||
aliveStatusCodes:
|
|
||||||
- 0
|
|
||||||
- 200
|
|
||||||
- 403
|
|
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
2
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -13,8 +13,6 @@ Thank you for making LibAFL better!
|
|||||||
|
|
||||||
**Describe the bug**
|
**Describe the bug**
|
||||||
A clear and concise description of what the bug is.
|
A clear and concise description of what the bug is.
|
||||||
If you want to present the backtrace, don't forget to run with `errors_backtrace` feature and log from `RUST_LOG`
|
|
||||||
In addition, please tell us what is your fuzzer's Cargo.toml
|
|
||||||
|
|
||||||
**To Reproduce**
|
**To Reproduce**
|
||||||
Steps to reproduce the behavior:
|
Steps to reproduce the behavior:
|
||||||
|
8
.github/dependabot.yml
vendored
8
.github/dependabot.yml
vendored
@ -1,8 +0,0 @@
|
|||||||
version: 2
|
|
||||||
updates:
|
|
||||||
- package-ecosystem: "cargo"
|
|
||||||
directory: "/"
|
|
||||||
schedule:
|
|
||||||
interval: "daily"
|
|
||||||
ignore:
|
|
||||||
- dependency-name: "pyo3"
|
|
620
.github/workflows/build_and_test.yml
vendored
620
.github/workflows/build_and_test.yml
vendored
@ -2,536 +2,291 @@ name: build and test
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ main, "pr/**" ]
|
branches: [ main ]
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
types: ["labeled", "opened", "synchronize", "reopened"]
|
|
||||||
workflow_dispatch:
|
|
||||||
merge_group:
|
|
||||||
env:
|
env:
|
||||||
CARGO_TERM_COLOR: always
|
CARGO_TERM_COLOR: always
|
||||||
CARGO_NET_GIT_FETCH_WITH_CLI: true
|
|
||||||
MAIN_LLVM_VERSION: 18
|
|
||||||
|
|
||||||
concurrency:
|
|
||||||
group: ${{ github.workflow }}-${{ github.ref }}
|
|
||||||
cancel-in-progress: true
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
common:
|
common:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-24.04, windows-latest, macOS-latest ]
|
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- name: Install mimetype
|
- uses: actions-rs/toolchain@v1
|
||||||
if: runner.os == 'Linux'
|
with:
|
||||||
run: sudo apt-get update && sudo apt-get install -y libfile-mimeinfo-perl
|
profile: minimal
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
- name: install mdbook
|
- name: install mdbook
|
||||||
uses: baptiste0928/cargo-install@v3
|
uses: baptiste0928/cargo-install@v1.3.0
|
||||||
with:
|
with:
|
||||||
crate: mdbook
|
crate: mdbook
|
||||||
- name: install linkcheck
|
- name: install linkcheck
|
||||||
uses: baptiste0928/cargo-install@v3
|
uses: baptiste0928/cargo-install@v1.3.0
|
||||||
with:
|
with:
|
||||||
crate: mdbook-linkcheck
|
crate: mdbook-linkcheck
|
||||||
# NOTE: The current crates.io release of mdbook-linkcheck (v0.7.7) is broken
|
- uses: actions/checkout@v3
|
||||||
# => https://github.com/Michael-F-Bryan/mdbook-linkcheck/pull/82#issuecomment-2241058491
|
|
||||||
git: https://github.com/Michael-F-Bryan/mdbook-linkcheck.git
|
|
||||||
rev: 8c783c5d754d83bcd50c28fb4174854b04ece990
|
|
||||||
- name: default nightly
|
|
||||||
run: rustup default nightly
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- if: runner.os == 'Linux'
|
|
||||||
uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with: { shared-key: "ubuntu" }
|
- name: Install mimetype
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
- uses: Swatinem/rust-cache@v2
|
run: sudo apt-get install libfile-mimeinfo-perl
|
||||||
if: runner.os != 'Linux'
|
|
||||||
- name: Check for binary blobs
|
- name: Check for binary blobs
|
||||||
if: runner.os == 'Linux'
|
if: runner.os == 'Linux'
|
||||||
run: ./scripts/check_for_blobs.sh
|
run: ./scripts/check_for_blobs.sh
|
||||||
- name: Build libafl debug
|
- name: Build libafl debug
|
||||||
run: cargo build -p libafl
|
run: cargo build -p libafl
|
||||||
- name: Test the book (Linux)
|
- name: Build the book
|
||||||
|
run: cd docs && mdbook build
|
||||||
|
- name: Test the book
|
||||||
# TODO: fix books test fail with updated windows-rs
|
# TODO: fix books test fail with updated windows-rs
|
||||||
if: runner.os == 'Linux'
|
if: runner.os != 'Windows'
|
||||||
run: cd docs && mdbook test -L ../target/debug/deps
|
run: cd docs && mdbook test -L ../target/debug/deps
|
||||||
- name: Test the book (MacOS)
|
|
||||||
if: runner.os == 'MacOS'
|
|
||||||
run: cd docs && mdbook test -L ../target/debug/deps $(python3-config --ldflags | cut -d ' ' -f1)
|
|
||||||
- name: Run tests
|
- name: Run tests
|
||||||
run: cargo test
|
run: cargo test
|
||||||
- name: Test libafl no_std
|
- name: Test libafl no_std
|
||||||
run: cd libafl && cargo test --no-default-features
|
run: cd libafl && cargo test --no-default-features
|
||||||
- name: Test libafl_bolts no_std no_alloc
|
|
||||||
run: cd libafl_bolts && cargo test --no-default-features
|
|
||||||
- name: Test libafl_targets no_std
|
- name: Test libafl_targets no_std
|
||||||
run: cd libafl_targets && cargo test --no-default-features
|
run: cd libafl_targets && cargo test --no-default-features
|
||||||
|
|
||||||
ubuntu-doc-build:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
# ---- doc check ----
|
|
||||||
- name: Build Docs
|
|
||||||
run: RUSTFLAGS="--cfg docsrs" cargo +nightly doc --all-features --no-deps
|
|
||||||
|
|
||||||
ubuntu-doc-test:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
# ---- doc check ----
|
|
||||||
- name: Test Docs
|
|
||||||
run: RUSTFLAGS="--cfg docsrs" cargo +nightly test --doc --all-features
|
|
||||||
|
|
||||||
ubuntu-miri:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
if: contains( github.event.pull_request.labels.*.name, 'pre-release')
|
|
||||||
steps:
|
|
||||||
- name: Add nightly clippy
|
|
||||||
run: rustup toolchain install nightly --component miri --allow-downgrade
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
# --- miri undefined behavior test --
|
|
||||||
- name: Run miri tests
|
|
||||||
run: RUST_BACKTRACE=1 MIRIFLAGS="-Zmiri-disable-isolation" cargo +nightly miri test
|
|
||||||
|
|
||||||
ubuntu:
|
ubuntu:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-22.04
|
||||||
steps:
|
steps:
|
||||||
- name: Remove Dotnet & Haskell
|
- name: Remove Dotnet & Haskell
|
||||||
run: rm -rf /usr/share/dotnet && rm -rf /opt/ghc
|
run: rm -rf /usr/share/dotnet && rm -rf /opt/ghc
|
||||||
- uses: actions/checkout@v4
|
- uses: actions-rs/toolchain@v1
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
- name: set mold linker as default linker
|
||||||
|
uses: rui314/setup-mold@v1
|
||||||
|
- name: Install and cache deps
|
||||||
|
uses: awalsh128/cache-apt-pkgs-action@v1.1.0
|
||||||
|
with:
|
||||||
|
packages: llvm llvm-dev clang ninja-build clang-format-13 shellcheck libgtk-3-dev gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libslirp-dev
|
||||||
|
- name: get clang version
|
||||||
|
run: command -v llvm-config && clang -v
|
||||||
|
- name: Install cargo-hack
|
||||||
|
run: curl -LsSf https://github.com/taiki-e/cargo-hack/releases/latest/download/cargo-hack-x86_64-unknown-linux-gnu.tar.gz | tar xzf - -C ~/.cargo/bin
|
||||||
|
- name: Add nightly rustfmt and clippy
|
||||||
|
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
|
||||||
|
- uses: actions/checkout@v3
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with: { shared-key: "ubuntu" }
|
|
||||||
|
# ---- format check ----
|
||||||
# pcguard edges and pcguard hitcounts are not compatible and we need to build them seperately
|
# pcguard edges and pcguard hitcounts are not compatible and we need to build them seperately
|
||||||
- name: Check pcguard edges
|
- name: Check pcguard edges
|
||||||
run: cargo check --features=sancov_pcguard_edges
|
run: cargo check --features=sancov_pcguard_edges
|
||||||
|
- name: Format
|
||||||
|
run: cargo fmt -- --check
|
||||||
|
- name: Run clang-format style check for C/C++ programs.
|
||||||
|
run: clang-format-13 -n -Werror --style=file $(find . -type f \( -name '*.cpp' -o -iname '*.hpp' -o -name '*.cc' -o -name '*.cxx' -o -name '*.cc' -o -name '*.h' \) | grep -v '/target/' | grep -v 'libpng-1\.6\.37' | grep -v 'stb_image\.h' | grep -v 'dlmalloc\.c' | grep -v 'QEMU-Nyx')
|
||||||
- name: run shellcheck
|
- name: run shellcheck
|
||||||
run: shellcheck ./scripts/*.sh
|
run: shellcheck ./scripts/*.sh
|
||||||
# ---- build normal and examples ----
|
- name: Run clippy
|
||||||
|
run: ./scripts/clippy.sh
|
||||||
|
|
||||||
|
# ---- doc check ----
|
||||||
|
- name: Build Docs
|
||||||
|
run: cargo doc
|
||||||
|
- name: Test Docs
|
||||||
|
run: cargo +nightly test --doc --all-features
|
||||||
|
|
||||||
|
# ---- 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-clippy:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- name: Remove Dotnet & Haskell
|
|
||||||
run: rm -rf /usr/share/dotnet && rm -rf /opt/ghc
|
|
||||||
- name: Add nightly clippy
|
|
||||||
run: rustup toolchain install nightly --component clippy --allow-downgrade && rustup default nightly
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with: { shared-key: "ubuntu" }
|
|
||||||
- name: Run clippy
|
|
||||||
run: LLVM_CONFIG=llvm-config-${{env.MAIN_LLVM_VERSION}} ./scripts/clippy.sh
|
|
||||||
# --- test embedding the libafl_libfuzzer_runtime library
|
|
||||||
# Fix me plz
|
|
||||||
# - name: Test Build libafl_libfuzzer with embed
|
|
||||||
# run: cargo +nightly test --features=embed-runtime --manifest-path libafl_libfuzzer/Cargo.toml
|
|
||||||
|
|
||||||
ubuntu-check:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
needs: ubuntu
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
instance_idx: [ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17" ]
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with: { shared-key: "ubuntu" }
|
|
||||||
# ---- 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
|
||||||
# `sancov_pcguard_edges` is tested seperatelyc
|
# `agpl`, `nautilus` require nightly
|
||||||
run: python3 ./scripts/parallellize_cargo_check.py ${{ matrix.instance_idx }}
|
# `sancov_pcguard_edges` is tested seperately
|
||||||
|
run: cargo hack check --each-feature --clean-per-run --exclude-features=prelude,agpl,nautilus,python,sancov_pcguard_edges,arm,aarch64,i386,be,systemmode --no-dev-deps
|
||||||
|
- name: Check nightly features
|
||||||
|
run: cargo +nightly check --features=agpl && cargo +nightly check --features=nautilus
|
||||||
|
- name: Build examples
|
||||||
|
run: cargo build --examples --verbose
|
||||||
|
|
||||||
ubuntu-concolic:
|
ubuntu-concolic:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
needs: ubuntu
|
|
||||||
steps:
|
steps:
|
||||||
- name: Install curl
|
- uses: actions-rs/toolchain@v1
|
||||||
run: sudo apt-get update && sudo apt-get install clang
|
with:
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
profile: minimal
|
||||||
- uses: actions/checkout@v4
|
toolchain: stable
|
||||||
|
- uses: actions/checkout@v3
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with: { shared-key: "ubuntu" }
|
|
||||||
- name: Install smoke test deps
|
- name: Install smoke test deps
|
||||||
run: sudo ./libafl_concolic/test/smoke_test_ubuntu_deps.sh
|
run: sudo ./libafl_concolic/test/smoke_test_ubuntu_deps.sh
|
||||||
- name: Run smoke test
|
- name: Run smoke test
|
||||||
run: ./libafl_concolic/test/smoke_test.sh
|
run: ./libafl_concolic/test/smoke_test.sh
|
||||||
|
|
||||||
python-bindings:
|
bindings:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
|
- uses: actions-rs/toolchain@v1
|
||||||
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
- name: set mold linker as default linker
|
||||||
|
uses: rui314/setup-mold@v1
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: sudo apt-get update && sudo apt-get install -y lsb-release wget software-properties-common gnupg ninja-build python3-dev python3-pip python3-venv libz3-dev
|
run: sudo apt-get install -y llvm llvm-dev clang ninja-build python3-dev python3-pip python3-venv
|
||||||
- name: Install maturin
|
- name: Install maturin
|
||||||
run: cargo install --locked maturin
|
run: python3 -m pip install maturin
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
with: { shared-key: "ubuntu" }
|
|
||||||
- name: Run a maturin build
|
- name: Run a maturin build
|
||||||
run: export LLVM_CONFIG=llvm-config-${{env.MAIN_LLVM_VERSION}} && cd ./bindings/pylibafl && python3 -m venv .env && . .env/bin/activate && pip install --upgrade --force-reinstall . && ./test.sh
|
run: cd ./bindings/pylibafl && maturin build
|
||||||
- name: Run python test
|
|
||||||
run: . ./bindings/pylibafl/.env/bin/activate # && cd ./fuzzers/binary_only/python_qemu/ && python3 fuzzer.py 2>&1 | grep "Bye"
|
|
||||||
|
|
||||||
cargo-fmt:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
env:
|
|
||||||
MAIN_LLVM_VERSION: 19
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- name: Add rustfmt nightly
|
|
||||||
shell: bash
|
|
||||||
run: rustup component add --toolchain nightly-x86_64-unknown-linux-gnu rustfmt
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with: { shared-key: "ubuntu" }
|
|
||||||
- name: Installing black
|
|
||||||
run: python3 -m pip install black
|
|
||||||
- name: Format Check
|
|
||||||
run: ./scripts/fmt_all.sh check
|
|
||||||
|
|
||||||
check-md-links:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Install linkspector
|
|
||||||
shell: bash
|
|
||||||
run: sudo apt-get update && sudo apt-get install -y npm && npm install -g @umbrelladocs/linkspector
|
|
||||||
- name: Run linkspector
|
|
||||||
shell: bash
|
|
||||||
run: ./scripts/check_md_links.sh
|
|
||||||
# TODO: Use github action once it's fixed (https://github.com/UmbrellaDocs/action-linkspector/issues/20)
|
|
||||||
# - name: Run linkspector
|
|
||||||
# uses: umbrelladocs/action-linkspector@v1
|
|
||||||
# with:
|
|
||||||
# fail_on_error: 'true'
|
|
||||||
# config_file: '.github/.linkspector.yml'
|
|
||||||
|
|
||||||
msrv:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: taiki-e/install-action@cargo-hack
|
|
||||||
# Note: We currently only specify minimum rust versions for the default workspace members
|
|
||||||
- run: cargo hack check --rust-version -p libafl -p libafl_bolts -p libafl_derive -p libafl_cc -p libafl_targets
|
|
||||||
|
|
||||||
fuzzers-preflight:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Fuzzer in CI Check
|
|
||||||
run: ./scripts/check_tested_fuzzers.sh
|
|
||||||
|
|
||||||
fuzzers:
|
fuzzers:
|
||||||
needs:
|
|
||||||
- fuzzers-preflight
|
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
|
||||||
matrix:
|
matrix:
|
||||||
os: [ ubuntu-24.04 ]
|
os: [ubuntu-latest, macos-latest]
|
||||||
fuzzer:
|
|
||||||
# Baby
|
|
||||||
- ./fuzzers/baby/baby_fuzzer_swap_differential
|
|
||||||
- ./fuzzers/baby/tutorial
|
|
||||||
- ./fuzzers/baby/baby_fuzzer
|
|
||||||
# - ./fuzzers/baby/backtrace_baby_fuzzers
|
|
||||||
- ./fuzzers/baby/baby_fuzzer_unicode
|
|
||||||
- ./fuzzers/baby/baby_fuzzer_minimizing
|
|
||||||
- ./fuzzers/baby/backtrace_baby_fuzzers/c_code_with_fork_executor
|
|
||||||
- ./fuzzers/baby/backtrace_baby_fuzzers/c_code_with_inprocess_executor
|
|
||||||
- ./fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_fork_executor
|
|
||||||
- ./fuzzers/baby/backtrace_baby_fuzzers/rust_code_with_inprocess_executor
|
|
||||||
- ./fuzzers/baby/backtrace_baby_fuzzers/command_executor
|
|
||||||
- ./fuzzers/baby/backtrace_baby_fuzzers/forkserver_executor
|
|
||||||
- ./fuzzers/baby/baby_fuzzer_custom_executor
|
|
||||||
|
|
||||||
# Binary-only
|
|
||||||
- ./fuzzers/binary_only/fuzzbench_fork_qemu
|
|
||||||
- ./fuzzers/binary_only/frida_executable_libpng
|
|
||||||
- ./fuzzers/binary_only/frida_windows_gdiplus
|
|
||||||
- ./fuzzers/binary_only/frida_libpng
|
|
||||||
- ./fuzzers/binary_only/fuzzbench_qemu
|
|
||||||
- ./fuzzers/binary_only/intel_pt_baby_fuzzer
|
|
||||||
- ./fuzzers/binary_only/intel_pt_command_executor
|
|
||||||
- ./fuzzers/binary_only/tinyinst_simple
|
|
||||||
|
|
||||||
# Forkserver
|
|
||||||
- ./fuzzers/forkserver/forkserver_simple
|
|
||||||
- ./fuzzers/forkserver/forkserver_libafl_cc
|
|
||||||
- ./fuzzers/forkserver/fuzzbench_forkserver
|
|
||||||
- ./fuzzers/forkserver/fuzzbench_forkserver_cmplog
|
|
||||||
- ./fuzzers/forkserver/libafl-fuzz
|
|
||||||
- ./fuzzers/forkserver/baby_fuzzer_with_forkexecutor
|
|
||||||
|
|
||||||
# Full-system
|
|
||||||
- ./fuzzers/full_system/nyx_libxml2_standalone
|
|
||||||
- ./fuzzers/full_system/nyx_libxml2_parallel
|
|
||||||
|
|
||||||
# Structure-aware
|
|
||||||
- ./fuzzers/structure_aware/nautilus_sync
|
|
||||||
- ./fuzzers/structure_aware/baby_fuzzer_grimoire
|
|
||||||
- ./fuzzers/structure_aware/baby_fuzzer_gramatron
|
|
||||||
- ./fuzzers/structure_aware/baby_fuzzer_tokens
|
|
||||||
- ./fuzzers/structure_aware/baby_fuzzer_multi
|
|
||||||
- ./fuzzers/structure_aware/baby_fuzzer_custom_input
|
|
||||||
- ./fuzzers/structure_aware/baby_fuzzer_nautilus
|
|
||||||
- ./fuzzers/structure_aware/forkserver_simple_nautilus
|
|
||||||
|
|
||||||
# In-process
|
|
||||||
- ./fuzzers/fuzz_anything/cargo_fuzz
|
|
||||||
# - ./fuzzers/inprocess/dynamic_analysis
|
|
||||||
- ./fuzzers/inprocess/fuzzbench
|
|
||||||
- ./fuzzers/inprocess/fuzzbench_text
|
|
||||||
- ./fuzzers/inprocess/fuzzbench_ctx
|
|
||||||
- ./fuzzers/inprocess/libfuzzer_libmozjpeg
|
|
||||||
- ./fuzzers/inprocess/libfuzzer_libpng
|
|
||||||
- ./fuzzers/inprocess/libfuzzer_libpng_launcher
|
|
||||||
- ./fuzzers/inprocess/libfuzzer_libpng_accounting
|
|
||||||
- ./fuzzers/inprocess/libfuzzer_libpng_centralized
|
|
||||||
- ./fuzzers/inprocess/libfuzzer_libpng_cmin
|
|
||||||
- ./fuzzers/inprocess/libfuzzer_libpng_norestart
|
|
||||||
# - ./fuzzers/inprocess/libfuzzer_libpng_tcp_manager
|
|
||||||
- ./fuzzers/inprocess/libfuzzer_stb_image_sugar
|
|
||||||
- ./fuzzers/inprocess/libfuzzer_stb_image
|
|
||||||
# - ./fuzzers/structure_aware/libfuzzer_stb_image_concolic
|
|
||||||
# - ./fuzzers/inprocess/libfuzzer_windows_asan
|
|
||||||
# - ./fuzzers/inprocess/sqlite_centralized_multi_machine
|
|
||||||
|
|
||||||
# Fuzz Anything
|
|
||||||
- ./fuzzers/fuzz_anything/push_harness
|
|
||||||
- ./fuzzers/fuzz_anything/push_stage_harness
|
|
||||||
- ./fuzzers/fuzz_anything/libafl_atheris
|
|
||||||
- ./fuzzers/fuzz_anything/baby_no_std
|
|
||||||
- ./fuzzers/fuzz_anything/baby_fuzzer_wasm
|
|
||||||
|
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions-rs/toolchain@v1
|
||||||
- uses: ./.github/workflows/fuzzer-tester-prepare
|
with:
|
||||||
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
- name: set mold linker as default linker
|
||||||
|
if: runner.os == 'Linux' # mold only support linux until now
|
||||||
|
uses: rui314/setup-mold@v1
|
||||||
|
- name: Add nightly rustfmt and clippy
|
||||||
|
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
|
||||||
|
- name: Add no_std toolchain
|
||||||
|
run: rustup toolchain install nightly-x86_64-unknown-linux-gnu ; rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
|
||||||
|
- name: Install python
|
||||||
|
if: runner.os == 'macOS'
|
||||||
|
run: brew install --force-bottle --overwrite python@3.11
|
||||||
|
- uses: lyricwulf/abc@v1
|
||||||
|
with:
|
||||||
|
# todo: remove afl++-clang when nyx support samcov_pcguard
|
||||||
|
linux: llvm llvm-dev clang nasm ninja-build gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libgtk-3-dev afl++-clang pax-utils
|
||||||
|
# update bash for macos to support `declare -A` command`
|
||||||
|
macos: llvm libpng nasm coreutils z3 bash
|
||||||
|
- name: pip install
|
||||||
|
run: python3 -m pip install msgpack jinja2
|
||||||
|
# Note that nproc needs to have coreutils installed on macOS, so the order of CI commands matters.
|
||||||
|
- name: enable mult-thread for `make`
|
||||||
|
run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)"
|
||||||
|
- name: install cargo-make
|
||||||
|
uses: baptiste0928/cargo-install@v1.3.0
|
||||||
|
with:
|
||||||
|
crate: cargo-make
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
with:
|
||||||
|
submodules: true # recursively checkout submodules
|
||||||
|
- 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'
|
||||||
shell: bash
|
run: ./scripts/test_all_fuzzers.sh
|
||||||
run: RUN_ON_CI=1 LLVM_CONFIG=llvm-config-${{env.MAIN_LLVM_VERSION}} ./scripts/test_fuzzer.sh ${{ matrix.fuzzer }}
|
- name: Build and run example fuzzers (macOS)
|
||||||
|
if: runner.os == 'macOS' # use bash v4
|
||||||
changes:
|
run: /usr/local/bin/bash ./scripts/test_all_fuzzers.sh
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
permissions:
|
|
||||||
pull-requests: read
|
|
||||||
outputs:
|
|
||||||
qemu: ${{ steps.filter.outputs.qemu }}
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: dorny/paths-filter@v3
|
|
||||||
id: filter
|
|
||||||
with:
|
|
||||||
filters: |
|
|
||||||
qemu:
|
|
||||||
- '.github/**'
|
|
||||||
- 'libafl/**'
|
|
||||||
- 'libafl_bolts/**'
|
|
||||||
- 'libafl_targets/**'
|
|
||||||
- 'libafl_qemu/**'
|
|
||||||
- 'fuzzers/*qemu*/**'
|
|
||||||
|
|
||||||
fuzzers-qemu:
|
|
||||||
needs:
|
|
||||||
- changes
|
|
||||||
if: ${{ needs.changes.outputs.qemu == 'true' }}
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
os: [ubuntu-24.04]
|
|
||||||
fuzzer:
|
|
||||||
# Binary only
|
|
||||||
- ./fuzzers/binary_only/qemu_cmin
|
|
||||||
- ./fuzzers/binary_only/qemu_coverage
|
|
||||||
- ./fuzzers/binary_only/qemu_launcher
|
|
||||||
|
|
||||||
# Full-system
|
|
||||||
- ./fuzzers/full_system/qemu_baremetal
|
|
||||||
# - ./fuzzers/full_system/qemu_linux_kernel
|
|
||||||
#- ./fuzzers/full_system/qemu_linux_process
|
|
||||||
|
|
||||||
runs-on: [ self-hosted, qemu ]
|
|
||||||
container: registry.gitlab.com/qemu-project/qemu/qemu/ubuntu2204:latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/qemu-fuzzer-tester-prepare
|
|
||||||
- name: Build and run example QEMU fuzzers (Linux)
|
|
||||||
if: runner.os == 'Linux'
|
|
||||||
shell: bash
|
|
||||||
run: RUN_ON_CI=1 LLVM_CONFIG=llvm-config-${{env.MAIN_LLVM_VERSION}} ./scripts/test_fuzzer.sh ${{ matrix.fuzzer }}
|
|
||||||
|
|
||||||
nostd-build:
|
nostd-build:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
components: rust-src
|
profile: minimal
|
||||||
|
toolchain: nightly
|
||||||
|
override: true
|
||||||
|
components: rustfmt, clippy, rust-src
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Add targets
|
- name: Add targets
|
||||||
run: rustup target add arm-linux-androideabi && rustup target add thumbv6m-none-eabi
|
run: rustup target add arm-linux-androideabi && rustup target add thumbv6m-none-eabi
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Build aarch64-unknown-none
|
- name: Build aarch64-unknown-none
|
||||||
run: cd ./fuzzers/fuzz_anything/baby_no_std && cargo +nightly build -Zbuild-std=core,alloc --target aarch64-unknown-none -v --release && cd ../..
|
run: cd ./fuzzers/baby_no_std && cargo +nightly build -Zbuild-std=core,alloc --target aarch64-unknown-none -v --release && cd ../..
|
||||||
- name: run x86_64 until panic!
|
- name: run x86_64 until panic!
|
||||||
run: cd ./fuzzers/fuzz_anything/baby_no_std && cargo +nightly run || test $? -ne 0 || exit 1
|
run: cd ./fuzzers/baby_no_std && cargo +nightly run || test $? -ne 0 || exit 1
|
||||||
- name: no_std tests
|
- name: no_std tests
|
||||||
run: cd ./libafl && cargo test --no-default-features
|
run: cd ./libafl && cargo test --no-default-features
|
||||||
|
|
||||||
nostd-clippy:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
|
||||||
with:
|
|
||||||
components: clippy, rust-src
|
|
||||||
- name: Add targets
|
|
||||||
run: rustup target add arm-linux-androideabi && rustup target add thumbv6m-none-eabi
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: libafl armv6m-none-eabi (32 bit no_std) clippy
|
- name: libafl armv6m-none-eabi (32 bit no_std) clippy
|
||||||
run: cd ./libafl && cargo clippy --target thumbv6m-none-eabi --no-default-features
|
run: cd ./libafl && cargo clippy --target thumbv6m-none-eabi --no-default-features
|
||||||
- name: Build no_std no_alloc bolts
|
|
||||||
run: cd ./libafl_bolts && cargo +nightly build -Zbuild-std=core --target aarch64-unknown-none --no-default-features -v --release && cd ../
|
|
||||||
|
|
||||||
format-toml:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- name: Install taplo
|
|
||||||
run: curl -fsSL https://github.com/tamasfe/taplo/releases/latest/download/taplo-full-linux-x86_64.gz | gzip -d - | install -m 755 /dev/stdin /usr/local/bin/taplo
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Run taplo
|
|
||||||
run: taplo format --check
|
|
||||||
|
|
||||||
build-docker:
|
build-docker:
|
||||||
runs-on: ubuntu-24.04
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- name: Build docker
|
- name: Build docker
|
||||||
run: docker build -t libafl .
|
run: docker build -t libafl .
|
||||||
|
|
||||||
windows-frida-libpng:
|
windows:
|
||||||
runs-on: windows-latest
|
runs-on: windows-latest
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions-rs/toolchain@v1
|
||||||
- uses: ./.github/workflows/windows-tester-prepare
|
with:
|
||||||
- name: Build fuzzers/binary_only/frida_libpng
|
profile: minimal
|
||||||
run: cd fuzzers/binary_only/frida_libpng/ && cargo make test
|
toolchain: stable
|
||||||
|
- uses: actions/checkout@v3
|
||||||
windows-frida-libfuzzer-stb-image:
|
|
||||||
runs-on: windows-latest
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/windows-tester-prepare
|
|
||||||
- name: Build fuzzers/inprocess/libfuzzer_stb_image
|
|
||||||
run: cd fuzzers/inprocess/libfuzzer_stb_image && cargo build --release
|
|
||||||
|
|
||||||
windows-frida-gdiplus:
|
|
||||||
runs-on: windows-latest
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/windows-tester-prepare
|
|
||||||
- name: Build fuzzers/binary_only/frida_windows_gdiplus
|
|
||||||
run: cd fuzzers/binary_only/frida_windows_gdiplus/ && cargo make test && cargo make test_cmplog
|
|
||||||
|
|
||||||
windows-tinyinst-simple:
|
|
||||||
runs-on: windows-latest
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- name: install cxx bridge
|
|
||||||
run: cargo install cxxbridge-cmd
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/windows-tester-prepare
|
|
||||||
- name: Build fuzzers/binary_only/tinyinst_simple
|
|
||||||
run: cd fuzzers/binary_only/tinyinst_simple/ && cargo make test
|
|
||||||
|
|
||||||
windows-clippy:
|
|
||||||
runs-on: windows-latest
|
|
||||||
needs:
|
|
||||||
- common
|
|
||||||
steps:
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: ./.github/workflows/windows-tester-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: Run real clippy, not the fake one
|
- name: Windows Build
|
||||||
shell: pwsh
|
run: cargo build --verbose
|
||||||
run: .\scripts\clippy.ps1
|
- name: Run clippy
|
||||||
|
uses: actions-rs/cargo@v1
|
||||||
|
with:
|
||||||
|
command: clippy
|
||||||
|
- name: Build docs
|
||||||
|
run: cargo doc
|
||||||
|
- name: Set LIBCLANG_PATH
|
||||||
|
run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
|
||||||
|
- name: install cargo-make
|
||||||
|
run: cargo install --force cargo-make
|
||||||
|
- uses: ilammy/msvc-dev-cmd@v1
|
||||||
|
- name: Build fuzzers/frida_libpng
|
||||||
|
run: cd fuzzers/frida_libpng/ && cargo make test
|
||||||
|
- name: Build fuzzers/frida_gdiplus
|
||||||
|
run: cd fuzzers/frida_gdiplus/ && cargo make test
|
||||||
|
|
||||||
macos:
|
macos:
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: actions-rs/toolchain@v1
|
||||||
- name: Add nightly clippy
|
with:
|
||||||
run: rustup toolchain install nightly --component clippy --allow-downgrade && rustup default nightly
|
profile: minimal
|
||||||
|
toolchain: stable
|
||||||
|
- name: Add nightly rustfmt and clippy
|
||||||
|
run: rustup toolchain install nightly --component rustfmt --component clippy --allow-downgrade
|
||||||
- name: Install deps
|
- name: Install deps
|
||||||
run: brew install z3 gtk+3 python
|
run: brew install z3 gtk+3
|
||||||
- name: Install cxxbridge
|
- uses: actions/checkout@v3
|
||||||
run: cargo install cxxbridge-cmd
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
- name: MacOS Build
|
- name: MacOS Build
|
||||||
run: cargo build --verbose
|
run: cargo build --verbose
|
||||||
|
- name: Run clippy
|
||||||
|
run: ./scripts/clippy.sh
|
||||||
- name: Increase map sizes
|
- name: Increase map sizes
|
||||||
run: ./scripts/shmem_limits_macos.sh
|
run: ./scripts/shmem_limits_macos.sh
|
||||||
- name: Run Tests
|
- name: Run Tests
|
||||||
run: cargo test
|
run: cargo test
|
||||||
- name: Clippy
|
|
||||||
run: cargo +nightly clippy --tests --all --exclude libafl_nyx --exclude symcc_runtime --exclude runtime_test
|
|
||||||
|
|
||||||
ios:
|
other_targets:
|
||||||
runs-on: macOS-latest
|
runs-on: macOS-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
- uses: actions-rs/toolchain@v1
|
||||||
- name: install ios
|
with:
|
||||||
run: rustup target add aarch64-apple-ios
|
profile: minimal
|
||||||
- uses: actions/checkout@v4
|
toolchain: stable
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Build iOS
|
|
||||||
run: PYO3_CROSS_PYTHON_VERSION=$(python3 -c "print('{}.{}'.format(__import__('sys').version_info.major, __import__('sys').version_info.minor))") cargo build --target aarch64-apple-ios && cd libafl_frida && cargo build --target aarch64-apple-ios && cd ..
|
|
||||||
|
|
||||||
android:
|
|
||||||
runs-on: ubuntu-24.04
|
|
||||||
steps:
|
|
||||||
- name: Install curl
|
|
||||||
run: sudo apt-get update && sudo apt-get install clang
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
|
||||||
- uses: nttld/setup-ndk@v1
|
- uses: nttld/setup-ndk@v1
|
||||||
with:
|
with:
|
||||||
ndk-version: r25b
|
ndk-version: r21e
|
||||||
|
- name: install ios
|
||||||
|
run: rustup target add aarch64-apple-ios
|
||||||
- name: install android
|
- name: install android
|
||||||
run: rustup target add aarch64-linux-android
|
run: rustup target add aarch64-linux-android
|
||||||
- name: install cargo ndk
|
- name: install cargo ndk
|
||||||
run: cargo install cargo-ndk
|
run: cargo install cargo-ndk
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v3
|
||||||
- uses: Swatinem/rust-cache@v2
|
- uses: Swatinem/rust-cache@v2
|
||||||
|
- name: Build iOS
|
||||||
|
run: cargo build --target aarch64-apple-ios
|
||||||
- name: Build Android
|
- name: Build Android
|
||||||
run: cd libafl && PYO3_CROSS_PYTHON_VERSION=$(python3 -c "print('{}.{}'.format(__import__('sys').version_info.major, __import__('sys').version_info.minor))") cargo ndk -t arm64-v8a build --release
|
run: cargo ndk -t arm64-v8a build --release
|
||||||
|
|
||||||
#run: cargo build --target aarch64-linux-android
|
#run: cargo build --target aarch64-linux-android
|
||||||
# TODO: Figure out how to properly build stuff with clang
|
# TODO: Figure out how to properly build stuff with clang
|
||||||
#- name: Add clang path to $PATH env
|
#- name: Add clang path to $PATH env
|
||||||
@ -541,3 +296,36 @@ jobs:
|
|||||||
# run: clang -v
|
# run: clang -v
|
||||||
#- name: Windows Test
|
#- name: Windows Test
|
||||||
# run: C:\Rust\.cargo\bin\cargo.exe test --verbose
|
# run: C:\Rust\.cargo\bin\cargo.exe test --verbose
|
||||||
|
|
||||||
|
freebsd:
|
||||||
|
runs-on: macos-12
|
||||||
|
name: Simple build in FreeBSD
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v3
|
||||||
|
- name: Test in FreeBSD
|
||||||
|
id: test
|
||||||
|
uses: vmactions/freebsd-vm@v0
|
||||||
|
with:
|
||||||
|
usesh: true
|
||||||
|
sync: rsync
|
||||||
|
copyback: false
|
||||||
|
mem: 2048
|
||||||
|
release: 13.1
|
||||||
|
prepare: |
|
||||||
|
pkg install -y curl bash sudo llvm14
|
||||||
|
curl https://sh.rustup.rs -sSf | sh -s -- -y
|
||||||
|
|
||||||
|
run: |
|
||||||
|
freebsd-version
|
||||||
|
. "$HOME/.cargo/env"
|
||||||
|
rustup toolchain install nightly
|
||||||
|
export LLVM_CONFIG=/usr/local/bin/llvm-config14
|
||||||
|
pwd
|
||||||
|
ls -lah
|
||||||
|
echo "local/bin"
|
||||||
|
ls -lah /usr/local/bin/
|
||||||
|
which llvm-config
|
||||||
|
chmod +x ./scripts/clippy.sh
|
||||||
|
bash ./scripts/shmem_limits_fbsd.sh
|
||||||
|
bash ./scripts/clippy.sh
|
||||||
|
cargo test
|
||||||
|
@ -1,40 +0,0 @@
|
|||||||
name: Setup Rust Environment
|
|
||||||
description: Sets up the Rust environment for the CI workflow
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
fetch-depth: 0
|
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with: { shared-key: "${{ runner.os }}-shared-fuzzer-cache" }
|
|
||||||
- name: Install fuzzers deps
|
|
||||||
shell: bash
|
|
||||||
run: sudo apt-get update && sudo apt-get install -y nasm nlohmann-json3-dev gcc-aarch64-linux-gnu g++-aarch64-linux-gnu gcc-mipsel-linux-gnu g++-mipsel-linux-gnu gcc-powerpc-linux-gnu g++-powerpc-linux-gnu libc6-dev-i386-cross libc6-dev libc6-dev-i386 lib32gcc-11-dev lib32stdc++-11-dev libgtk-3-dev pax-utils python3-msgpack python3-jinja2
|
|
||||||
- name: enable mult-thread for `make`
|
|
||||||
shell: bash
|
|
||||||
run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)"
|
|
||||||
- name: Add no_std toolchain
|
|
||||||
shell: bash
|
|
||||||
run: rustup toolchain install nightly-x86_64-unknown-linux-gnu ; rustup component add rust-src --toolchain nightly-x86_64-unknown-linux-gnu
|
|
||||||
- name: Add wasm target
|
|
||||||
shell: bash
|
|
||||||
run: rustup target add wasm32-unknown-unknown
|
|
||||||
- name: install cargo-make
|
|
||||||
uses: baptiste0928/cargo-install@v3
|
|
||||||
with:
|
|
||||||
crate: cargo-make
|
|
||||||
- name: install wasm-pack
|
|
||||||
uses: baptiste0928/cargo-install@v3
|
|
||||||
with:
|
|
||||||
crate: wasm-pack
|
|
||||||
- name: install cxxbridge-cmd
|
|
||||||
uses: baptiste0928/cargo-install@v3
|
|
||||||
with:
|
|
||||||
crate: cxxbridge-cmd
|
|
||||||
- name: install chrome
|
|
||||||
uses: browser-actions/setup-chrome@v1
|
|
||||||
with:
|
|
||||||
chrome-version: stable
|
|
@ -1,23 +0,0 @@
|
|||||||
name: Setup QEMU Fuzzers environment
|
|
||||||
description: Sets up the QEMU fuzzers environment
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- name: Install QEMU deps
|
|
||||||
shell: bash
|
|
||||||
run: apt-get update && apt-get install -y qemu-utils sudo python3-msgpack python3-jinja2 curl python3-dev
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
|
||||||
- name: enable mult-thread for `make`
|
|
||||||
shell: bash
|
|
||||||
run: export MAKEFLAGS="-j$(expr $(nproc) \+ 1)"
|
|
||||||
- name: install cargo-make
|
|
||||||
uses: baptiste0928/cargo-install@v3
|
|
||||||
with:
|
|
||||||
crate: cargo-make
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
submodules: true
|
|
||||||
fetch-depth: 0
|
|
||||||
- uses: ./.github/workflows/ubuntu-prepare
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
with: { shared-key: "${{ runner.os }}-shared-fuzzer-cache" }
|
|
36
.github/workflows/ubuntu-prepare/action.yml
vendored
36
.github/workflows/ubuntu-prepare/action.yml
vendored
@ -1,36 +0,0 @@
|
|||||||
name: Setup Rust Environment
|
|
||||||
description: Sets up the Rust environment for the CI workflow
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- name: Install and cache deps
|
|
||||||
shell: bash
|
|
||||||
run: sudo apt-get update && sudo apt-get install -y curl lsb-release wget software-properties-common gnupg ninja-build shellcheck pax-utils nasm libsqlite3-dev libc6-dev libgtk-3-dev gcc g++ gcc-arm-none-eabi gcc-arm-linux-gnueabi g++-arm-linux-gnueabi libslirp-dev libz3-dev build-essential cmake
|
|
||||||
- uses: dtolnay/rust-toolchain@stable
|
|
||||||
- name: Add stable clippy
|
|
||||||
shell: bash
|
|
||||||
run: rustup toolchain install stable --component clippy --allow-downgrade
|
|
||||||
- name: Add nightly clippy
|
|
||||||
shell: bash
|
|
||||||
run: rustup toolchain install nightly --component clippy --allow-downgrade
|
|
||||||
- name: Remove existing clang and LLVM
|
|
||||||
shell: bash
|
|
||||||
run: sudo apt-get purge -y *llvm* *clang* lld* lldb* opt*
|
|
||||||
- name: Install cargo-hack
|
|
||||||
shell: bash
|
|
||||||
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
|
|
||||||
shell: bash
|
|
||||||
run: rustup toolchain install nightly --allow-downgrade
|
|
||||||
- name: Default to nightly
|
|
||||||
shell: bash
|
|
||||||
run: rustup default nightly
|
|
||||||
- name: Add LLVM in sources list
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
wget https://apt.llvm.org/llvm.sh
|
|
||||||
chmod +x llvm.sh
|
|
||||||
sudo ./llvm.sh ${{env.MAIN_LLVM_VERSION}} all
|
|
||||||
- name: Symlink Headers
|
|
||||||
shell: bash
|
|
||||||
run: sudo ln -s /usr/include/asm-generic /usr/include/asm
|
|
@ -1,20 +0,0 @@
|
|||||||
name: Setup Rust Environment
|
|
||||||
description: Sets up the Rust environment for the CI workflow
|
|
||||||
runs:
|
|
||||||
using: composite
|
|
||||||
steps:
|
|
||||||
- uses: dtolnay/rust-toolchain@nightly
|
|
||||||
with:
|
|
||||||
components: llvm-tools, clippy, rustfmt
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- uses: Swatinem/rust-cache@v2
|
|
||||||
- name: Build docs
|
|
||||||
shell: pwsh
|
|
||||||
run: cargo doc
|
|
||||||
- uses: ilammy/msvc-dev-cmd@v1
|
|
||||||
- name: Set LIBCLANG_PATH
|
|
||||||
shell: pwsh
|
|
||||||
run: echo "LIBCLANG_PATH=$((gcm clang).source -replace "clang.exe")" >> $env:GITHUB_ENV
|
|
||||||
- name: install cargo-make
|
|
||||||
shell: pwsh
|
|
||||||
run: cargo install --force cargo-make
|
|
27
.gitignore
vendored
27
.gitignore
vendored
@ -6,9 +6,7 @@ vendor
|
|||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.env
|
.env
|
||||||
.vscode
|
|
||||||
|
|
||||||
*.test
|
|
||||||
*.tmp
|
*.tmp
|
||||||
*.swp
|
*.swp
|
||||||
*.o
|
*.o
|
||||||
@ -19,13 +17,10 @@ vendor
|
|||||||
*.bin
|
*.bin
|
||||||
*.dll
|
*.dll
|
||||||
*.exe
|
*.exe
|
||||||
*.dylib
|
|
||||||
*.dSYM
|
*.dSYM
|
||||||
*.obj
|
*.obj
|
||||||
|
|
||||||
.cur_input
|
.cur_input
|
||||||
.cur_input_*
|
|
||||||
cur_input
|
|
||||||
.venv
|
.venv
|
||||||
|
|
||||||
crashes
|
crashes
|
||||||
@ -34,15 +29,15 @@ callgrind.out.*
|
|||||||
perf.data
|
perf.data
|
||||||
perf.data.old
|
perf.data.old
|
||||||
|
|
||||||
.vscode/settings.json
|
.vscode
|
||||||
test.dict
|
test.dict
|
||||||
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# Ignore all built fuzzers
|
# Ignore all built fuzzers
|
||||||
|
fuzzer_*
|
||||||
AFLplusplus
|
AFLplusplus
|
||||||
test_*
|
test_*
|
||||||
*_fuzzer
|
*_fuzzer
|
||||||
|
*_harness
|
||||||
|
|
||||||
# Ignore common dummy and logfiles
|
# Ignore common dummy and logfiles
|
||||||
*.log
|
*.log
|
||||||
@ -51,7 +46,6 @@ a
|
|||||||
forkserver_test
|
forkserver_test
|
||||||
__pycache__
|
__pycache__
|
||||||
*.lafl_lock
|
*.lafl_lock
|
||||||
*.metadata
|
|
||||||
|
|
||||||
*atomic_file_testfile*
|
*atomic_file_testfile*
|
||||||
**/libxml2
|
**/libxml2
|
||||||
@ -60,18 +54,3 @@ __pycache__
|
|||||||
|
|
||||||
libafl_nyx/QEMU-Nyx
|
libafl_nyx/QEMU-Nyx
|
||||||
libafl_nyx/packer
|
libafl_nyx/packer
|
||||||
|
|
||||||
.z3-trace
|
|
||||||
|
|
||||||
# No gdb history
|
|
||||||
.gdb_history
|
|
||||||
# No llvm IR
|
|
||||||
*.ll
|
|
||||||
|
|
||||||
*.tar.gz
|
|
||||||
|
|
||||||
# common harness names
|
|
||||||
harness
|
|
||||||
program
|
|
||||||
fuzzer_libpng*
|
|
||||||
forkserver_simple
|
|
||||||
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "libafl_concolic/symcc_runtime/symcc"]
|
||||||
|
path = libafl_concolic/symcc_runtime/symcc
|
||||||
|
url = https://github.com/AFLplusplus/symcc.git
|
@ -1,13 +0,0 @@
|
|||||||
# See https://pre-commit.com for more information
|
|
||||||
# See https://pre-commit.com/hooks.html for more hooks
|
|
||||||
repos:
|
|
||||||
- repo: local
|
|
||||||
hooks:
|
|
||||||
- id: fmt
|
|
||||||
name: fmt
|
|
||||||
entry: scripts/fmt_all.sh check
|
|
||||||
language: script
|
|
||||||
- repo: https://github.com/ComPWA/taplo-pre-commit
|
|
||||||
rev: v0.9.3
|
|
||||||
hooks:
|
|
||||||
- id: taplo-format
|
|
12
.vscode/settings.json.default
vendored
12
.vscode/settings.json.default
vendored
@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"rust-analyzer.cargo.buildScripts.overrideCommand": [
|
|
||||||
"cargo",
|
|
||||||
"check",
|
|
||||||
"--message-format=json",
|
|
||||||
],
|
|
||||||
"rust-analyzer.check.overrideCommand": [
|
|
||||||
"cargo",
|
|
||||||
"check",
|
|
||||||
"--message-format=json",
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
# How to Contribute to LibAFL
|
|
||||||
|
|
||||||
For bugs, feel free to open issues or contact us directly. Thank you for your support. <3
|
|
||||||
|
|
||||||
## Pull Request guideline
|
|
||||||
|
|
||||||
Even though we will gladly assist you in finishing up your PR, try to:
|
|
||||||
|
|
||||||
- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26))
|
|
||||||
- run `cargo +nightly fmt` on your code before pushing
|
|
||||||
- check the output of `cargo clippy --all` or `./scripts/clippy.sh` (On windows use `.\scripts\clippy.ps1`)
|
|
||||||
- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code.
|
|
||||||
- Please add and describe your changes to MIGRATION.md if you change the APIs.
|
|
||||||
|
|
||||||
Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help.
|
|
||||||
|
|
||||||
### Pre-commit hooks
|
|
||||||
|
|
||||||
Some of these checks can be performed automatically during commit using [pre-commit](https://pre-commit.com/).
|
|
||||||
Once the package is installed, simply run `pre-commit install` to enable the hooks, the checks will run automatically before the commit becomes effective.
|
|
151
Cargo.toml
151
Cargo.toml
@ -1,167 +1,42 @@
|
|||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
|
||||||
members = [
|
members = [
|
||||||
"libafl",
|
"libafl",
|
||||||
"libafl_bolts",
|
"libafl_derive",
|
||||||
"libafl_cc",
|
"libafl_cc",
|
||||||
|
"libafl_targets",
|
||||||
|
"libafl_frida",
|
||||||
|
"libafl_qemu",
|
||||||
|
"libafl_tinyinst",
|
||||||
|
"libafl_sugar",
|
||||||
|
"libafl_nyx",
|
||||||
"libafl_concolic/symcc_runtime",
|
"libafl_concolic/symcc_runtime",
|
||||||
"libafl_concolic/symcc_libafl",
|
"libafl_concolic/symcc_libafl",
|
||||||
"libafl_derive",
|
|
||||||
"libafl_frida",
|
|
||||||
"libafl_intelpt",
|
|
||||||
"libafl_libfuzzer",
|
|
||||||
"libafl_nyx",
|
|
||||||
"libafl_targets",
|
|
||||||
"libafl_tinyinst",
|
|
||||||
"libafl_qemu",
|
|
||||||
"libafl_qemu/libafl_qemu_build",
|
|
||||||
"libafl_qemu/libafl_qemu_sys",
|
|
||||||
"libafl_sugar",
|
|
||||||
"libafl_concolic/test/dump_constraints",
|
"libafl_concolic/test/dump_constraints",
|
||||||
"libafl_concolic/test/runtime_test",
|
"libafl_concolic/test/runtime_test",
|
||||||
"utils/build_and_test_fuzzers",
|
|
||||||
"utils/deexit",
|
"utils/deexit",
|
||||||
"utils/drcov_utils",
|
|
||||||
"utils/gramatron/construct_automata",
|
"utils/gramatron/construct_automata",
|
||||||
"utils/libafl_benches",
|
"utils/libafl_benches",
|
||||||
"utils/libafl_jumper",
|
|
||||||
"bindings/pylibafl",
|
|
||||||
]
|
]
|
||||||
default-members = [
|
default-members = [
|
||||||
"libafl",
|
"libafl",
|
||||||
"libafl_bolts",
|
|
||||||
"libafl_cc",
|
|
||||||
"libafl_derive",
|
"libafl_derive",
|
||||||
|
"libafl_cc",
|
||||||
"libafl_targets",
|
"libafl_targets",
|
||||||
]
|
]
|
||||||
|
|
||||||
exclude = [
|
exclude = [
|
||||||
"fuzzers",
|
"fuzzers",
|
||||||
"libafl_libfuzzer_runtime",
|
"bindings",
|
||||||
"utils/noaslr",
|
|
||||||
"utils/gdb_qemu",
|
|
||||||
"utils/libafl_fmt",
|
|
||||||
"utils/desyscall",
|
|
||||||
"utils/multi_machine_generator",
|
|
||||||
"scripts",
|
"scripts",
|
||||||
# additional crates
|
"libafl_qemu/libafl_qemu_build",
|
||||||
"libafl_concolic/test/symcc/util/symcc_fuzzing_helper",
|
"libafl_qemu/libafl_qemu_sys"
|
||||||
]
|
]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.14.1"
|
version = "0.8.2"
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
|
|
||||||
[workspace.dependencies]
|
|
||||||
# Internal deps
|
|
||||||
libafl = { path = "./libafl", version = "0.14.1", default-features = false }
|
|
||||||
libafl_bolts = { path = "./libafl_bolts", version = "0.14.1", default-features = false }
|
|
||||||
libafl_cc = { path = "./libafl_cc", version = "0.14.1", default-features = false }
|
|
||||||
symcc_runtime = { path = "./libafl_concolic/symcc_runtime", version = "0.14.1", default-features = false }
|
|
||||||
symcc_libafl = { path = "./libafl_concolic/symcc_libafl", version = "0.14.1", default-features = false }
|
|
||||||
libafl_derive = { path = "./libafl_derive", version = "0.14.1", default-features = false }
|
|
||||||
libafl_frida = { path = "./libafl_frida", version = "0.14.1", default-features = false }
|
|
||||||
libafl_intelpt = { path = "./libafl_intelpt", version = "0.14.1", default-features = false }
|
|
||||||
libafl_libfuzzer = { path = "./libafl_libfuzzer", version = "0.14.1", default-features = false }
|
|
||||||
libafl_nyx = { path = "./libafl_nyx", version = "0.14.1", default-features = false }
|
|
||||||
libafl_targets = { path = "./libafl_targets", version = "0.14.1", default-features = false }
|
|
||||||
libafl_tinyinst = { path = "./libafl_tinyinst", version = "0.14.1", default-features = false }
|
|
||||||
libafl_qemu = { path = "./libafl_qemu", version = "0.14.1", default-features = false }
|
|
||||||
libafl_qemu_build = { path = "./libafl_qemu/libafl_qemu_build", version = "0.14.1", default-features = false }
|
|
||||||
libafl_qemu_sys = { path = "./libafl_qemu/libafl_qemu_sys", version = "0.14.1", default-features = false }
|
|
||||||
libafl_sugar = { path = "./libafl_sugar", version = "0.14.1", default-features = false }
|
|
||||||
dump_constraints = { path = "./libafl_concolic/test/dump_constraints", version = "0.14.1", default-features = false }
|
|
||||||
runtime_test = { path = "./libafl_concolic/test/runtime_test", version = "0.14.1", default-features = false }
|
|
||||||
build_and_test_fuzzers = { path = "./utils/build_and_test_fuzzers", version = "0.14.1", default-features = false }
|
|
||||||
deexit = { path = "./utils/deexit", version = "0.14.1", default-features = false }
|
|
||||||
drcov_utils = { path = "./utils/drcov_utils", version = "0.14.1", default-features = false }
|
|
||||||
construct_automata = { path = "./utils/gramatron/construct_automata", version = "0.14.1", default-features = false }
|
|
||||||
libafl_benches = { path = "./utils/libafl_benches", version = "0.14.1", default-features = false }
|
|
||||||
libafl_jumper = { path = "./utils/libafl_jumper", version = "0.14.1", default-features = false }
|
|
||||||
|
|
||||||
# External deps
|
|
||||||
ahash = { version = "0.8.11", default-features = false } # The hash function already used in hashbrown
|
|
||||||
arbitrary-int = "1.2.7" # arbitrary sized integers, useful in combination with bitfields (bitbybit crate)
|
|
||||||
backtrace = { version = "0.3.74", default-features = false } # Used to get the stacktrace in StacktraceObserver
|
|
||||||
bindgen = "0.70.1"
|
|
||||||
bitbybit = "1.3.2" # bitfields, use this for bit fields and bit enums
|
|
||||||
clap = "4.5.18"
|
|
||||||
cc = "1.1.21"
|
|
||||||
cmake = "0.1.51"
|
|
||||||
document-features = "0.2.10"
|
|
||||||
hashbrown = { version = "0.14.5", default-features = false } # A faster hashmap, nostd compatible
|
|
||||||
libc = "0.2.159" # For (*nix) libc
|
|
||||||
libipt = "0.2.0"
|
|
||||||
log = "0.4.22"
|
|
||||||
meminterval = "0.4.1"
|
|
||||||
mimalloc = { version = "0.1.43", default-features = false }
|
|
||||||
nix = { version = "0.29.0", default-features = false }
|
|
||||||
num_enum = { version = "0.7.3", default-features = false }
|
|
||||||
num-traits = { version = "0.2.19", default-features = false }
|
|
||||||
paste = "1.0.15"
|
|
||||||
postcard = { version = "1.0.10", features = [
|
|
||||||
"alloc",
|
|
||||||
], default-features = false } # no_std compatible serde serialization format
|
|
||||||
pyo3 = "0.23.2"
|
|
||||||
pyo3-build-config = "0.23.2"
|
|
||||||
rangemap = "1.5.1"
|
|
||||||
regex = "1.10.6"
|
|
||||||
rustversion = "1.0.17"
|
|
||||||
serde = { version = "1.0.210", default-features = false } # serialization lib
|
|
||||||
serial_test = { version = "3.1.1", default-features = false }
|
|
||||||
serde_json = { version = "1.0.128", default-features = false }
|
|
||||||
serde_yaml = { version = "0.9.34" } # For parsing the injections yaml file
|
|
||||||
static_assertions = "1.1.0"
|
|
||||||
strum = "0.26.3"
|
|
||||||
strum_macros = "0.26.4"
|
|
||||||
toml = "0.8.19" # For parsing the injections toml file
|
|
||||||
typed-builder = "0.20.0" # Implement the builder pattern at compiletime
|
|
||||||
uuid = { version = "1.10.0", features = ["serde", "v4"] }
|
|
||||||
which = "6.0.3"
|
|
||||||
windows = "0.58.0"
|
|
||||||
z3 = "0.12.1"
|
|
||||||
|
|
||||||
|
|
||||||
[workspace.lints.rust]
|
|
||||||
# Forbid
|
|
||||||
unexpected_cfgs = "forbid"
|
|
||||||
|
|
||||||
# Allow
|
|
||||||
incomplete_features = "allow"
|
|
||||||
ambiguous_glob_reexports = "allow"
|
|
||||||
|
|
||||||
|
|
||||||
[workspace.lints.clippy]
|
|
||||||
# Deny
|
|
||||||
all = { level = "deny", priority = -1 }
|
|
||||||
pedantic = { level = "deny", priority = -1 }
|
|
||||||
cargo_common_metadata = "deny"
|
|
||||||
|
|
||||||
# Warn
|
|
||||||
cargo = { level = "warn", priority = -1 }
|
|
||||||
negative_feature_names = "warn"
|
|
||||||
|
|
||||||
# Allow
|
|
||||||
unreadable_literal = "allow"
|
|
||||||
type_repetition_in_bounds = "allow"
|
|
||||||
missing_errors_doc = "allow"
|
|
||||||
cast_possible_truncation = "allow"
|
|
||||||
used_underscore_binding = "allow"
|
|
||||||
ptr_as_ptr = "allow"
|
|
||||||
missing_panics_doc = "allow"
|
|
||||||
module_name_repetitions = "allow"
|
|
||||||
unsafe_derive_deserialize = "allow"
|
|
||||||
similar_names = "allow"
|
|
||||||
too_many_lines = "allow"
|
|
||||||
|
|
||||||
|
|
||||||
[workspace.lints.rustdoc]
|
|
||||||
# Deny
|
|
||||||
broken_intra_doc_links = "deny"
|
|
||||||
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
codegen-units = 1
|
codegen-units = 1
|
||||||
opt-level = 3
|
opt-level = 3
|
||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
|
60
Dockerfile
60
Dockerfile
@ -1,12 +1,10 @@
|
|||||||
# syntax=docker/dockerfile:1.2
|
# syntax=docker/dockerfile:1.2
|
||||||
FROM rust:1.76.0 AS libafl
|
FROM rust:bullseye AS libafl
|
||||||
LABEL "maintainer"="afl++ team <afl@aflplus.plus>"
|
LABEL "maintainer"="afl++ team <afl@aflplus.plus>"
|
||||||
LABEL "about"="LibAFL Docker image"
|
LABEL "about"="LibAFL Docker image"
|
||||||
|
|
||||||
# Install cargo-binstall to download the sccache build
|
|
||||||
RUN curl -L --proto '=https' --tlsv1.2 -sSf https://raw.githubusercontent.com/cargo-bins/cargo-binstall/main/install-from-binstall-release.sh | bash
|
|
||||||
# install sccache to cache subsequent builds of dependencies
|
# install sccache to cache subsequent builds of dependencies
|
||||||
RUN cargo binstall --no-confirm sccache
|
RUN cargo install sccache
|
||||||
|
|
||||||
ENV HOME=/root
|
ENV HOME=/root
|
||||||
ENV SCCACHE_CACHE_SIZE="1G"
|
ENV SCCACHE_CACHE_SIZE="1G"
|
||||||
@ -18,17 +16,10 @@ RUN sh -c 'echo set encoding=utf-8 > /root/.vimrc' \
|
|||||||
mkdir ~/.cargo && \
|
mkdir ~/.cargo && \
|
||||||
echo "[build]\nrustc-wrapper = \"${RUSTC_WRAPPER}\"" >> ~/.cargo/config
|
echo "[build]\nrustc-wrapper = \"${RUSTC_WRAPPER}\"" >> ~/.cargo/config
|
||||||
|
|
||||||
RUN rustup default nightly
|
|
||||||
RUN rustup component add rustfmt clippy
|
RUN rustup component add rustfmt clippy
|
||||||
|
|
||||||
# Install clang 18, common build tools
|
# Install clang 11, common build tools
|
||||||
ENV LLVM_VERSION=18
|
RUN apt update && apt install -y build-essential gdb git wget clang clang-tools libc++-11-dev libc++abi-11-dev llvm
|
||||||
RUN apt update && apt install -y build-essential gdb git wget python3-venv ninja-build lsb-release software-properties-common gnupg cmake
|
|
||||||
RUN set -ex &&\
|
|
||||||
wget https://apt.llvm.org/llvm.sh &&\
|
|
||||||
chmod +x llvm.sh &&\
|
|
||||||
./llvm.sh ${LLVM_VERSION}
|
|
||||||
|
|
||||||
|
|
||||||
# Copy a dummy.rs and Cargo.toml first, so that dependencies are cached
|
# Copy a dummy.rs and Cargo.toml first, so that dependencies are cached
|
||||||
WORKDIR /libafl
|
WORKDIR /libafl
|
||||||
@ -37,39 +28,26 @@ COPY Cargo.toml README.md ./
|
|||||||
COPY libafl_derive/Cargo.toml libafl_derive/Cargo.toml
|
COPY libafl_derive/Cargo.toml libafl_derive/Cargo.toml
|
||||||
COPY scripts/dummy.rs libafl_derive/src/lib.rs
|
COPY scripts/dummy.rs libafl_derive/src/lib.rs
|
||||||
|
|
||||||
COPY libafl/Cargo.toml libafl/build.rs libafl/README.md libafl/
|
COPY libafl/Cargo.toml libafl/build.rs libafl/
|
||||||
|
COPY libafl/examples libafl/examples
|
||||||
COPY scripts/dummy.rs libafl/src/lib.rs
|
COPY scripts/dummy.rs libafl/src/lib.rs
|
||||||
|
|
||||||
# Set up LLVM aliases
|
|
||||||
COPY scripts/createAliases.sh libafl/
|
|
||||||
RUN bash libafl/createAliases.sh ${LLVM_VERSION}
|
|
||||||
|
|
||||||
COPY libafl_bolts/Cargo.toml libafl_bolts/build.rs libafl_bolts/README.md libafl_bolts/
|
|
||||||
COPY libafl_bolts/examples libafl_bolts/examples
|
|
||||||
COPY scripts/dummy.rs libafl_bolts/src/lib.rs
|
|
||||||
|
|
||||||
COPY libafl_frida/Cargo.toml libafl_frida/build.rs libafl_frida/
|
COPY libafl_frida/Cargo.toml libafl_frida/build.rs libafl_frida/
|
||||||
COPY scripts/dummy.rs libafl_frida/src/lib.rs
|
COPY scripts/dummy.rs libafl_frida/src/lib.rs
|
||||||
COPY libafl_frida/src/gettls.c libafl_frida/src/gettls.c
|
COPY libafl_frida/src/gettls.c libafl_frida/src/gettls.c
|
||||||
|
|
||||||
COPY libafl_intelpt/Cargo.toml libafl_intelpt/README.md libafl_intelpt/
|
COPY libafl_qemu/Cargo.toml libafl_qemu/build.rs libafl_qemu/
|
||||||
COPY scripts/dummy.rs libafl_intelpt/src/lib.rs
|
|
||||||
|
|
||||||
COPY libafl_qemu/Cargo.toml libafl_qemu/build.rs libafl_qemu/build_linux.rs libafl_qemu/
|
|
||||||
COPY scripts/dummy.rs libafl_qemu/src/lib.rs
|
COPY scripts/dummy.rs libafl_qemu/src/lib.rs
|
||||||
|
|
||||||
COPY libafl_qemu/libafl_qemu_build/Cargo.toml libafl_qemu/libafl_qemu_build/
|
COPY libafl_qemu/libafl_qemu_build/Cargo.toml libafl_qemu/libafl_qemu_build/
|
||||||
COPY scripts/dummy.rs libafl_qemu/libafl_qemu_build/src/lib.rs
|
COPY scripts/dummy.rs libafl_qemu/libafl_qemu_build/src/lib.rs
|
||||||
|
|
||||||
COPY libafl_qemu/libafl_qemu_sys/Cargo.toml libafl_qemu/libafl_qemu_sys/build.rs libafl_qemu/libafl_qemu_sys/build_linux.rs libafl_qemu/libafl_qemu_sys/
|
COPY libafl_qemu/libafl_qemu_sys/Cargo.toml libafl_qemu/libafl_qemu_sys/build.rs libafl_qemu/libafl_qemu_sys/
|
||||||
COPY scripts/dummy.rs libafl_qemu/libafl_qemu_sys/src/lib.rs
|
COPY scripts/dummy.rs libafl_qemu/libafl_qemu_sys/src/lib.rs
|
||||||
|
|
||||||
COPY libafl_sugar/Cargo.toml libafl_sugar/
|
COPY libafl_sugar/Cargo.toml libafl_sugar/
|
||||||
COPY scripts/dummy.rs libafl_sugar/src/lib.rs
|
COPY scripts/dummy.rs libafl_sugar/src/lib.rs
|
||||||
|
|
||||||
COPY bindings/pylibafl/Cargo.toml bindings/pylibafl/Cargo.toml
|
|
||||||
COPY bindings/pylibafl/src bindings/pylibafl/src
|
|
||||||
|
|
||||||
COPY libafl_cc/Cargo.toml libafl_cc/Cargo.toml
|
COPY libafl_cc/Cargo.toml libafl_cc/Cargo.toml
|
||||||
COPY libafl_cc/build.rs libafl_cc/build.rs
|
COPY libafl_cc/build.rs libafl_cc/build.rs
|
||||||
COPY libafl_cc/src libafl_cc/src
|
COPY libafl_cc/src libafl_cc/src
|
||||||
@ -91,16 +69,12 @@ COPY scripts/dummy.rs libafl_concolic/symcc_runtime/src/lib.rs
|
|||||||
COPY libafl_concolic/symcc_libafl/Cargo.toml libafl_concolic/symcc_libafl/
|
COPY libafl_concolic/symcc_libafl/Cargo.toml libafl_concolic/symcc_libafl/
|
||||||
COPY scripts/dummy.rs libafl_concolic/symcc_libafl/src/lib.rs
|
COPY scripts/dummy.rs libafl_concolic/symcc_libafl/src/lib.rs
|
||||||
|
|
||||||
COPY libafl_nyx/Cargo.toml libafl_nyx/build.rs libafl_nyx/build_nyx_support.sh libafl_nyx/
|
COPY libafl_nyx/Cargo.toml libafl_nyx/build.rs libafl_nyx/
|
||||||
COPY scripts/dummy.rs libafl_nyx/src/lib.rs
|
COPY scripts/dummy.rs libafl_nyx/src/lib.rs
|
||||||
|
|
||||||
COPY libafl_tinyinst/Cargo.toml libafl_tinyinst/
|
COPY libafl_tinyinst/Cargo.toml libafl_tinyinst/
|
||||||
COPY scripts/dummy.rs libafl_tinyinst/src/lib.rs
|
COPY scripts/dummy.rs libafl_tinyinst/src/lib.rs
|
||||||
|
|
||||||
# avoid pulling in the runtime, as this is quite an expensive build, until later
|
|
||||||
COPY libafl_libfuzzer/Cargo.toml libafl_libfuzzer/
|
|
||||||
COPY scripts/dummy.rs libafl_libfuzzer/src/lib.rs
|
|
||||||
|
|
||||||
COPY utils utils
|
COPY utils utils
|
||||||
|
|
||||||
RUN cargo build && cargo build --release
|
RUN cargo build && cargo build --release
|
||||||
@ -121,8 +95,6 @@ COPY libafl_cc/src libafl_cc/src
|
|||||||
RUN touch libafl_cc/src/lib.rs
|
RUN touch libafl_cc/src/lib.rs
|
||||||
COPY libafl_derive/src libafl_derive/src
|
COPY libafl_derive/src libafl_derive/src
|
||||||
RUN touch libafl_derive/src/lib.rs
|
RUN touch libafl_derive/src/lib.rs
|
||||||
COPY libafl_bolts/src libafl_bolts/src
|
|
||||||
RUN touch libafl_bolts/src/lib.rs
|
|
||||||
COPY libafl/src libafl/src
|
COPY libafl/src libafl/src
|
||||||
RUN touch libafl/src/lib.rs
|
RUN touch libafl/src/lib.rs
|
||||||
COPY libafl_targets/src libafl_targets/src
|
COPY libafl_targets/src libafl_targets/src
|
||||||
@ -132,8 +104,6 @@ RUN touch libafl_qemu/libafl_qemu_build/src/lib.rs
|
|||||||
COPY libafl_qemu/libafl_qemu_build/src libafl_qemu/libafl_qemu_build/src
|
COPY libafl_qemu/libafl_qemu_build/src libafl_qemu/libafl_qemu_build/src
|
||||||
RUN touch libafl_qemu/libafl_qemu_sys/src/lib.rs
|
RUN touch libafl_qemu/libafl_qemu_sys/src/lib.rs
|
||||||
COPY libafl_qemu/libafl_qemu_sys/src libafl_qemu/libafl_qemu_sys/src
|
COPY libafl_qemu/libafl_qemu_sys/src libafl_qemu/libafl_qemu_sys/src
|
||||||
COPY libafl_qemu/runtime libafl_qemu/runtime
|
|
||||||
COPY libafl_qemu/libqasan libafl_qemu/libqasan
|
|
||||||
RUN touch libafl_qemu/src/lib.rs
|
RUN touch libafl_qemu/src/lib.rs
|
||||||
COPY libafl_qemu/src libafl_qemu/src
|
COPY libafl_qemu/src libafl_qemu/src
|
||||||
RUN touch libafl_frida/src/lib.rs
|
RUN touch libafl_frida/src/lib.rs
|
||||||
@ -142,19 +112,11 @@ COPY libafl_concolic/symcc_runtime libafl_concolic/symcc_runtime
|
|||||||
COPY libafl_concolic/test libafl_concolic/test
|
COPY libafl_concolic/test libafl_concolic/test
|
||||||
COPY libafl_nyx/src libafl_nyx/src
|
COPY libafl_nyx/src libafl_nyx/src
|
||||||
RUN touch libafl_nyx/src/lib.rs
|
RUN touch libafl_nyx/src/lib.rs
|
||||||
COPY libafl_libfuzzer_runtime libafl_libfuzzer_runtime
|
|
||||||
COPY libafl_libfuzzer/src libafl_libfuzzer/src
|
|
||||||
COPY libafl_libfuzzer/runtime libafl_libfuzzer/runtime
|
|
||||||
COPY libafl_libfuzzer/build.rs libafl_libfuzzer/build.rs
|
|
||||||
RUN touch libafl_libfuzzer/src/lib.rs
|
|
||||||
COPY libafl_intelpt/src libafl_intelpt/src
|
|
||||||
RUN touch libafl_intelpt/src/lib.rs
|
|
||||||
RUN cargo build && cargo build --release
|
RUN cargo build && cargo build --release
|
||||||
|
|
||||||
# Copy fuzzers over
|
# Copy fuzzers over
|
||||||
COPY fuzzers fuzzers
|
COPY fuzzers fuzzers
|
||||||
|
|
||||||
# RUN ./scripts/test_fuzzer.sh --no-fmt
|
# RUN ./scripts/test_all_fuzzers.sh --no-fmt
|
||||||
|
|
||||||
ENTRYPOINT [ "/bin/bash", "-c" ]
|
ENTRYPOINT [ "/bin/bash" ]
|
||||||
CMD ["/bin/bash"]
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
# Pre 0.9 -> 0.9
|
|
||||||
- [Migrating from LibAFL <0.9 to 0.9](https://aflplus.plus/libafl-book/design/migration-0.9.html)
|
|
||||||
|
|
||||||
# 0.14.0 -> 0.14.1
|
|
||||||
- Removed `with_observers` from `Executor` trait.
|
|
||||||
- `MmapShMemProvider::new_shmem_persistent` has been removed in favour of `MmapShMem::persist`. You probably want to do something like this: `let shmem = MmapShMemProvider::new()?.new_shmem(size)?.persist()?;`
|
|
141
README.md
141
README.md
@ -1,10 +1,19 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
LibAFL is a collection of reusable pieces of fuzzers, written in Rust, it gives you many of the benefits of an off-the-shelf fuzzer, while being completely customizable.
|
LibAFL is written and maintained by
|
||||||
|
|
||||||
|
* [Andrea Fioraldi](https://twitter.com/andreafioraldi) <andrea@aflplus.plus>
|
||||||
|
* [Dominik Maier](https://twitter.com/domenuk) <dominik@aflplus.plus>
|
||||||
|
* [s1341](https://twitter.com/srubenst1341) <github@shmarya.net>
|
||||||
|
* [Dongjia Zhang](https://github.com/tokatoka) <toka@aflplus.plus>
|
||||||
|
|
||||||
|
## Why LibAFL?
|
||||||
|
|
||||||
|
LibAFL gives you many of the benefits of an off-the-shelf fuzzer, while being completely customizable.
|
||||||
Some highlight features currently include:
|
Some highlight features currently include:
|
||||||
- `fast`: We do everything we can at compile time, keeping runtime overhead minimal. Users reach 120k execs/sec in frida-mode on a phone (using all cores).
|
- `fast`: We do everything we can at compile time, keeping runtime overhead minimal. Users reach 120k execs/sec in frida-mode on a phone (using all cores).
|
||||||
- `scalable`: `Low Level Message Passing`, `LLMP` for short, allows LibAFL to scale almost linearly over cores, and via TCP to multiple machines.
|
- `scalable`: `Low Level Message Passing`, `LLMP` for short, allows LibAFL to scale almost linearly over cores, and via TCP to multiple machines.
|
||||||
@ -13,74 +22,104 @@ feel free to add an AST-based input for structured fuzzing, and more.
|
|||||||
- `multi platform`: LibAFL was confirmed to work on *Windows*, *MacOS*, *Linux*, and *Android* on *x86_64* and *aarch64*. `LibAFL` can be built in `no_std` mode to inject LibAFL into obscure targets like embedded devices and hypervisors.
|
- `multi platform`: LibAFL was confirmed to work on *Windows*, *MacOS*, *Linux*, and *Android* on *x86_64* and *aarch64*. `LibAFL` can be built in `no_std` mode to inject LibAFL into obscure targets like embedded devices and hypervisors.
|
||||||
- `bring your own target`: We support binary-only modes, like Frida-Mode, as well as multiple compilation passes for sourced-based instrumentation. Of course it's easy to add custom instrumentation backends.
|
- `bring your own target`: We support binary-only modes, like Frida-Mode, as well as multiple compilation passes for sourced-based instrumentation. Of course it's easy to add custom instrumentation backends.
|
||||||
|
|
||||||
## Core concepts
|
## Overview
|
||||||
|
|
||||||
LibAFL is fast, multi-platform, no_std compatible, and scales over cores and machines. It offers a main crate that provide building blocks for custom fuzzers, [libafl](./libafl), a library containing common code that can be used for targets instrumentation, [libafl_targets](./libafl_targets), and a library providing facilities to wrap compilers, [libafl_cc](./libafl_cc). It offers integrations with popular instrumentation frameworks. At the moment, the supported backends are:
|
LibAFL is a collection of reusable pieces of fuzzers, written in Rust.
|
||||||
+ `SanitizerCoverage`, in [libafl_targets](./libafl_targets)
|
It is fast, multi-platform, no_std compatible, and scales over cores and machines.
|
||||||
+ `Frida`, in [libafl_frida](./libafl_frida)
|
|
||||||
+ `QEMU` user-mode and system mode, including hooks for emulation, in [libafl_qemu](./libafl_qemu)
|
|
||||||
+ `TinyInst`, in [libafl_tinyinst](./libafl_tinyinst) by [elbiazo](https://github.com/elbiazo)
|
|
||||||
|
|
||||||
## Building and installing
|
It offers a main crate that provide building blocks for custom fuzzers, [libafl](./libafl), a library containing common code that can be used for targets instrumentation, [libafl_targets](./libafl_targets), and a library providing facilities to wrap compilers, [libafl_cc](./libafl_cc).
|
||||||
|
|
||||||
#### Install the Dependencies
|
LibAFL offers integrations with popular instrumentation frameworks. At the moment, the supported backends are:
|
||||||
- **The Rust development language**
|
|
||||||
- We highly recommend *not* to use e.g. your Linux distribution package as this is likely outdated. So rather install Rust directly, instructions can be found [here](https://www.rust-lang.org/tools/install).
|
|
||||||
- **LLVM tools**
|
|
||||||
- The LLVM tools (including clang, clang++) are needed (newer than LLVM 15.0.0 up to LLVM 18.1.3) If you are using Debian/Ubuntu, again, we highly recommmend that you install the package from [here](https://apt.llvm.org/)
|
|
||||||
- (In `libafl_concolic`, we only support LLVM version newer than 18)
|
|
||||||
- Cargo-make:
|
|
||||||
- We use cargo-make to build the fuzzers in `fuzzers/` directory. You can install it with `cargo install cargo-make`
|
|
||||||
|
|
||||||
#### Clone the LibAFL repository with
|
+ SanitizerCoverage, in [libafl_targets](./libafl_targets)
|
||||||
```sh
|
+ Frida, in [libafl_frida](./libafl_frida)
|
||||||
|
+ QEMU user-mode, in [libafl_qemu](./libafl_qemu)
|
||||||
|
+ TinyInst, in [libafl_tinyinst](./libafl_tinyinst) by [elbiazo](https://github.com/elbiazo)
|
||||||
|
|
||||||
|
## Getting started
|
||||||
|
|
||||||
|
1. Install the Dependecies
|
||||||
|
- The Rust development language.
|
||||||
|
We highly recommend *not* to use e.g. your Linux distribition package as this is likely outdated. So rather install
|
||||||
|
Rust directly, instructions can be found [here](https://www.rust-lang.org/tools/install).
|
||||||
|
|
||||||
|
- LLVM tools
|
||||||
|
The LLVM tools are needed (newer than LLVM 11.0.0 but older than LLVM 15.0.0)
|
||||||
|
|
||||||
|
- Cargo-make
|
||||||
|
We use cargo-make to build the fuzzers in `fuzzers/` directory. You can install it with
|
||||||
|
|
||||||
|
```
|
||||||
|
cargo install cargo-make
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Clone the LibAFL repository with
|
||||||
|
|
||||||
|
```
|
||||||
git clone https://github.com/AFLplusplus/LibAFL
|
git clone https://github.com/AFLplusplus/LibAFL
|
||||||
```
|
```
|
||||||
#### Build the library using
|
|
||||||
```sh
|
3. Build the library using
|
||||||
|
|
||||||
|
```
|
||||||
cargo build --release
|
cargo build --release
|
||||||
```
|
```
|
||||||
#### Build the API documentation with
|
|
||||||
```sh
|
4. Build the API documentation with
|
||||||
|
|
||||||
|
```
|
||||||
cargo doc
|
cargo doc
|
||||||
```
|
```
|
||||||
#### Browse the LibAFL book (WIP!) with (requires [mdbook](https://rust-lang.github.io/mdBook/index.html))
|
|
||||||
```sh
|
5. Browse the LibAFL book (WIP!) with (requires [mdbook](https://github.com/rust-lang/mdBook))
|
||||||
|
|
||||||
|
```
|
||||||
cd docs && mdbook serve
|
cd docs && mdbook serve
|
||||||
```
|
```
|
||||||
## Getting started
|
|
||||||
We collect all example fuzzers in [`./fuzzers`](./fuzzers/).
|
We collect all example fuzzers in [`./fuzzers`](./fuzzers/).
|
||||||
Be sure to read their documentation (and source), this is *the natural way to get started!*
|
Be sure to read their documentation (and source), this is *the natural way to get started!*
|
||||||
```sh
|
|
||||||
|
You can run each example fuzzer with
|
||||||
|
```
|
||||||
cargo make run
|
cargo make run
|
||||||
```
|
```
|
||||||
You can run each example fuzzer with this following command, as long as the fuzzer directory has `Makefile.toml` file. The best-tested fuzzer is [`./fuzzers/inprocess/libfuzzer_libpng`](./fuzzers/inprocess/libfuzzer_libpng), a multicore libfuzzer-like fuzzer using LibAFL for a libpng harness.
|
as long as the fuzzer directory has `Makefile.toml` file.
|
||||||
|
|
||||||
### Resources
|
The best-tested fuzzer is [`./fuzzers/libfuzzer_libpng`](./fuzzers/libfuzzer_libpng), a multicore libfuzzer-like fuzzer using LibAFL for a libpng harness.
|
||||||
- [Installation guide](./docs/src/getting_started/setup.md)
|
|
||||||
- [Online API documentation](https://docs.rs/libafl/)
|
|
||||||
- The LibAFL book (WIP) [online](https://aflplus.plus/libafl-book) or in the [repo](./docs/src/)
|
|
||||||
- Our research [paper](https://www.s3.eurecom.fr/docs/ccs22_fioraldi.pdf)
|
|
||||||
- Our RC3 [talk](http://www.youtube.com/watch?v=3RWkT1Q5IV0 "Fuzzers Like LEGO") explaining the core concepts
|
|
||||||
- Our Fuzzcon Europe [talk](https://www.youtube.com/watch?v=PWB8GIhFAaI "LibAFL: The Advanced Fuzzing Library") with a (a bit but not so much outdated) step-by-step discussion on how to build some example fuzzers
|
|
||||||
- The Fuzzing101 [solutions](https://github.com/epi052/fuzzing-101-solutions) & series of [blog posts](https://epi052.gitlab.io/notes-to-self/blog/2021-11-01-fuzzing-101-with-libafl/) by [epi](https://github.com/epi052)
|
|
||||||
- Blogpost on binary-only fuzzing lib libaf_qemu, [Hacking TMNF - Fuzzing the game server](https://blog.bricked.tech/posts/tmnf/part1/), by [RickdeJager](https://github.com/RickdeJager).
|
|
||||||
- [A LibAFL Introductory Workshop](https://www.atredis.com/blog/2023/12/4/a-libafl-introductory-workshop), by [Jordan Whitehead](https://github.com/jordan9001)
|
|
||||||
|
|
||||||
## Contributors
|
## Resources
|
||||||
|
|
||||||
LibAFL is written and maintained by
|
+ [Installation guide](./docs/src/getting_started/setup.md)
|
||||||
|
|
||||||
* [Andrea Fioraldi](https://twitter.com/andreafioraldi) <andrea@aflplus.plus>
|
+ [Online API documentation](https://docs.rs/libafl/)
|
||||||
* [Dominik Maier](https://twitter.com/domenuk) <dominik@aflplus.plus>
|
|
||||||
* [s1341](https://twitter.com/srubenst1341) <github@shmarya.net>
|
|
||||||
* [Dongjia Zhang](https://github.com/tokatoka) <toka@aflplus.plus>
|
|
||||||
* [Addison Crump](https://github.com/addisoncrump) <me@addisoncrump.info>
|
|
||||||
* [Romain Malmain](https://github.com/rmalmain) <rmalmain@pm.me>
|
|
||||||
|
|
||||||
Please check out [CONTRIBUTING.md](CONTRIBUTING.md) for the contributing guideline.
|
+ The LibAFL book (WIP) [online](https://aflplus.plus/libafl-book) or in the [repo](./docs/src/)
|
||||||
|
|
||||||
|
+ Our research [paper](https://www.s3.eurecom.fr/docs/ccs22_fioraldi.pdf)
|
||||||
|
|
||||||
|
+ Our RC3 [talk](http://www.youtube.com/watch?v=3RWkT1Q5IV0 "Fuzzers Like LEGO") explaining the core concepts
|
||||||
|
|
||||||
|
+ Our Fuzzcon Europe [talk](https://www.youtube.com/watch?v=PWB8GIhFAaI "LibAFL: The Advanced Fuzzing Library") with a (a bit but not so much outdated) step-by-step discussion on how to build some example fuzzers
|
||||||
|
|
||||||
|
+ The Fuzzing101 [solutions](https://github.com/epi052/fuzzing-101-solutions) & series of [blog posts](https://epi052.gitlab.io/notes-to-self/blog/2021-11-01-fuzzing-101-with-libafl/) by [epi](https://github.com/epi052)
|
||||||
|
|
||||||
|
+ Blogpost on binary-only fuzzing lib libaf_qemu, [Hacking TMNF - Fuzzing the game server](https://blog.bricked.tech/posts/tmnf/part1/), by [RickdeJager](https://github.com/RickdeJager).
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
For bugs, feel free to open issues or contact us directly. Thank you for your support. <3
|
||||||
|
|
||||||
|
Even though we will gladly assist you in finishing up your PR, try to
|
||||||
|
- keep all the crates compiling with *stable* rust (hide the eventual non-stable code under [`cfg`s](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/build.rs#L26))
|
||||||
|
- run `cargo fmt` on your code before pushing
|
||||||
|
- check the output of `cargo clippy --all` or `./clippy.sh`
|
||||||
|
- run `cargo build --no-default-features` to check for `no_std` compatibility (and possibly add `#[cfg(feature = "std")]`) to hide parts of your code.
|
||||||
|
|
||||||
|
Some of the parts in this list may be hard, don't be afraid to open a PR if you cannot fix them by yourself, so we can help.
|
||||||
|
|
||||||
## Cite
|
## Cite
|
||||||
|
|
||||||
If you use LibAFL for your academic work, please cite the following paper:
|
If you use LibAFL for your academic work, please cite the following paper:
|
||||||
|
|
||||||
```bibtex
|
```bibtex
|
||||||
@ -110,3 +149,11 @@ Unless you explicitly state otherwise, any contribution intentionally submitted
|
|||||||
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
|
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
|
||||||
be dual licensed as above, without any additional terms or conditions.
|
be dual licensed as above, without any additional terms or conditions.
|
||||||
</sub>
|
</sub>
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
<sub>
|
||||||
|
Dependencies under more restrictive licenses, such as GPL or AGPL, can be enabled
|
||||||
|
using the respective feature in each crate when it is present, such as the
|
||||||
|
'agpl' feature of the libafl crate.
|
||||||
|
</sub>
|
||||||
|
17
TROPHIES.md
17
TROPHIES.md
@ -1,17 +0,0 @@
|
|||||||
# Bugs found by `libafl` and `libafl_libfuzzer`
|
|
||||||
|
|
||||||
* pdf-rs
|
|
||||||
* <https://github.com/pdf-rs/pdf/issues/183>
|
|
||||||
* <https://github.com/pdf-rs/pdf/issues/184>
|
|
||||||
* <https://github.com/pdf-rs/pdf/issues/185>
|
|
||||||
* <https://github.com/pdf-rs/pdf/issues/186>
|
|
||||||
* <https://github.com/pdf-rs/pdf/issues/187>
|
|
||||||
* <https://github.com/pdf-rs/pdf/issues/189>
|
|
||||||
* nu-shell
|
|
||||||
* https://github.com/nushell/nushell/issues/10365
|
|
||||||
* https://github.com/nushell/nushell/issues/9417
|
|
||||||
* exrs
|
|
||||||
* https://github.com/johannesvollmer/exrs/pull/221
|
|
||||||
* pcre2
|
|
||||||
* https://github.com/PCRE2Project/pcre2/issues/275
|
|
||||||
|
|
1
bindings/pylibafl/.gitignore
vendored
1
bindings/pylibafl/.gitignore
vendored
@ -1 +0,0 @@
|
|||||||
dist/
|
|
@ -1,30 +1,16 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "pylibafl"
|
name = "pylibafl"
|
||||||
description = "Python bindings for LibAFL"
|
version = "0.8.2"
|
||||||
version = "0.14.1"
|
|
||||||
license = "MIT OR Apache-2.0"
|
|
||||||
repository = "https://github.com/AFLplusplus/LibAFL/"
|
|
||||||
keywords = ["fuzzing", "testing", "security", "python"]
|
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
categories = ["development-tools::testing", "emulators", "embedded", "os"]
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
pyo3 = { workspace = true, features = ["extension-module"] }
|
pyo3 = { version = "0.17", features = ["extension-module"] }
|
||||||
pyo3-log = { version = "0.12.0" }
|
libafl_qemu = { path = "../../libafl_qemu", version = "0.8.2", features = ["python"] }
|
||||||
libafl_sugar = { path = "../../libafl_sugar", version = "0.14.1", features = [
|
libafl_sugar = { path = "../../libafl_sugar", version = "0.8.2", features = ["python"] }
|
||||||
"python",
|
libafl = { path = "../../libafl", version = "0.8.2", features = ["python"] }
|
||||||
] }
|
|
||||||
libafl_bolts = { path = "../../libafl_bolts", version = "0.14.1", features = [
|
|
||||||
"python",
|
|
||||||
] }
|
|
||||||
|
|
||||||
[target.'cfg(target_os = "linux")'.dependencies]
|
|
||||||
libafl_qemu = { path = "../../libafl_qemu", version = "0.14.1", features = [
|
|
||||||
"python",
|
|
||||||
] }
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
pyo3-build-config = { workspace = true }
|
pyo3-build-config = { version = "0.17" }
|
||||||
|
|
||||||
[lib]
|
[lib]
|
||||||
name = "pylibafl"
|
name = "pylibafl"
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
[build-system]
|
|
||||||
requires = ["maturin[patchelf]>=0.14.10,<0.15"]
|
|
||||||
build-backend = "maturin"
|
|
||||||
|
|
||||||
[project]
|
|
||||||
name = "PyLibAFL"
|
|
||||||
version = "0.14.1"
|
|
||||||
description = "Advanced Fuzzing Library for Python"
|
|
||||||
readme = "README.md"
|
|
||||||
requires-python = ">=3.8"
|
|
||||||
license = { text = "Apache-2.0" }
|
|
||||||
classifiers = [
|
|
||||||
"License :: OSI Approved :: Apache Software License",
|
|
||||||
"License :: OSI Approved :: MIT License",
|
|
||||||
"Programming Language :: Rust",
|
|
||||||
"Topic :: Security",
|
|
||||||
]
|
|
||||||
|
|
||||||
[project.urls]
|
|
||||||
repository = "https://github.com/AFLplusplus/LibAFL.git"
|
|
||||||
|
|
||||||
[tool.maturin]
|
|
||||||
bindings = "pylibafl"
|
|
||||||
manifest-path = "Cargo.toml"
|
|
||||||
python-source = "python"
|
|
||||||
all-features = true
|
|
@ -1,33 +1,121 @@
|
|||||||
use pyo3::prelude::*;
|
use libafl;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
use libafl_qemu;
|
||||||
|
use libafl_sugar;
|
||||||
|
use pyo3::{prelude::*, types::PyDict};
|
||||||
|
|
||||||
|
const LIBAFL_CODE: &str = r#"
|
||||||
|
class BaseObserver:
|
||||||
|
def flush(self):
|
||||||
|
pass
|
||||||
|
def pre_exec(self, state, input):
|
||||||
|
pass
|
||||||
|
def post_exec(self, state, input, exit_kind):
|
||||||
|
pass
|
||||||
|
def pre_exec_child(self, state, input):
|
||||||
|
pass
|
||||||
|
def post_exec_child(self, state, input, exit_kind):
|
||||||
|
pass
|
||||||
|
def name(self):
|
||||||
|
return type(self).__name__
|
||||||
|
def as_observer(self):
|
||||||
|
return Observer.new_py(self)
|
||||||
|
|
||||||
|
class BaseFeedback:
|
||||||
|
def init_state(self, state):
|
||||||
|
pass
|
||||||
|
def is_interesting(self, state, mgr, input, observers, exit_kind) -> bool:
|
||||||
|
return False
|
||||||
|
def append_metadata(self, state, testcase):
|
||||||
|
pass
|
||||||
|
def discard_metadata(self, state, input):
|
||||||
|
pass
|
||||||
|
def name(self):
|
||||||
|
return type(self).__name__
|
||||||
|
def as_feedback(self):
|
||||||
|
return Feedback.new_py(self)
|
||||||
|
|
||||||
|
class BaseExecutor:
|
||||||
|
def observers(self) -> ObserversTuple:
|
||||||
|
raise NotImplementedError('Implement this yourself')
|
||||||
|
def run_target(self, fuzzer, state, mgr, input) -> ExitKind:
|
||||||
|
raise NotImplementedError('Implement this yourself')
|
||||||
|
def as_executor(self):
|
||||||
|
return Executor.new_py(self)
|
||||||
|
|
||||||
|
class BaseStage:
|
||||||
|
def perform(self, fuzzer, executor, state, manager, corpus_idx):
|
||||||
|
pass
|
||||||
|
def as_stage(self):
|
||||||
|
return Stage.new_py(self)
|
||||||
|
|
||||||
|
class BaseMutator:
|
||||||
|
def mutate(self, state, input, stage_idx):
|
||||||
|
pass
|
||||||
|
def post_exec(self, state, stage_idx, corpus_idx):
|
||||||
|
pass
|
||||||
|
def as_mutator(self):
|
||||||
|
return Mutator.new_py(self)
|
||||||
|
|
||||||
|
class FnStage(BaseStage):
|
||||||
|
def __init__(self, fn):
|
||||||
|
self.fn = fn
|
||||||
|
def __call__(self, fuzzer, executor, state, manager, corpus_idx):
|
||||||
|
self.fn(fuzzer, executor, state, manager, corpus_idx)
|
||||||
|
def perform(self, fuzzer, executor, state, manager, corpus_idx):
|
||||||
|
self.fn(fuzzer, executor, state, manager, corpus_idx)
|
||||||
|
|
||||||
|
def feedback_not(a):
|
||||||
|
return NotFeedback(a).as_feedback()
|
||||||
|
|
||||||
|
def feedback_and(a, b):
|
||||||
|
return EagerAndFeedback(a, b).as_feedback()
|
||||||
|
|
||||||
|
def feedback_and_fast(a, b):
|
||||||
|
return FastAndFeedback(a, b).as_feedback()
|
||||||
|
|
||||||
|
def feedback_or(a, b):
|
||||||
|
return EagerOrFeedback(a, b).as_feedback()
|
||||||
|
|
||||||
|
def feedback_or_fast(a, b):
|
||||||
|
return FastOrFeedback(a, b).as_feedback()
|
||||||
|
"#;
|
||||||
|
|
||||||
/// Setup python modules for `libafl_qemu` and `libafl_sugar`.
|
|
||||||
///
|
|
||||||
/// # Errors
|
|
||||||
/// Returns error if python libafl setup failed.
|
|
||||||
#[pymodule]
|
#[pymodule]
|
||||||
#[pyo3(name = "pylibafl")]
|
#[pyo3(name = "pylibafl")]
|
||||||
pub fn python_module(m: &Bound<'_, PyModule>) -> PyResult<()> {
|
pub fn python_module(py: Python, m: &PyModule) -> PyResult<()> {
|
||||||
pyo3_log::init();
|
let modules = py.import("sys")?.getattr("modules")?;
|
||||||
|
|
||||||
let modules = m.py().import("sys")?.getattr("modules")?;
|
let sugar_module = PyModule::new(py, "sugar")?;
|
||||||
|
libafl_sugar::python_module(py, sugar_module)?;
|
||||||
|
m.add_submodule(sugar_module)?;
|
||||||
|
|
||||||
let sugar_module = PyModule::new(m.py(), "sugar")?;
|
|
||||||
libafl_sugar::python_module(&sugar_module)?;
|
|
||||||
m.add_submodule(&sugar_module)?;
|
|
||||||
modules.set_item("pylibafl.sugar", sugar_module)?;
|
modules.set_item("pylibafl.sugar", sugar_module)?;
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
{
|
let qemu_module = PyModule::new(py, "qemu")?;
|
||||||
let qemu_module = PyModule::new(m.py(), "qemu")?;
|
#[cfg(target_os = "linux")]
|
||||||
libafl_qemu::python_module(&qemu_module)?;
|
libafl_qemu::python_module(py, qemu_module)?;
|
||||||
m.add_submodule(&qemu_module)?;
|
#[cfg(target_os = "linux")]
|
||||||
|
m.add_submodule(qemu_module)?;
|
||||||
|
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
modules.set_item("pylibafl.qemu", qemu_module)?;
|
modules.set_item("pylibafl.qemu", qemu_module)?;
|
||||||
|
|
||||||
|
let libafl_module = PyModule::new(py, "libafl")?;
|
||||||
|
libafl::pybind::python_module(py, libafl_module)?;
|
||||||
|
|
||||||
|
libafl_module.add("__builtins__", py.import("builtins")?)?;
|
||||||
|
|
||||||
|
let locals = PyDict::new(py);
|
||||||
|
py.run(LIBAFL_CODE, Some(libafl_module.dict()), Some(locals))?;
|
||||||
|
for (key, val) in locals.iter() {
|
||||||
|
libafl_module.add(key.extract::<&str>()?, val)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
let bolts_module = PyModule::new(m.py(), "libafl_bolts")?;
|
m.add_submodule(libafl_module)?;
|
||||||
libafl_bolts::pybind::python_module(&bolts_module)?;
|
|
||||||
m.add_submodule(&bolts_module)?;
|
modules.set_item("pylibafl.libafl", libafl_module)?;
|
||||||
modules.set_item("pylibafl.libafl_bolts", bolts_module)?;
|
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,94 @@
|
|||||||
import pylibafl.sugar as sugar
|
from pylibafl.libafl import *
|
||||||
import ctypes
|
import ctypes
|
||||||
import platform
|
|
||||||
|
|
||||||
print("Starting to fuzz from python!")
|
|
||||||
fuzzer = sugar.InMemoryBytesCoverageSugar(
|
class FooObserver(BaseObserver):
|
||||||
input_dirs=["./in"], output_dir="out", broker_port=1337, cores=[0, 1]
|
def __init__(self):
|
||||||
|
self.n = 0
|
||||||
|
|
||||||
|
def name(self):
|
||||||
|
return "Foo"
|
||||||
|
|
||||||
|
def pre_exec(self, state, input):
|
||||||
|
if self.n % 10000 == 0:
|
||||||
|
print("FOO!", self.n, input)
|
||||||
|
self.n += 1
|
||||||
|
|
||||||
|
|
||||||
|
class FooFeedback(BaseFeedback):
|
||||||
|
def is_interesting(self, state, mgr, input, observers, exit_kind):
|
||||||
|
ob = observers.match_name("Foo").unwrap_py()
|
||||||
|
return ob.n % 10000 == 0
|
||||||
|
|
||||||
|
|
||||||
|
class FooExecutor(BaseExecutor):
|
||||||
|
def __init__(self, harness, observers: ObserversTuple):
|
||||||
|
self.h = harness
|
||||||
|
self.o = observers
|
||||||
|
|
||||||
|
def observers(self):
|
||||||
|
return self.o
|
||||||
|
|
||||||
|
def run_target(self, fuzzer, state, mgr, input) -> ExitKind:
|
||||||
|
return (self.h)(input)
|
||||||
|
|
||||||
|
|
||||||
|
libc = ctypes.cdll.LoadLibrary("libc.so.6")
|
||||||
|
|
||||||
|
area_ptr = libc.calloc(1, 4096)
|
||||||
|
|
||||||
|
observer = StdMapObserverI8("mymap", area_ptr, 4096)
|
||||||
|
|
||||||
|
m = observer.as_map_observer()
|
||||||
|
|
||||||
|
observers = ObserversTuple(
|
||||||
|
[observer.as_map_observer().as_observer(), FooObserver().as_observer()]
|
||||||
)
|
)
|
||||||
fuzzer.run(lambda b: print("foo"))
|
|
||||||
|
feedback = feedback_or(MaxMapFeedbackI8(m).as_feedback(), FooFeedback().as_feedback())
|
||||||
|
|
||||||
|
objective = feedback_and_fast(
|
||||||
|
CrashFeedback().as_feedback(), MaxMapFeedbackI8(m).as_feedback()
|
||||||
|
)
|
||||||
|
|
||||||
|
fuzzer = StdFuzzer(feedback, objective)
|
||||||
|
|
||||||
|
rand = StdRand.with_current_nanos()
|
||||||
|
|
||||||
|
state = StdState(
|
||||||
|
rand.as_rand(),
|
||||||
|
InMemoryCorpus().as_corpus(),
|
||||||
|
InMemoryCorpus().as_corpus(),
|
||||||
|
feedback,
|
||||||
|
objective,
|
||||||
|
)
|
||||||
|
|
||||||
|
monitor = SimpleMonitor(lambda s: print(s))
|
||||||
|
|
||||||
|
mgr = SimpleEventManager(monitor.as_monitor())
|
||||||
|
|
||||||
|
|
||||||
|
def harness(buf) -> ExitKind:
|
||||||
|
# print(buf)
|
||||||
|
m[0] = 1
|
||||||
|
if len(buf) > 0 and buf[0] == ord("a"):
|
||||||
|
m[1] = 1
|
||||||
|
if len(buf) > 1 and buf[1] == ord("b"):
|
||||||
|
m[2] = 1
|
||||||
|
if len(buf) > 2 and buf[2] == ord("c"):
|
||||||
|
m[3] = 1
|
||||||
|
return ExitKind.crash()
|
||||||
|
return ExitKind.ok()
|
||||||
|
|
||||||
|
|
||||||
|
# executor = InProcessExecutor(harness, observers, fuzzer, state, mgr.as_manager())
|
||||||
|
|
||||||
|
executor = FooExecutor(harness, observers)
|
||||||
|
|
||||||
|
stage = StdMutationalStage(StdHavocMutator().as_mutator())
|
||||||
|
|
||||||
|
stage_tuple_list = StagesTuple([stage.as_stage()])
|
||||||
|
|
||||||
|
fuzzer.add_input(state, executor.as_executor(), mgr.as_manager(), b"\0\0")
|
||||||
|
|
||||||
|
fuzzer.fuzz_loop(executor.as_executor(), state, mgr.as_manager(), stage_tuple_list)
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
mkdir in || true
|
|
||||||
echo "a" > ./in/a
|
|
||||||
|
|
||||||
timeout 10 python3 ./test.py
|
|
||||||
export exit_code=$?
|
|
||||||
if [ $exit_code -eq 124 ]; then
|
|
||||||
# 124 = timeout happened. All good.
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
exit $exit_code
|
|
||||||
fi
|
|
||||||
|
|
1
docs/.gitignore
vendored
1
docs/.gitignore
vendored
@ -1,2 +1 @@
|
|||||||
book
|
book
|
||||||
!listings/**/*
|
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "baby_fuzzer_listing_01"
|
|
||||||
version = "0.14.1"
|
|
||||||
authors = ["Your Name <you@example.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
@ -1,21 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "baby_fuzzer_listing_02"
|
|
||||||
version = "0.14.1"
|
|
||||||
authors = ["Your Name <you@example.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libafl = { path = "path/to/libafl/" }
|
|
||||||
libafl_bolts = { path = "path/to/libafl_bolts/" }
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
panic = "abort"
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
opt-level = 3
|
|
||||||
debug = true
|
|
@ -1,3 +0,0 @@
|
|||||||
fn main() {
|
|
||||||
println!("Hello, world!");
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "baby_fuzzer_listing_03"
|
|
||||||
version = "0.14.1"
|
|
||||||
authors = ["Your Name <you@example.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libafl = { path = "path/to/libafl/" }
|
|
||||||
libafl_bolts = { path = "path/to/libafl_bolts/" }
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
panic = "abort"
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
opt-level = 3
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
panic = []
|
|
@ -1,26 +0,0 @@
|
|||||||
extern crate libafl;
|
|
||||||
extern crate libafl_bolts;
|
|
||||||
use libafl::{
|
|
||||||
executors::ExitKind,
|
|
||||||
inputs::{BytesInput, HasTargetBytes},
|
|
||||||
};
|
|
||||||
use libafl_bolts::AsSlice;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut harness = |input: &BytesInput| {
|
|
||||||
let target = input.target_bytes();
|
|
||||||
let buf = target.as_slice();
|
|
||||||
if buf.len() > 0 && buf[0] == 'a' as u8 {
|
|
||||||
if buf.len() > 1 && buf[1] == 'b' as u8 {
|
|
||||||
if buf.len() > 2 && buf[2] == 'c' as u8 {
|
|
||||||
panic!("=)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExitKind::Ok
|
|
||||||
};
|
|
||||||
// To test the panic:
|
|
||||||
let input = BytesInput::new(Vec::from("abc"));
|
|
||||||
#[cfg(feature = "panic")]
|
|
||||||
harness(&input);
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "baby_fuzzer_listing_04"
|
|
||||||
version = "0.14.1"
|
|
||||||
authors = ["Your Name <you@example.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libafl = { path = "path/to/libafl/" }
|
|
||||||
libafl_bolts = { path = "path/to/libafl_bolts/" }
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
panic = "abort"
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
opt-level = 3
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
panic = []
|
|
@ -1,86 +0,0 @@
|
|||||||
/* ANCHOR: use */
|
|
||||||
extern crate libafl;
|
|
||||||
extern crate libafl_bolts;
|
|
||||||
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use libafl::{
|
|
||||||
corpus::{InMemoryCorpus, OnDiskCorpus},
|
|
||||||
events::SimpleEventManager,
|
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
|
||||||
fuzzer::StdFuzzer,
|
|
||||||
generators::RandPrintablesGenerator,
|
|
||||||
inputs::{BytesInput, HasTargetBytes},
|
|
||||||
monitors::SimpleMonitor,
|
|
||||||
schedulers::QueueScheduler,
|
|
||||||
state::StdState,
|
|
||||||
};
|
|
||||||
use libafl_bolts::{rands::StdRand, tuples::tuple_list, AsSlice, nonzero};
|
|
||||||
/* ANCHOR_END: use */
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let mut harness = |input: &BytesInput| {
|
|
||||||
let target = input.target_bytes();
|
|
||||||
let buf = target.as_slice();
|
|
||||||
if buf.len() > 0 && buf[0] == 'a' as u8 {
|
|
||||||
if buf.len() > 1 && buf[1] == 'b' as u8 {
|
|
||||||
if buf.len() > 2 && buf[2] == 'c' as u8 {
|
|
||||||
panic!("=)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExitKind::Ok
|
|
||||||
};
|
|
||||||
// To test the panic:
|
|
||||||
let input = BytesInput::new(Vec::from("abc"));
|
|
||||||
#[cfg(feature = "panic")]
|
|
||||||
harness(&input);
|
|
||||||
|
|
||||||
/* ANCHOR: state */
|
|
||||||
// create a State from scratch
|
|
||||||
let mut state = StdState::new(
|
|
||||||
// RNG
|
|
||||||
StdRand::new(),
|
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
|
||||||
InMemoryCorpus::new(),
|
|
||||||
// Corpus in which we store solutions (crashes in this example),
|
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
|
||||||
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
|
||||||
&mut (),
|
|
||||||
&mut (),
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
/* ANCHOR_END: state */
|
|
||||||
|
|
||||||
/* ANCHOR: event_manager */
|
|
||||||
// The Monitor trait defines how the fuzzer stats are displayed to the user
|
|
||||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
|
||||||
|
|
||||||
// The event manager handles the various events generated during the fuzzing loop
|
|
||||||
// such as the notification of the addition of a new item to the corpus
|
|
||||||
let mut mgr = SimpleEventManager::new(mon);
|
|
||||||
/* ANCHOR_END: event_manager */
|
|
||||||
|
|
||||||
/* ANCHOR: scheduler_fuzzer */
|
|
||||||
// A queue policy to get testcases from the corpus
|
|
||||||
let scheduler = QueueScheduler::new();
|
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, (), ());
|
|
||||||
/* ANCHOR_END: scheduler_fuzzer */
|
|
||||||
|
|
||||||
/* ANCHOR: executor */
|
|
||||||
// Create the executor for an in-process function
|
|
||||||
let mut executor = InProcessExecutor::new(&mut harness, (), &mut fuzzer, &mut state, &mut mgr)
|
|
||||||
.expect("Failed to create the Executor");
|
|
||||||
/* ANCHOR_END: executor */
|
|
||||||
|
|
||||||
/* ANCHOR: generator */
|
|
||||||
// Generator of printable bytearrays of max size 32
|
|
||||||
let mut generator = RandPrintablesGenerator::new(nonzero!(32));
|
|
||||||
|
|
||||||
// Generate 8 initial inputs
|
|
||||||
state
|
|
||||||
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
|
|
||||||
.expect("Failed to generate the initial corpus");
|
|
||||||
/* ANCHOR_END: generator */
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "baby_fuzzer_listing_05"
|
|
||||||
version = "0.14.1"
|
|
||||||
authors = ["Your Name <you@example.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libafl = { path = "path/to/libafl/" }
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
panic = "abort"
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
opt-level = 3
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
panic = []
|
|
@ -1,116 +0,0 @@
|
|||||||
/* ANCHOR: use */
|
|
||||||
extern crate libafl;
|
|
||||||
extern crate libafl_bolts;
|
|
||||||
|
|
||||||
use libafl::{
|
|
||||||
corpus::{InMemoryCorpus, OnDiskCorpus},
|
|
||||||
events::SimpleEventManager,
|
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
|
||||||
fuzzer::StdFuzzer,
|
|
||||||
generators::RandPrintablesGenerator,
|
|
||||||
inputs::{BytesInput, HasTargetBytes},
|
|
||||||
monitors::SimpleMonitor,
|
|
||||||
observers::StdMapObserver,
|
|
||||||
schedulers::QueueScheduler,
|
|
||||||
state::StdState,
|
|
||||||
};
|
|
||||||
use libafl_bolts::{rands::StdRand, tuples::tuple_list, AsSlice, nonzero};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
/* ANCHOR_END: use */
|
|
||||||
|
|
||||||
/* ANCHOR: signals */
|
|
||||||
// Coverage map with explicit assignments due to the lack of instrumentation
|
|
||||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
|
||||||
|
|
||||||
fn signals_set(idx: usize) {
|
|
||||||
unsafe { SIGNALS[idx] = 1 };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// The closure that we want to fuzz
|
|
||||||
let mut harness = |input: &BytesInput| {
|
|
||||||
let target = input.target_bytes();
|
|
||||||
let buf = target.as_slice();
|
|
||||||
signals_set(0); // set SIGNALS[0]
|
|
||||||
if buf.len() > 0 && buf[0] == 'a' as u8 {
|
|
||||||
signals_set(1); // set SIGNALS[1]
|
|
||||||
if buf.len() > 1 && buf[1] == 'b' as u8 {
|
|
||||||
signals_set(2); // set SIGNALS[2]
|
|
||||||
if buf.len() > 2 && buf[2] == 'c' as u8 {
|
|
||||||
panic!("=)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExitKind::Ok
|
|
||||||
};
|
|
||||||
/* ANCHOR_END: signals */
|
|
||||||
// To test the panic:
|
|
||||||
let input = BytesInput::new(Vec::from("abc"));
|
|
||||||
#[cfg(feature = "panic")]
|
|
||||||
harness(&input);
|
|
||||||
|
|
||||||
/* ANCHOR: observer */
|
|
||||||
// Create an observation channel using the signals map
|
|
||||||
let observer = unsafe { StdMapObserver::new("signals", &mut SIGNALS) };
|
|
||||||
/* ANCHOR_END: observer */
|
|
||||||
|
|
||||||
/* ANCHOR: state_with_feedback_and_objective */
|
|
||||||
// Feedback to rate the interestingness of an input
|
|
||||||
let mut feedback = MaxMapFeedback::new(&observer);
|
|
||||||
|
|
||||||
// A feedback to choose if an input is a solution or not
|
|
||||||
let mut objective = CrashFeedback::new();
|
|
||||||
|
|
||||||
// create a State from scratch
|
|
||||||
let mut state = StdState::new(
|
|
||||||
// RNG
|
|
||||||
StdRand::new(),
|
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
|
||||||
InMemoryCorpus::new(),
|
|
||||||
// Corpus in which we store solutions (crashes in this example),
|
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
|
||||||
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
|
||||||
&mut feedback,
|
|
||||||
&mut objective,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
/* ANCHOR_END: state_with_feedback_and_objective */
|
|
||||||
|
|
||||||
// The Monitor trait defines how the fuzzer stats are displayed to the user
|
|
||||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
|
||||||
|
|
||||||
// The event manager handles the various events generated during the fuzzing loop
|
|
||||||
// such as the notification of the addition of a new item to the corpus
|
|
||||||
let mut mgr = SimpleEventManager::new(mon);
|
|
||||||
|
|
||||||
// A queue policy to get testcasess from the corpus
|
|
||||||
let scheduler = QueueScheduler::new();
|
|
||||||
/* ANCHOR: state_with_feedback_and_objective */
|
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
|
||||||
/* ANCHOR_END: state_with_feedback_and_objective */
|
|
||||||
|
|
||||||
/* ANCHOR: executor_with_observer */
|
|
||||||
// Create the executor for an in-process function with just one observer
|
|
||||||
let mut executor = InProcessExecutor::new(
|
|
||||||
&mut harness,
|
|
||||||
tuple_list!(observer),
|
|
||||||
&mut fuzzer,
|
|
||||||
&mut state,
|
|
||||||
&mut mgr,
|
|
||||||
)
|
|
||||||
.expect("Failed to create the Executor");
|
|
||||||
/* ANCHOR_END: executor_with_observer */
|
|
||||||
|
|
||||||
// Generator of printable bytearrays of max size 32
|
|
||||||
let mut generator = RandPrintablesGenerator::new(nonzero!(32));
|
|
||||||
|
|
||||||
// Generate 8 initial inputs
|
|
||||||
state
|
|
||||||
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
|
|
||||||
.expect("Failed to generate the initial corpus");
|
|
||||||
/* ANCHOR: signals */
|
|
||||||
}
|
|
||||||
/* ANCHOR_END: signals */
|
|
@ -1,24 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "baby_fuzzer_listing_06"
|
|
||||||
version = "0.14.1"
|
|
||||||
authors = ["Your Name <you@example.com>"]
|
|
||||||
edition = "2018"
|
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
libafl = { path = "path/to/libafl/" }
|
|
||||||
libafl_bolts = { path = "path/to/libafl_bolts/" }
|
|
||||||
|
|
||||||
[profile.dev]
|
|
||||||
panic = "abort"
|
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
panic = "abort"
|
|
||||||
lto = true
|
|
||||||
codegen-units = 1
|
|
||||||
opt-level = 3
|
|
||||||
debug = true
|
|
||||||
|
|
||||||
[features]
|
|
||||||
panic = []
|
|
@ -1,117 +0,0 @@
|
|||||||
/* ANCHOR: use */
|
|
||||||
extern crate libafl;
|
|
||||||
extern crate libafl_bolts;
|
|
||||||
use std::num::NonZeroUsize;
|
|
||||||
|
|
||||||
use libafl::{
|
|
||||||
corpus::{InMemoryCorpus, OnDiskCorpus},
|
|
||||||
events::SimpleEventManager,
|
|
||||||
executors::{inprocess::InProcessExecutor, ExitKind},
|
|
||||||
feedbacks::{CrashFeedback, MaxMapFeedback},
|
|
||||||
fuzzer::{Fuzzer, StdFuzzer},
|
|
||||||
generators::RandPrintablesGenerator,
|
|
||||||
inputs::{BytesInput, HasTargetBytes},
|
|
||||||
monitors::SimpleMonitor,
|
|
||||||
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
|
||||||
observers::StdMapObserver,
|
|
||||||
schedulers::QueueScheduler,
|
|
||||||
stages::mutational::StdMutationalStage,
|
|
||||||
state::StdState,
|
|
||||||
};
|
|
||||||
use libafl_bolts::{rands::StdRand, tuples::tuple_list, AsSlice, nonzero};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
/* ANCHOR_END: use */
|
|
||||||
|
|
||||||
// Coverage map with explicit assignments due to the lack of instrumentation
|
|
||||||
static mut SIGNALS: [u8; 16] = [0; 16];
|
|
||||||
|
|
||||||
fn signals_set(idx: usize) {
|
|
||||||
unsafe { SIGNALS[idx] = 1 };
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
// The closure that we want to fuzz
|
|
||||||
let mut harness = |input: &BytesInput| {
|
|
||||||
let target = input.target_bytes();
|
|
||||||
let buf = target.as_slice();
|
|
||||||
signals_set(0); // set SIGNALS[0]
|
|
||||||
if buf.len() > 0 && buf[0] == 'a' as u8 {
|
|
||||||
signals_set(1); // set SIGNALS[1]
|
|
||||||
if buf.len() > 1 && buf[1] == 'b' as u8 {
|
|
||||||
signals_set(2); // set SIGNALS[2]
|
|
||||||
if buf.len() > 2 && buf[2] == 'c' as u8 {
|
|
||||||
panic!("=)");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ExitKind::Ok
|
|
||||||
};
|
|
||||||
// To test the panic:
|
|
||||||
let input = BytesInput::new(Vec::from("abc"));
|
|
||||||
#[cfg(feature = "panic")]
|
|
||||||
harness(&input);
|
|
||||||
|
|
||||||
// Create an observation channel using the signals map
|
|
||||||
let observer = unsafe { StdMapObserver::new("signals", &mut SIGNALS) };
|
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
|
||||||
let mut feedback = MaxMapFeedback::new(&observer);
|
|
||||||
|
|
||||||
// A feedback to choose if an input is a solution or not
|
|
||||||
let mut objective = CrashFeedback::new();
|
|
||||||
|
|
||||||
// create a State from scratch
|
|
||||||
let mut state = StdState::new(
|
|
||||||
// RNG
|
|
||||||
StdRand::new(),
|
|
||||||
// Corpus that will be evolved, we keep it in memory for performance
|
|
||||||
InMemoryCorpus::new(),
|
|
||||||
// Corpus in which we store solutions (crashes in this example),
|
|
||||||
// on disk so the user can get them after stopping the fuzzer
|
|
||||||
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
|
||||||
&mut feedback,
|
|
||||||
&mut objective,
|
|
||||||
)
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
// The Monitor trait defines how the fuzzer stats are displayed to the user
|
|
||||||
let mon = SimpleMonitor::new(|s| println!("{s}"));
|
|
||||||
|
|
||||||
// The event manager handles the various events generated during the fuzzing loop
|
|
||||||
// such as the notification of the addition of a new item to the corpus
|
|
||||||
let mut mgr = SimpleEventManager::new(mon);
|
|
||||||
|
|
||||||
// A queue policy to get testcasess from the corpus
|
|
||||||
let scheduler = QueueScheduler::new();
|
|
||||||
|
|
||||||
// A fuzzer with feedbacks and a corpus scheduler
|
|
||||||
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
|
||||||
|
|
||||||
// Create the executor for an in-process function with just one observer
|
|
||||||
let mut executor = InProcessExecutor::new(
|
|
||||||
&mut harness,
|
|
||||||
tuple_list!(observer),
|
|
||||||
&mut fuzzer,
|
|
||||||
&mut state,
|
|
||||||
&mut mgr,
|
|
||||||
)
|
|
||||||
.expect("Failed to create the Executor");
|
|
||||||
|
|
||||||
// Generator of printable bytearrays of max size 32
|
|
||||||
let mut generator = RandPrintablesGenerator::new(nonzero!(32));
|
|
||||||
|
|
||||||
// Generate 8 initial inputs
|
|
||||||
state
|
|
||||||
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
|
|
||||||
.expect("Failed to generate the initial corpus");
|
|
||||||
|
|
||||||
/* ANCHOR: mutational_stage */
|
|
||||||
// Setup a mutational stage with a basic bytes mutator
|
|
||||||
let mutator = StdScheduledMutator::new(havoc_mutations());
|
|
||||||
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
|
||||||
|
|
||||||
fuzzer
|
|
||||||
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
|
||||||
.expect("Error in the fuzzing loop");
|
|
||||||
/* ANCHOR_END: mutational_stage */
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
# General debugging tips
|
|
||||||
This file answers some commmon questions that arise when you are writing a fuzzer using LibAFL.
|
|
||||||
|
|
||||||
## Q. My fuzzer crashed but the stack trace is useless.
|
|
||||||
You can enable the `errors_backtrace` feature of the `libafl` crate. With this the stacktrace is meaningful.
|
|
||||||
|
|
||||||
## Q. I started the fuzzer but the corpus count is 0.
|
|
||||||
Unless the initial corpus is loaded with the "load_initial_inputs_forced" function, we only store the interesting inputs, which is the inputs that triggered the feedback. So this usually means that your input was not interesting or your target was simply not properly implemented.
|
|
||||||
Either way, what you can do is attach to the executable with gdb and set a breakpoint at where the new edges should be reported. If no instrumentation code is executed, then the the problem is in the instrumentation. If the instrumentation code is hit, but still the your input is not instrumented, then the problem could be that you are not passign the observer/feedback correctly to the fuzzer.
|
|
||||||
|
|
||||||
## Q. I started the fuzzer but the coverage is 0.
|
|
||||||
This could mean two things. Perhaps your target was not properly instrumented, or you are not using the correct observer, feedback feature.
|
|
||||||
In this case, again, what usually should do is to run the fuzzer with gdb and set a breakpoint at where the coverage is recorded (e.g. __sanitizer_coverage_trace_pcguard), and validate that the target is giving the feedback to the fuzzer.
|
|
||||||
|
|
||||||
## Q. I started the fuzzer but there's no output.
|
|
||||||
First, verify that your stdout and stderr are not redirected to `/dev/null`. If you get the log, then it should either fall into the previous 2 cases. Either the fuzzer crashed because you didn't have the initial seeds, or the coverage feedback is not working.
|
|
||||||
|
|
||||||
## Q. My fuzzer is slow.
|
|
||||||
Try running the fuzzer with the `introspection` feature of the `libafl`. This will show how much time is spent on each module of your fuzzer. Also you might be using a wrong size of the coverage map. If you see `2621440` for the size of the coverage map, you are doing it wrong. One possible mistake is the misuse of `libafl_targets::coverage::EDGES_MAP`
|
|
||||||
```
|
|
||||||
let map = StdMapObserver::from_mut_ptr("edges", EDGES_MAP.as_mut_ptr(), EDGES_MAP.len());
|
|
||||||
```
|
|
||||||
You should *never* use the `EDGES_MAP`'s size as this is just the size of the allocated size of the coverage map. Consider using something smaller or our default value `libafl_targets::LIBAFL_EDGES_MAP_DEFAULT_SIZE`.
|
|
||||||
|
|
||||||
## Q. I still have problems with my fuzzer.
|
|
||||||
Finally, if you really have no idea what is going on, run your fuzzer with logging enabled. (You can use `env_logger`, `SimpleStdoutLogger`, `SimpleStderrLogger` from `libafl_bolts`. `fuzzbench_text` has an example to show how to use it.) (Don't forget to enable stdout and stderr), and you can open an issue or ask us in Discord.
|
|
||||||
|
|
||||||
## Q. My fuzzer died of ``Storing state in crashed fuzzer instance did not work''.
|
|
||||||
If the exit code is zero, then this is because either your harness exited or you are using fuzzer_loop_for and forgot to add `mgr.on_restart` at the end of the fuzzer. In the first case, you should patch your harness not to exit. (or use `utils/deexit`).
|
|
||||||
|
|
||||||
## Q. I can't leave the TUI screen
|
|
||||||
Type `q` then you leave TUI.
|
|
@ -25,7 +25,6 @@
|
|||||||
- [Architecture](./design/architecture.md)
|
- [Architecture](./design/architecture.md)
|
||||||
- [Metadata](./design/metadata.md)
|
- [Metadata](./design/metadata.md)
|
||||||
- [Migrating from LibAFL <0.9 to 0.9](./design/migration-0.9.md)
|
- [Migrating from LibAFL <0.9 to 0.9](./design/migration-0.9.md)
|
||||||
- [Migrating from LibAFL <0.11 to 0.11](./design/migration-0.11.md)
|
|
||||||
|
|
||||||
- [Message Passing](./message_passing/message_passing.md)
|
- [Message Passing](./message_passing/message_passing.md)
|
||||||
- [Spawning Instances](./message_passing/spawn_instances.md)
|
- [Spawning Instances](./message_passing/spawn_instances.md)
|
||||||
|
@ -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
|
||||||
|
|
||||||
@ -115,7 +114,7 @@ The `symcc_runtime` crate supports this use case and runtimes built with `symcc_
|
|||||||
|
|
||||||
## Hybrid Fuzzing in LibAFL
|
## Hybrid Fuzzing in LibAFL
|
||||||
|
|
||||||
The LibAFL repository contains an [example hybrid fuzzer](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/structure_aware/libfuzzer_stb_image_concolic).
|
The LibAFL repository contains an [example hybrid fuzzer](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/libfuzzer_stb_image_concolic).
|
||||||
|
|
||||||
There are three main steps involved with building a hybrid fuzzer using LibAFL:
|
There are three main steps involved with building a hybrid fuzzer using LibAFL:
|
||||||
|
|
||||||
@ -124,23 +123,21 @@ 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
|
||||||
|
|
||||||
Building a custom runtime can be done easily using the `symcc_runtime` crate.
|
Building a custom runtime can be done easily using the `symcc_runtime` crate.
|
||||||
Note, that a custom runtime is a separate shared object file, which means that we need a separate crate for our runtime.
|
Note, that a custom runtime is a separate shared object file, which means that we need a separate crate for our runtime.
|
||||||
Check out the [example hybrid fuzzer's runtime](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/structure_aware/libfuzzer_stb_image_concolic/runtime) and the [`symcc_runtime` docs](https://docs.rs/symcc_runtime/0.1/symcc_runtime) for inspiration.
|
Check out the [example hybrid fuzzer's runtime](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/libfuzzer_stb_image_concolic/runtime) and the [`symcc_runtime` docs](https://docs.rs/symcc_runtime/0.1/symcc_runtime) for inspiration.
|
||||||
|
|
||||||
### 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.
|
||||||
|
|
||||||
@ -151,7 +148,7 @@ How exactly this is done does not matter.
|
|||||||
However, the SymCC compiler needs to be made aware of the location of the runtime that it should instrument against.
|
However, the SymCC compiler needs to be made aware of the location of the runtime that it should instrument against.
|
||||||
This is done by setting the `SYMCC_RUNTIME_DIR` environment variable to the directory which contains the runtime (typically the `target/(debug|release)` folder of your runtime crate).
|
This is done by setting the `SYMCC_RUNTIME_DIR` environment variable to the directory which contains the runtime (typically the `target/(debug|release)` folder of your runtime crate).
|
||||||
|
|
||||||
The example hybrid fuzzer instruments the target in its [`build.rs` build script](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/build.rs#L50).
|
The example hybrid fuzzer instruments the target in its [`build.rs` build script](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/libfuzzer_stb_image_concolic/fuzzer/build.rs#L50).
|
||||||
It does this by cloning and building a copy of SymCC and then using this version to instrument the target.
|
It does this by cloning and building a copy of SymCC and then using this version to instrument the target.
|
||||||
The [`symcc_libafl` crate](https://docs.rs/symcc_libafl) contains helper functions for cloning and building SymCC.
|
The [`symcc_libafl` crate](https://docs.rs/symcc_libafl) contains helper functions for cloning and building SymCC.
|
||||||
|
|
||||||
@ -161,27 +158,27 @@ 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/latest/libafl/executors/command/struct.CommandExecutor.html) to execute your target ([example](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/structure_aware/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/latest/libafl/observers/concolic/struct.ConcolicObserver.html) wrapped in a [`ConcolicTracingStage`](https://docs.rs/libafl/latest/libafl/stages/concolic/struct.ConcolicTracingStage.html), which will attach a [`ConcolicMetadata`](https://docs.rs/libafl/latest/libafl/observers/concolic/struct.ConcolicMetadata.html) to every [`TestCase`](https://docs.rs/libafl/latest/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/latest/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.
|
||||||
|
|
||||||
### Example
|
### Example
|
||||||
|
|
||||||
The example fuzzer shows how to use the [`ConcolicTracingStage` together with the `SimpleConcolicMutationalStage`](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/structure_aware/libfuzzer_stb_image_concolic/fuzzer/src/main.rs#L222) to build a basic hybrid fuzzer.
|
The example fuzzer shows how to use the [`ConcolicTracingStage` together with the `SimpleConcolicMutationalStage`](https://github.com/AFLplusplus/LibAFL/blob/main/fuzzers/libfuzzer_stb_image_concolic/fuzzer/src/main.rs#L222) to build a basic hybrid fuzzer.
|
||||||
|
@ -4,7 +4,7 @@ LibAFL supports different instrumentation engines for binary-only fuzzing.
|
|||||||
A potent cross-platform (Windows, MacOS, Android, Linux, iOS) option for binary-only fuzzing is Frida; the dynamic instrumentation tool.
|
A potent cross-platform (Windows, MacOS, Android, Linux, iOS) option for binary-only fuzzing is Frida; the dynamic instrumentation tool.
|
||||||
|
|
||||||
In this section, we will talk about the components in fuzzing with `libafl_frida`.
|
In this section, we will talk about the components in fuzzing with `libafl_frida`.
|
||||||
You can take a look at a working example in our [`fuzzers/binary_only/frida_libpng`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/binary_only/frida_libpng) folder for Linux, and [`fuzzers/binary_only/frida_windows_gdiplus`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/binary_only/frida_windows_gdiplus) for Windows.
|
You can take a look at a working example in our [`fuzzers/frida_libpng`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/frida_libpng) folder for Linux, and [`fuzzers/frida_gdiplus`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/frida_gdiplus) for Windows.
|
||||||
|
|
||||||
## Dependencies
|
## Dependencies
|
||||||
|
|
||||||
@ -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,
|
||||||
));
|
));
|
||||||
```
|
```
|
||||||
@ -73,7 +73,7 @@ You can then link this observer to `FridaInProcessExecutor` as follows:
|
|||||||
tuple_list!(
|
tuple_list!(
|
||||||
edges_observer,
|
edges_observer,
|
||||||
time_observer,
|
time_observer,
|
||||||
AsanErrorsObserver::from_static_asan_errors()
|
AsanErrorsObserver::new(&ASAN_ERRORS)
|
||||||
),
|
),
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
@ -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/binary_only`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/binary_only/) 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.
|
@ -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
|
||||||
@ -37,4 +37,4 @@ pub extern "C" fn external_current_millis() -> u64 {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
See [./fuzzers/fuzz_anything/baby_no_std](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/fuzz_anything/baby_no_std) for an example.
|
See [./fuzzers/baby_no_std](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/baby_no_std) for an example.
|
||||||
|
@ -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,11 +20,11 @@ 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/full_system/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):
|
||||||
|
|
||||||
the parameter's meaning is listed below:
|
the parameter's meaning is listed below:
|
||||||
|
|
||||||
@ -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/full_system/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/full_system/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()
|
||||||
@ -118,6 +121,6 @@ match Launcher::builder()
|
|||||||
{
|
{
|
||||||
Ok(()) => (),
|
Ok(()) => (),
|
||||||
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
Err(Error::ShuttingDown) => println!("Fuzzing stopped by user. Good bye."),
|
||||||
Err(err) => panic!("Failed to run launcher: {err:?}"),
|
Err(err) => panic!("Failed to run launcher: {:?}", err),
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -6,7 +6,7 @@ While the following chapters discuss the components of LibAFL in detail, here we
|
|||||||
|
|
||||||
We are going to fuzz a simple Rust function that panics under a condition. The fuzzer will be single-threaded and will stop after the crash, just like libFuzzer normally does.
|
We are going to fuzz a simple Rust function that panics under a condition. The fuzzer will be single-threaded and will stop after the crash, just like libFuzzer normally does.
|
||||||
|
|
||||||
You can find a complete version of this tutorial as an example fuzzer in [`fuzzers/baby/baby_fuzzer`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/baby/baby_fuzzer).
|
You can find a complete version of this tutorial as an example fuzzer in [`fuzzers/baby_fuzzer`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/baby_fuzzer).
|
||||||
|
|
||||||
> ### Warning
|
> ### Warning
|
||||||
>
|
>
|
||||||
@ -17,7 +17,7 @@ You can find a complete version of this tutorial as an example fuzzer in [`fuzze
|
|||||||
|
|
||||||
We use cargo to create a new Rust project with LibAFL as a dependency.
|
We use cargo to create a new Rust project with LibAFL as a dependency.
|
||||||
|
|
||||||
```console
|
```sh
|
||||||
$ cargo new baby_fuzzer
|
$ cargo new baby_fuzzer
|
||||||
$ cd baby_fuzzer
|
$ cd baby_fuzzer
|
||||||
```
|
```
|
||||||
@ -25,11 +25,18 @@ $ cd baby_fuzzer
|
|||||||
The generated `Cargo.toml` looks like the following:
|
The generated `Cargo.toml` looks like the following:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
{{#include ../../listings/baby_fuzzer/listing-01/Cargo.toml}}
|
[package]
|
||||||
|
name = "baby_fuzzer"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Your Name <you@example.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
```
|
```
|
||||||
|
|
||||||
In order to use LibAFl we must add it as dependency adding `libafl = { path = "path/to/libafl/" }` under `[dependencies]`.
|
In order to use LibAFl we must add it as dependency adding `libafl = { path = "path/to/libafl/" }` under `[dependencies]`.
|
||||||
That path actually needs to point to the `libafl` directory within the cloned repo, not the root of the repo itself.
|
|
||||||
You can use the LibAFL version from [crates.io](https://crates.io/crates/libafl) if you want, in this case, you have to use `libafl = "*"` to get the latest version (or set it to the current version).
|
You can use the LibAFL version from [crates.io](https://crates.io/crates/libafl) if you want, in this case, you have to use `libafl = "*"` to get the latest version (or set it to the current version).
|
||||||
|
|
||||||
As we are going to fuzz Rust code, we want that a panic does not simply cause the program to exit, but raise an `abort` that can then be caught by the fuzzer.
|
As we are going to fuzz Rust code, we want that a panic does not simply cause the program to exit, but raise an `abort` that can then be caught by the fuzzer.
|
||||||
@ -40,9 +47,27 @@ Alongside this setting, we add some optimization flags for the compilation, when
|
|||||||
The final `Cargo.toml` should look similar to the following:
|
The final `Cargo.toml` should look similar to the following:
|
||||||
|
|
||||||
```toml
|
```toml
|
||||||
{{#include ../../listings/baby_fuzzer/listing-02/Cargo.toml}}
|
[package]
|
||||||
```
|
name = "baby_fuzzer"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Your Name <you@example.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libafl = { path = "path/to/libafl/" }
|
||||||
|
|
||||||
|
[profile.dev]
|
||||||
|
panic = "abort"
|
||||||
|
|
||||||
|
[profile.release]
|
||||||
|
panic = "abort"
|
||||||
|
lto = true
|
||||||
|
codegen-units = 1
|
||||||
|
opt-level = 3
|
||||||
|
debug = true
|
||||||
|
```
|
||||||
|
|
||||||
## The function under test
|
## The function under test
|
||||||
|
|
||||||
@ -51,32 +76,52 @@ To start, we create the closure that we want to fuzz. It takes a buffer as input
|
|||||||
`ExitKind` is used to inform the fuzzer about the harness' exit status.
|
`ExitKind` is used to inform the fuzzer about the harness' exit status.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-03/src/main.rs}}
|
extern crate libafl;
|
||||||
|
use libafl::{
|
||||||
|
bolts::AsSlice,
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
executors::ExitKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main(){
|
||||||
|
let mut harness = |input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let buf = target.as_slice();
|
||||||
|
if buf.len() > 0 && buf[0] == 'a' as u8 {
|
||||||
|
if buf.len() > 1 && buf[1] == 'b' as u8 {
|
||||||
|
if buf.len() > 2 && buf[2] == 'c' as u8 {
|
||||||
|
panic!("=)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
|
// To test the panic:
|
||||||
|
let input = BytesInput::new(Vec::from("abc"));
|
||||||
|
#[cfg(feature = "panic")]
|
||||||
|
harness(&input);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
To test the crash manually, you can add a feature in `Cargo.toml` that enables the call that triggers the panic:
|
|
||||||
|
|
||||||
```toml
|
|
||||||
{{#include ../../listings/baby_fuzzer/listing-03/Cargo.toml:23:25}}
|
|
||||||
```
|
|
||||||
|
|
||||||
And then run the program with that feature activated:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ cargo run -F panic
|
|
||||||
```
|
|
||||||
|
|
||||||
And you should see the program crash as expected.
|
|
||||||
|
|
||||||
## Generating and running some tests
|
## 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
|
// create a State from scratch
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:state}}
|
let mut state = StdState::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
InMemoryCorpus::new(),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
||||||
|
&mut (),
|
||||||
|
&mut ()
|
||||||
|
).unwrap();
|
||||||
```
|
```
|
||||||
|
|
||||||
- The first parameter is a random number generator, that is part of the fuzzer state, in this case, we use the default one `StdRand`, but you can choose a different one. We seed it with the current nanoseconds.
|
- The first parameter is a random number generator, that is part of the fuzzer state, in this case, we use the default one `StdRand`, but you can choose a different one. We seed it with the current nanoseconds.
|
||||||
@ -84,26 +129,43 @@ In our `main` we create a basic State instance like the following:
|
|||||||
|
|
||||||
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.
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:event_manager}}
|
// The Monitor trait defines how the fuzzer stats are displayed to the user
|
||||||
|
let mon = SimpleMonitor::new(|s| println!("{}", s));
|
||||||
|
|
||||||
|
// The event manager handle the various events generated during the fuzzing loop
|
||||||
|
// such as the notification of the addition of a new item to the corpus
|
||||||
|
let mut mgr = SimpleEventManager::new(mon);
|
||||||
```
|
```
|
||||||
|
|
||||||
In addition, we have the **Fuzzer**, an entity that contains some actions that alter the State. One of these actions is the scheduling of the testcases to the fuzzer using a **Scheduler**.
|
In addition, we have the **Fuzzer**, an entity that contains some actions that alter the State. One of these actions is the scheduling of the testcases to the fuzzer using a **Scheduler**.
|
||||||
We create it as `QueueScheduler`, a scheduler that serves testcases to the fuzzer in a FIFO fashion.
|
We create it as `QueueScheduler`, a scheduler that serves testcases to the fuzzer in a FIFO fashion.
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:scheduler_fuzzer}}
|
// A queue policy to get testcasess from the corpus
|
||||||
|
let scheduler = QueueScheduler::new();
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, (), ());
|
||||||
```
|
```
|
||||||
|
|
||||||
Last but not least, we need an **Executor** that is the entity responsible to run our program under test. In this example, we want to run the harness function in-process (without forking off a child, for example), and so we use the `InProcessExecutor`.
|
Last but not least, we need an **Executor** that is the entity responsible to run our program under test. In this example, we want to run the harness function in-process (without forking off a child, for example), and so we use the `InProcessExecutor`.
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:executor}}
|
// Create the executor for an in-process function
|
||||||
|
let mut executor = InProcessExecutor::new(
|
||||||
|
&mut harness,
|
||||||
|
(),
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
)
|
||||||
|
.expect("Failed to create the Executor");
|
||||||
```
|
```
|
||||||
|
|
||||||
It takes a reference to the harness, the state, and the event manager. We will discuss the second parameter later.
|
It takes a reference to the harness, the state, and the event manager. We will discuss the second parameter later.
|
||||||
@ -113,19 +175,41 @@ Now we have the 4 major entities ready for running our tests, but we still canno
|
|||||||
|
|
||||||
For this purpose, we use a **Generator**, `RandPrintablesGenerator` that generates a string of printable bytes.
|
For this purpose, we use a **Generator**, `RandPrintablesGenerator` that generates a string of printable bytes.
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:generator}}
|
use libafl::generators::RandPrintablesGenerator;
|
||||||
|
|
||||||
|
// Generator of printable bytearrays of max size 32
|
||||||
|
let mut generator = RandPrintablesGenerator::new(32);
|
||||||
|
|
||||||
|
// Generate 8 initial inputs
|
||||||
|
state
|
||||||
|
.generate_initial_inputs(&mut fuzzer, &mut executor, &mut generator, &mut mgr, 8)
|
||||||
|
.expect("Failed to generate the initial corpus".into());
|
||||||
```
|
```
|
||||||
|
|
||||||
Now you can prepend the necessary `use` directives to your main.rs and compile the fuzzer.
|
Now you can prepend the necessary `use` directives to your main.rs and compile the fuzzer.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-04/src/main.rs:use}}
|
extern crate libafl;
|
||||||
|
|
||||||
|
use std::path::PathBuf;
|
||||||
|
use libafl::{
|
||||||
|
bolts::{AsSlice, current_nanos, rands::StdRand},
|
||||||
|
corpus::{InMemoryCorpus, OnDiskCorpus},
|
||||||
|
events::SimpleEventManager,
|
||||||
|
executors::{inprocess::InProcessExecutor, ExitKind},
|
||||||
|
fuzzer::StdFuzzer,
|
||||||
|
generators::RandPrintablesGenerator,
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
monitors::SimpleMonitor,
|
||||||
|
schedulers::QueueScheduler,
|
||||||
|
state::StdState,
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
When running, you should see something similar to:
|
When running, you should see something similar to:
|
||||||
|
|
||||||
```console
|
```sh
|
||||||
$ cargo run
|
$ cargo run
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
|
Finished dev [unoptimized + debuginfo] target(s) in 0.04s
|
||||||
Running `target/debug/baby_fuzzer`
|
Running `target/debug/baby_fuzzer`
|
||||||
@ -141,22 +225,60 @@ 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
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:signals}}
|
extern crate libafl;
|
||||||
|
use libafl::{
|
||||||
|
bolts::AsSlice,
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
executors::ExitKind,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Coverage map with explicit assignments due to the lack of instrumentation
|
||||||
|
static mut SIGNALS: [u8; 16] = [0; 16];
|
||||||
|
|
||||||
|
fn signals_set(idx: usize) {
|
||||||
|
unsafe { SIGNALS[idx] = 1 };
|
||||||
|
}
|
||||||
|
|
||||||
|
// The closure that we want to fuzz
|
||||||
|
let mut harness = |input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let buf = target.as_slice();
|
||||||
|
signals_set(0); // set SIGNALS[0]
|
||||||
|
if buf.len() > 0 && buf[0] == 'a' as u8 {
|
||||||
|
signals_set(1); // set SIGNALS[1]
|
||||||
|
if buf.len() > 1 && buf[1] == 'b' as u8 {
|
||||||
|
signals_set(2); // set SIGNALS[2]
|
||||||
|
if buf.len() > 2 && buf[2] == 'c' as u8 {
|
||||||
|
panic!("=)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ExitKind::Ok
|
||||||
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
The observer can be created directly from the `SIGNALS` map, in the following way:
|
The observer can be created directly from the `SIGNALS` map, in the following way:
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:observer}}
|
// Create an observation channel using the signals map
|
||||||
|
let observer = StdMapObserver::new("signals", unsafe { &mut SIGNALS });
|
||||||
```
|
```
|
||||||
|
|
||||||
The observers are usually kept in the corresponding executor as they keep track of information that is valid for just one run. We have then to modify our InProcessExecutor creation to include the observer as follows:
|
The observers are usually kept in the corresponding executor as they keep track of information that is valid for just one run. We have then to modify our InProcessExecutor creation to include the observer as follows:
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:executor_with_observer}}
|
// Create the executor for an in-process function with just one observer
|
||||||
|
let mut executor = InProcessExecutor::new(
|
||||||
|
&mut harness,
|
||||||
|
tuple_list!(observer),
|
||||||
|
&mut fuzzer,
|
||||||
|
&mut state,
|
||||||
|
&mut mgr,
|
||||||
|
)
|
||||||
|
.expect("Failed to create the Executor".into());
|
||||||
```
|
```
|
||||||
|
|
||||||
Now that the fuzzer can observe which condition is satisfied, we need a way to rate an input as interesting (i.e. worth of addition to the corpus) based on this observation. Here comes the notion of Feedback.
|
Now that the fuzzer can observe which condition is satisfied, we need a way to rate an input as interesting (i.e. worth of addition to the corpus) based on this observation. Here comes the notion of Feedback.
|
||||||
@ -165,23 +287,49 @@ 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:
|
||||||
|
|
||||||
```rust
|
```rust,ignore
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:state_with_feedback_and_objective}}
|
extern crate libafl;
|
||||||
```
|
use libafl::{
|
||||||
|
bolts::{current_nanos, rands::StdRand, tuples::tuple_list},
|
||||||
|
corpus::{InMemoryCorpus, OnDiskCorpus},
|
||||||
|
feedbacks::{MaxMapFeedback, CrashFeedback},
|
||||||
|
fuzzer::StdFuzzer,
|
||||||
|
state::StdState,
|
||||||
|
observers::StdMapObserver,
|
||||||
|
};
|
||||||
|
|
||||||
Once again, you need to add the necessary `use` directives for this to work properly:
|
// Feedback to rate the interestingness of an input
|
||||||
|
let mut feedback = MaxMapFeedback::new(&observer);
|
||||||
|
|
||||||
```rust
|
// A feedback to choose if an input is a solution or not
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-05/src/main.rs:use}}
|
let mut objective = CrashFeedback::new();
|
||||||
|
|
||||||
|
// create a State from scratch
|
||||||
|
let mut state = StdState::new(
|
||||||
|
// RNG
|
||||||
|
StdRand::with_seed(current_nanos()),
|
||||||
|
// Corpus that will be evolved, we keep it in memory for performance
|
||||||
|
InMemoryCorpus::new(),
|
||||||
|
// Corpus in which we store solutions (crashes in this example),
|
||||||
|
// on disk so the user can get them after stopping the fuzzer
|
||||||
|
OnDiskCorpus::new(PathBuf::from("./crashes")).unwrap(),
|
||||||
|
&mut feedback,
|
||||||
|
&mut objective
|
||||||
|
).unwrap();
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// A fuzzer with feedbacks and a corpus scheduler
|
||||||
|
let mut fuzzer = StdFuzzer::new(scheduler, feedback, objective);
|
||||||
```
|
```
|
||||||
|
|
||||||
## The actual fuzzing
|
## The actual fuzzing
|
||||||
|
|
||||||
Now, we can run the program, but the outcome is not so different from the previous one as the random generator does not take into account what we save as interesting in the corpus. To do that, we need to plug a Mutator.
|
Now, after including the correct `use`, we can run the program, but the outcome is not so different from the previous one as the random generator does not take into account what we save as interesting in the corpus. To do that, we need to plug a Mutator.
|
||||||
|
|
||||||
**Stages** perform actions on individual inputs, taken from the corpus.
|
**Stages** perform actions on individual inputs, taken from the corpus.
|
||||||
For instance, the `MutationalStage` executes the harness several times in a row, every time with mutated inputs.
|
For instance, the `MutationalStage` executes the harness several times in a row, every time with mutated inputs.
|
||||||
@ -189,20 +337,28 @@ For instance, the `MutationalStage` executes the harness several times in a row,
|
|||||||
As the last step, we create a MutationalStage that uses a mutator inspired by the havoc mutator of AFL.
|
As the last step, we create a MutationalStage that uses a mutator inspired by the havoc mutator of AFL.
|
||||||
|
|
||||||
```rust,ignore
|
```rust,ignore
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-06/src/main.rs:mutational_stage}}
|
use libafl::{
|
||||||
|
mutators::scheduled::{havoc_mutations, StdScheduledMutator},
|
||||||
|
stages::mutational::StdMutationalStage,
|
||||||
|
fuzzer::Fuzzer,
|
||||||
|
};
|
||||||
|
|
||||||
|
// ...
|
||||||
|
|
||||||
|
// Setup a mutational stage with a basic bytes mutator
|
||||||
|
let mutator = StdScheduledMutator::new(havoc_mutations());
|
||||||
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
|
||||||
|
fuzzer
|
||||||
|
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||||
|
.expect("Error in the fuzzing loop");
|
||||||
```
|
```
|
||||||
|
|
||||||
`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.
|
||||||
|
|
||||||
Again, we need to add the new `use` directives:
|
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.
|
||||||
|
|
||||||
```rust,ignore
|
```text
|
||||||
{{#rustdoc_include ../../listings/baby_fuzzer/listing-06/src/main.rs:use}}
|
|
||||||
```
|
|
||||||
|
|
||||||
After adding this code, we have a proper fuzzer, that can run and find the input that panics the function in less than a second.
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ cargo run
|
$ cargo run
|
||||||
Compiling baby_fuzzer v0.1.0 (/home/andrea/Desktop/baby_fuzzer)
|
Compiling baby_fuzzer v0.1.0 (/home/andrea/Desktop/baby_fuzzer)
|
||||||
Finished dev [unoptimized + debuginfo] target(s) in 1.56s
|
Finished dev [unoptimized + debuginfo] target(s) in 1.56s
|
||||||
@ -222,4 +378,4 @@ Bye!
|
|||||||
|
|
||||||
As you can see, after the panic message, the `objectives` count of the log increased by one and you will find the crashing input in `crashes/`.
|
As you can see, after the panic message, the `objectives` count of the log increased by one and you will find the crashing input in `crashes/`.
|
||||||
|
|
||||||
The complete code can be found in [`./fuzzers/baby/baby_fuzzer`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/baby/baby_fuzzer) alongside other `baby_` fuzzers.
|
The complete code can be found in [`./fuzzers/baby_fuzzer`](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/baby_fuzzer) alongside other `baby_` fuzzers.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
# More Examples
|
# More Examples
|
||||||
|
|
||||||
Examples can be found under `./fuzzers/baby`.
|
Examples can be found under `./fuzzer`.
|
||||||
|
|
||||||
|fuzzer name|usage|
|
|fuzzer name|usage|
|
||||||
| ---- | ---- |
|
| ---- | ---- |
|
||||||
@ -9,4 +9,4 @@ Examples can be found under `./fuzzers/baby`.
|
|||||||
| 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|
|
||||||
|
@ -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 Scheduler, 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/latest/libafl/corpus/trait.Corpus.html) and [`Scheduler`](https://docs.rs/libafl/latest/libafl/schedulers/trait.Scheduler.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.
|
||||||
|
@ -9,9 +9,11 @@ So the Executor is for instance responsible to inform the program about the inpu
|
|||||||
|
|
||||||
In our model, it can also hold a set of Observers connected with each execution.
|
In our model, it can also hold a set of Observers connected with each execution.
|
||||||
|
|
||||||
In Rust, we bind this concept to the [`Executor`](https://docs.rs/libafl/latest/libafl/executors/trait.Executor.html) trait. A structure implementing this trait must implement [`HasObservers`](https://docs.rs/libafl/latest/libafl/executors/trait.HasObservers.html) too if wants to hold a set of Observers.
|
In Rust, we bind this concept to the [`Executor`](https://docs.rs/libafl/0/libafl/executors/trait.Executor.html) trait. A structure implementing this trait must implement [`HasObservers`](https://docs.rs/libafl/0/libafl/executors/trait.HasObservers.html) too if wants to hold a set of Observers.
|
||||||
|
|
||||||
By default, we implement some commonly used Executors such as [`InProcessExecutor`](https://docs.rs/libafl/latest/libafl/executors/inprocess/type.InProcessExecutor.html) in which the target is a harness function providing in-process crash detection. Another Executor is the [`ForkserverExecutor`](https://docs.rs/libafl/latest/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 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`.
|
||||||
@ -22,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 AFL/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,
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ As you can see from the forkserver example,
|
|||||||
let mut shmem = StdShMemProvider::new().unwrap().new_shmem(MAP_SIZE).unwrap();
|
let mut shmem = StdShMemProvider::new().unwrap().new_shmem(MAP_SIZE).unwrap();
|
||||||
//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 mut shmem_buf = shmem.as_slice_mut();
|
let mut shmem_buf = shmem.as_mut_slice();
|
||||||
```
|
```
|
||||||
|
|
||||||
Here we make a shared memory region; `shmem`, and write this to environmental variable `__AFL_SHM_ID`. Then the instrumented binary, or the forkserver, finds this shared memory region (from the aforementioned env var) to record its coverage. On your fuzzer side, you can pass this shmem map to your `Observer` to obtain coverage feedbacks combined with any `Feedback`.
|
Here we make a shared memory region; `shmem`, and write this to environmental variable `__AFL_SHM_ID`. Then the instrumented binary, or the forkserver, finds this shared memory region (from the aforementioned env var) to record its coverage. On your fuzzer side, you can pass this shmem map to your `Observer` to obtain coverage feedbacks combined with any `Feedback`.
|
||||||
@ -46,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.
|
||||||
|
|
||||||
@ -57,9 +59,9 @@ On your fuzzer side, you can allocate a shared memory region and make the `EDGES
|
|||||||
```rust,ignore
|
```rust,ignore
|
||||||
let mut shmem;
|
let mut shmem;
|
||||||
unsafe{
|
unsafe{
|
||||||
shmem = StdShMemProvider::new().unwrap().new_shmem(EDGES_MAP_DEFAULT_SIZE).unwrap();
|
shmem = StdShMemProvider::new().unwrap().new_shmem(MAX_EDGES_NUM).unwrap();
|
||||||
}
|
}
|
||||||
let shmem_buf = shmem.as_slice_mut();
|
let shmem_buf = shmem.as_mut_slice();
|
||||||
unsafe{
|
unsafe{
|
||||||
EDGES_PTR = shmem_buf.as_ptr();
|
EDGES_PTR = shmem_buf.as_ptr();
|
||||||
}
|
}
|
||||||
|
@ -10,25 +10,17 @@ 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/latest/libafl/feedbacks/trait.Feedback.html) trait.
|
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.
|
||||||
It is used to implement functors that, given the state of the observers from the last execution, tells if the execution was interesting.
|
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.
|
||||||
So to speak, it reduces the observations to a boolean result of [`is_interesting`](https://docs.rs/libafl/latest/libafl/feedbacks/trait.Feedback.html#tymethod.is_interesting) - or not.
|
|
||||||
For this, a `Feedback` can store anything it wants to persist in the fuzzers's state.
|
|
||||||
This might be, for instance, the cumulative map of all edges seen so far, in the case of a feedback based on edge coverage.
|
|
||||||
This can be achieved by adding `Metadata` in [`init_state`](https://docs.rs/libafl/latest/libafl/feedbacks/trait.Feedback.html#method.init_state) and accessing it later in `is_interesting`.
|
|
||||||
`Feedback` can also add custom metadata to a newly created [`Testcase`](https://docs.rs/libafl/latest/libafl/corpus/testcase/struct.Testcase.html) using [`append_metadata`](https://docs.rs/libafl/latest/libafl/feedbacks/trait.Feedback.html#method.append_metadata).
|
|
||||||
|
|
||||||
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/latest/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/latest/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/latest/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.
|
||||||
Objectives use the same trait as a normal [`Feedback`](https://docs.rs/libafl/latest/libafl/feedbacks/trait.Feedback.html) and the implementations can be used interchangeably.
|
|
||||||
|
|
||||||
The only difference is that `interesting` Objectives won't be mutated further, and are counted as `Solutions`, a successful fuzzing campaign.
|
|
||||||
|
@ -6,4 +6,4 @@ Typically, a random generator is used to generate random inputs.
|
|||||||
|
|
||||||
Generators are traditionally less used in Feedback-driven Fuzzing, but there are exceptions, like Nautilus, that uses a Grammar generator to create the initial corpus and a sub-tree Generator as a mutation of its grammar Mutator.
|
Generators are traditionally less used in Feedback-driven Fuzzing, but there are exceptions, like Nautilus, that uses a Grammar generator to create the initial corpus and a sub-tree Generator as a mutation of its grammar Mutator.
|
||||||
|
|
||||||
In the code, [`Generator`](https://docs.rs/libafl/latest/libafl/generators/trait.Generator.html) is a trait.
|
In the code, [`Generator`](https://docs.rs/libafl/0/libafl/generators/trait.Generator.html) is a trait.
|
||||||
|
@ -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/latest/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/latest/libafl/inputs/gramatron/struct.GramatronInput.html) or `NautilusInput` on Rust nightly), as well as the token-level [EncodedInput](https://docs.rs/libafl/latest/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).
|
||||||
|
@ -1,11 +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.
|
||||||
|
|
||||||
There are also mutators that always produce valid inputs, say a mutator that generates valid JSON or code, but these grammar based mutators need a grammar to work.
|
In LibAFL, [`Mutator`](https://docs.rs/libafl/*/libafl/mutators/trait.Mutator.html) is a trait.
|
||||||
|
|
||||||
In LibAFL, [`Mutator`](https://docs.rs/libafl/latest/libafl/mutators/trait.Mutator.html) is a trait.
|
|
||||||
|
@ -4,10 +4,10 @@ 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/latest/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.
|
||||||
|
|
||||||
In addition to holding the volatile data connected with the last execution of the target, the structures implementing this trait can define some execution hooks that are executed before and after each fuzz case. In these hooks, the observer can modify the fuzzer's state.
|
In addition to holding the volatile data connected with the last execution of the target, the structures implementing this trait can define some execution hooks that are executed before and after each fuzz case. In these hooks, the observer can modify the fuzzer's state.
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
# 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.
|
||||||
|
|
||||||
There are several stages in the LibAFL codebase implementing the [`Stage`](https://docs.rs/libafl/latest/libafl/stages/trait.Stage.html) trait.
|
There are several stages in the LibAFL codebase implementing the [`Stage`](https://docs.rs/libafl/*/libafl/stages/trait.Stage.html) trait.
|
||||||
|
@ -8,8 +8,8 @@ The LibAFL code reuse mechanism is based on components, rather than sub-classes,
|
|||||||
|
|
||||||
Thinking about similar fuzzers, you can observe that most of the time the data structures that are modified are the ones related to testcases and the fuzzer global state.
|
Thinking about similar fuzzers, you can observe that most of the time the data structures that are modified are the ones related to testcases and the fuzzer global state.
|
||||||
|
|
||||||
Beside the entities previously described, we introduce the [`Testcase`](https://docs.rs/libafl/latest/libafl/corpus/testcase/struct.Testcase.html) and [`State`](https://docs.rs/libafl/latest/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/latest/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).
|
||||||
|
@ -5,10 +5,10 @@ A metadata in LibAFL is a self-contained structure that holds associated data to
|
|||||||
In terms of code, a metadata can be defined as a Rust struct registered in the SerdeAny register.
|
In terms of code, a metadata can be defined as a Rust struct registered in the SerdeAny register.
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# extern crate libafl_bolts;
|
extern crate libafl;
|
||||||
# extern crate serde;
|
extern crate serde;
|
||||||
|
|
||||||
use libafl_bolts::SerdeAny;
|
use libafl::SerdeAny;
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, SerdeAny)]
|
#[derive(Debug, Serialize, Deserialize, SerdeAny)]
|
||||||
@ -19,15 +19,15 @@ 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_bolts::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
|
||||||
|
|
||||||
Metadata objects are primarly intended to be used inside [`SerdeAnyMap`](https://docs.rs/libafl_bolts/latest/libafl_bolts/serdeany/serdeany_registry/struct.SerdeAnyMap.html) and [`NamedSerdeAnyMap`](https://docs.rs/libafl_bolts/latest/libafl_bolts/serdeany/serdeany_registry/struct.NamedSerdeAnyMap.html).
|
Metadata objects are primarly intended to be used inside [`SerdeAnyMap`](https://docs.rs/libafl/0.5.0/libafl/bolts/serdeany/serdeany_registry/struct.SerdeAnyMap.html) and [`NamedSerdeAnyMap`](https://docs.rs/libafl/0.5.0/libafl/bolts/serdeany/serdeany_registry/struct.NamedSerdeAnyMap.html).
|
||||||
|
|
||||||
With these maps, the user can retrieve instances by type (and name). Internally, the instances are stored as SerdeAny trait objects.
|
With these maps, the user can retrieve instances by type (and name). Internally, the instances are stored as SerdeAny trait objects.
|
||||||
|
|
||||||
Structs that want to have a set of metadata must implement the [`HasMetadata`](https://docs.rs/libafl/latest/libafl/common/trait.HasMetadata.html) trait.
|
Structs that want to have a set of metadata must implement the [`HasMetadata`](https://docs.rs/libafl/0.5.0/libafl/state/trait.HasMetadata.html) trait.
|
||||||
|
|
||||||
By default, Testcase and State implement it and hold a SerdeAnyMap testcase.
|
By default, Testcase and State implement it and hold a SerdeAnyMap testcase.
|
||||||
|
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
# Migrating from LibAFL <0.11 to 0.11
|
|
||||||
|
|
||||||
We moved the old `libafl::bolts` module to its own crate called `libafl_bolts`.
|
|
||||||
For this, imports for types in LibAFL bolts have changed in version 0.11, everything else should remain the same.
|
|
||||||
|
|
||||||
## Reasons for This Change
|
|
||||||
|
|
||||||
With the change we can now use a lot of low-level features of LibAFL for projects that are unrelated to fuzzing, or just completely different to LibAFL.
|
|
||||||
Some cross-platform things in bolts include
|
|
||||||
|
|
||||||
* SerdeAnyMap: a map that stores and retrieves elements by type and is serializable and deserializable
|
|
||||||
* ShMem: A cross-platform (Windows, Linux, Android, MacOS) shared memory implementation
|
|
||||||
* LLMP: A fast, lock-free IPC mechanism via SharedMap
|
|
||||||
* Core_affinity: A maintained version of `core_affinity` that can be used to get core information and bind processes to cores
|
|
||||||
* Rands: Fast random number generators for fuzzing (like [RomuRand](https://www.romu-random.org/))
|
|
||||||
* MiniBSOD: get and print information about the current process state including important registers.
|
|
||||||
* Tuples: Haskel-like compile-time tuple lists
|
|
||||||
* Os: OS specific stuff like signal handling, windows exception handling, pipes, and helpers for `fork`
|
|
||||||
|
|
||||||
## What changed
|
|
||||||
|
|
||||||
You will need to move all `libafl::bolts::` imports to `libafl_bolts:::` and add the crate dependency in your Cargo.toml (and specify feature flags there).
|
|
||||||
As only exception, the `libafl::bolts::launcher::Launcher` has moved to `libafl::events::launcher::Launcher` since it has fuzzer and `EventManager` specific code.
|
|
||||||
If you are using `prelude`, you may need to also ad `libafl_bolts::prelude`.
|
|
||||||
|
|
||||||
That's it.
|
|
||||||
Enjoy using `libafl_bolts` in other projects.
|
|
@ -1,9 +0,0 @@
|
|||||||
# Migrating from <0.12 to 0.12
|
|
||||||
|
|
||||||
We deleted `TimeoutExecutor` and `TimeoutForkserverExecutor` and make it mandatory for `InProcessExecutor` and `ForkserverExecutor` to have the timeout. Now `InProcessExecutor` and `ForkserverExecutor` have the default timeout of 5 seconds.
|
|
||||||
|
|
||||||
## Reason for This Change.
|
|
||||||
In 99% of the case, it is advised to have the timeout for the fuzzer. This is because we do not want the fuzzer to stop forever just because the target has hit a path that resulted in a infinite-loop.
|
|
||||||
|
|
||||||
## What changed
|
|
||||||
You do not have to wrap the executor with `TimeoutExecutor` anymore. You can just use `InProcessExecutor::new()` to instantiate the executor with the default timeout or use `InProcessExecutor::timeout(duration)` to start the executor with the customized duration of timeout.
|
|
@ -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..EDGES_MAP_DEFAULT_SIZE] };
|
|
||||||
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`.
|
|
||||||
|
@ -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.
|
||||||
|
@ -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)
|
||||||
@ -31,35 +31,20 @@ You can choose the features by using `features = ["feature1", "feature2", ...]`
|
|||||||
Out of this list, by default, `std`, `derive`, and `rand_trait` are already set.
|
Out of this list, by default, `std`, `derive`, and `rand_trait` are already set.
|
||||||
You can choose to disable them by setting `default-features = false` in your `Cargo.toml`.
|
You can choose to disable them by setting `default-features = false` in your `Cargo.toml`.
|
||||||
|
|
||||||
### [`libafl_bolts`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl_bolts)
|
### libafl_sugar
|
||||||
|
|
||||||
The `libafl_bolts` crate is a minimal tool shed filled with useful low-level rust features, not necessarily related to fuzzers.
|
|
||||||
In it, you'll find highlights like:
|
|
||||||
|
|
||||||
- `core_affinity` to bind the current process to cores
|
|
||||||
- `SerdeAnyMap` a map that can store typed values in a serializable fashion
|
|
||||||
- `minibsod` to dump the current process state
|
|
||||||
- `LLMP`, "low level message passing", a lock-free IPC mechanism
|
|
||||||
- `Rand`, different fast (non-cryptographically secure) RNG implementations like RomuRand
|
|
||||||
- `ShMem`, a platform independent shard memory implementation
|
|
||||||
- `Tuples`, a compiletime tuple implementation
|
|
||||||
|
|
||||||
... and much more.
|
|
||||||
|
|
||||||
### [`libafl_sugar`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl_sugar)
|
|
||||||
|
|
||||||
The sugar crate abstracts away most of the complexity of LibAFL's API.
|
The sugar crate abstracts away most of the complexity of LibAFL's API.
|
||||||
Instead of high flexibility, it aims to be high-level and easy-to-use.
|
Instead of high flexibility, it aims to be high-level and easy-to-use.
|
||||||
It is not as flexible as stitching your fuzzer together from each individual component, but allows you to build a fuzzer with minimal lines of code.
|
It is not as flexible as stitching your fuzzer together from each individual component, but allows you to build a fuzzer with minimal lines of code.
|
||||||
To see it in action, take a look at the [`libfuzzer_stb_image_sugar` example fuzzer](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/inprocess/libfuzzer_stb_image_sugar).
|
To see it in action, take a look at the [`libfuzzer_stb_image_sugar` example fuzzer](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/libfuzzer_stb_image_sugar).
|
||||||
|
|
||||||
### [`libafl_derive`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl_derive)
|
### libafl_derive
|
||||||
|
|
||||||
This a proc-macro crate paired with the `libafl` crate.
|
This a proc-macro crate paired with the `libafl` crate.
|
||||||
|
|
||||||
At the moment, it just exposes the `derive(SerdeAny)` macro that can be used to define Metadata structs, see the section about [Metadata](../design/metadata.md) for details.
|
At the moment, it just exposes the `derive(SerdeAny)` macro that can be used to define Metadata structs, see the section about [Metadata](../design/metadata.md) for details.
|
||||||
|
|
||||||
### [`libafl_targets`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl_targets)
|
### libafl_targets
|
||||||
|
|
||||||
This crate exposes code to interact with, and to instrument, targets.
|
This crate exposes code to interact with, and to instrument, targets.
|
||||||
To enable and disable features at compile-time, the features are enabled and disabled using feature flags.
|
To enable and disable features at compile-time, the features are enabled and disabled using feature flags.
|
||||||
@ -67,36 +52,36 @@ 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`](https://github.com/AFLplusplus/LibAFL/tree/main/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.
|
||||||
|
|
||||||
### [`libafl_frida`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl_frida)
|
### libafl_frida
|
||||||
|
|
||||||
This library bridges LibAFL with Frida as instrumentation backend.
|
This library bridges LibAFL with Frida as instrumentation backend.
|
||||||
With this crate, you can instrument targets on Linux/macOS/Windows/Android for coverage collection.
|
With this crate, you can instrument targets on Linux/macOS/Windows/Android for coverage collection.
|
||||||
Additionally, it supports CmpLog, and AddressSanitizer instrumentation and runtimes for aarch64.
|
Additionally, it supports CmpLog, and AddressSanitizer instrumentation and runtimes for aarch64.
|
||||||
See further information, as well as usage instructions, [later in the book](../advanced_features/frida.md).
|
See further information, as well as usage instructions, [later in the book](../advanced_features/frida.md).
|
||||||
|
|
||||||
### [`libafl_qemu`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl_qemu)
|
### libafl_qemu
|
||||||
|
|
||||||
This library bridges LibAFL with QEMU user-mode to fuzz ELF cross-platform binaries.
|
This library bridges LibAFL with QEMU user-mode to fuzz ELF cross-platform binaries.
|
||||||
|
|
||||||
It works on Linux and can collect edge coverage without collisions!
|
It works on Linux and can collect edge coverage without collisions!
|
||||||
It also supports a wide range of hooks and instrumentation options.
|
It also supports a wide range of hooks and instrumentation options.
|
||||||
|
|
||||||
### [`libafl_nyx`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl_nyx)
|
### libafl_nyx
|
||||||
|
|
||||||
[Nyx](https://nyx-fuzz.com/) is a KVM-based snapshot fuzzer. `libafl_nyx` adds these capabilities to LibAFL. There is a specific section explaining usage of libafl_nyx [later in the book](../advanced_features/nyx.md).
|
[Nyx](https://nyx-fuzz.com/) is a KVM-based snapshot fuzzer. `libafl_nyx` adds these capabilities to LibAFL. There is a specific section explaining usage of libafl_nyx [later in the book](../advanced_features/nyx.md).
|
||||||
|
|
||||||
### [`libafl_concolic`](https://github.com/AFLplusplus/LibAFL/tree/main/libafl_concolic)
|
### libafl_concolic
|
||||||
|
|
||||||
Concolic fuzzing is the combination of fuzzing and a symbolic execution engine.
|
Concolic fuzzing is the combination of fuzzing and a symbolic execution engine.
|
||||||
This can reach greater depth than normal fuzzing, and is exposed in this crate.
|
This can reach greater depth than normal fuzzing, and is exposed in this crate.
|
||||||
|
@ -11,27 +11,27 @@ 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
|
||||||
$ tar xvf main.tar.gz
|
$ tar xvf LibAFL-main.tar.gz
|
||||||
$ rm main.tar.gz
|
$ rm LibAFL-main.tar.gz
|
||||||
$ ls LibAFL-main # this is the extracted folder
|
$ 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,
|
||||||
@ -54,6 +54,6 @@ explained [here](https://clang.llvm.org/get_started.html).
|
|||||||
|
|
||||||
If you do not have Rust installed, you can easily follow the steps described [here](https://www.rust-lang.org/tools/install)
|
If you do not have Rust installed, you can easily follow the steps described [here](https://www.rust-lang.org/tools/install)
|
||||||
to install it on any supported system.
|
to install it on any supported system.
|
||||||
Be aware that Rust versions shipped with Linux distributions may be outdated, LibAFL always targets the latest `stable` version available via `rustup update`.
|
Be aware that Rust versions shipped with Linux distributions may be outdated, LibAFL always targets the latest `stable` version available via `rustup upgrade`.
|
||||||
|
|
||||||
We suggest installing Clang and LLVM first.
|
We suggest installing Clang and LLVM first.
|
||||||
|
@ -4,7 +4,7 @@ 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 can not just be combined for new projects.
|
Their outstanding features can not just be combined for new projects.
|
||||||
@ -24,11 +24,11 @@ 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.
|
||||||
- `fast`: We do everything we can at compile time so that the runtime overhead is as minimal as it can get.
|
- `fast`: We do everything we can at compile time so that the runtime overhead is as minimal as it can get.
|
||||||
- `bring your own target`: We support binary-only modes, like (full-system) QEMU-Mode and Frida-Mode with ASan and CmpLog, as well as multiple compilation passes for sourced-based instrumentation.
|
- `bring your own target`: We support binary-only modes, like QEMU-Mode and Frida-Mode with ASAN and CmpLog, as well as multiple compilation passes for sourced-based instrumentation.
|
||||||
Of course, we also support custom instrumentation, as you can see in the Python example based on Google's Atheris.
|
Of course, we also support custom instrumentation, as you can see in the Python example based on Google's Atheris.
|
||||||
- `usable`: This one is on you to decide. Dig right in!
|
- `usable`: This one is on you to decide. Dig right in!
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
# The LibAFL Fuzzing Library
|
# The LibAFL Fuzzing Library
|
||||||
|
|
||||||
<img align="right" src="https://raw.githubusercontent.com/AFLplusplus/Website/main/static/libafl_logo.svg" alt="LibAFL Logo" style="width: 256px; height: auto">
|
<img align="right" src="https://github.com/AFLplusplus/Website/raw/master/static/logo_256x256.png" alt="AFL++ Logo">
|
||||||
|
|
||||||
*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.
|
||||||
|
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
Configurations for individual fuzzer nodes are relevant for multi node fuzzing.
|
Configurations for individual fuzzer nodes are relevant for multi node fuzzing.
|
||||||
The chapter describes how to run nodes with different configurations
|
The chapter describes how to run nodes with different configurations
|
||||||
in one fuzzing cluster.
|
in one fuzzing cluster.
|
||||||
This allows, for example, a node compiled with ASan, to know that it needs to rerun new testcases for a node without ASan, while the same binary/configuration does not.
|
This allows, for example, a node compiled with ASAN, to know that it needs to rerun new testcases for a node without ASAN, while the same binary/configuration does not.
|
||||||
|
|
||||||
Fuzzers with the same configuration can exchange Observers for new testcases and reuse them without rerunning the input.
|
Fuzzers with the same configuration can exchange Observers for new testcases and reuse them without rerunning the input.
|
||||||
A different configuration indicates, that only the raw input can be exchanged, it must be rerun on the other node to capture relevant observations.
|
A different configuration indicates, that only the raw input can be exchanged, it must be rerun on the other node to capture relevant observations.
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
# 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 the `CachedOnDiskCorpus` or similar.
|
Depending on individual needs, LibAFL can also write testcase contents to disk, while still using events to notify other fuzzers, using an `OnDiskCorpus`.
|
||||||
|
|
||||||
In our tests, message passing scales very well to share new testcases and metadata between multiple running fuzzer instances for multi-core fuzzing.
|
In our tests, message passing scales very well to share new testcases and metadata between multiple running fuzzer instances for multi-core fuzzing.
|
||||||
Specifically, it scales _a lot_ better than using memory locks on a shared corpus, and _a lot_ better than sharing the testcases via the filesystem, as AFL traditionally does.
|
Specifically, it scales _a lot_ better than using memory locks on a shared corpus, and _a lot_ better than sharing the testcases via the filesystem, as AFL traditionally does.
|
||||||
@ -12,7 +12,7 @@ The `EventManager` interface is used to send Events over the wire using `Low Lev
|
|||||||
|
|
||||||
## Low Level Message Passing (LLMP)
|
## Low Level Message Passing (LLMP)
|
||||||
|
|
||||||
LibAFL comes with a reasonably lock-free message passing mechanism that scales well across cores and, using its _broker2broker_ mechanism, even to connected machines via TCP.
|
LibAFL comes with a reasonably lock-free message passing mechanism that scales well across cores and, using its *broker2broker* mechanism, even to connected machines via TCP.
|
||||||
Most example fuzzers use this mechanism, and it is the best `EventManager` if you want to fuzz on more than a single core.
|
Most example fuzzers use this mechanism, and it is the best `EventManager` if you want to fuzz on more than a single core.
|
||||||
In the following, we will describe the inner workings of `LLMP`.
|
In the following, we will describe the inner workings of `LLMP`.
|
||||||
|
|
||||||
@ -28,12 +28,12 @@ Shared maps, called shared memory for the sake of not colliding with Rust's `map
|
|||||||
Each client, usually a fuzzer trying to share stats and new testcases, maps an outgoing `ShMem` map.
|
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]
|
||||||
@ -75,10 +75,10 @@ So the outgoing messages flow is like this over the outgoing broadcast `Shmem`:
|
|||||||
To use `LLMP` in LibAFL, you usually want to use an `LlmpEventManager` or its restarting variant.
|
To use `LLMP` in LibAFL, you usually want to use an `LlmpEventManager` or its restarting variant.
|
||||||
They are the default if using LibAFL's `Launcher`.
|
They are the default if using LibAFL's `Launcher`.
|
||||||
|
|
||||||
If you should want to use `LLMP` in its raw form, without any `LibAFL` abstractions, take a look at the `llmp_test` example in [./libafl/examples](https://github.com/AFLplusplus/LibAFL/blob/main/libafl_bolts/examples/llmp_test/main.rs).
|
If you should want to use `LLMP` in its raw form, without any `LibAFL` abstractions, take a look at the `llmp_test` example in [./libafl/examples](https://github.com/AFLplusplus/LibAFL/blob/main/libafl/examples/llmp_test/main.rs).
|
||||||
You can run the example using `cargo run --example llmp_test` with the appropriate modes, as indicated by its help output.
|
You can run the example using `cargo run --example llmp_test` with the appropriate modes, as indicated by its help output.
|
||||||
First, you will have to create a broker using `LlmpBroker::new()`.
|
First, you will have to create a broker using `LlmpBroker::new()`.
|
||||||
Then, create some `LlmpClient`s in other threads and register them with the main thread using `LlmpBroker::register_client`.
|
Then, create some `LlmpClient``s` in other threads and register them with the main thread using `LlmpBroker::register_client`.
|
||||||
Finally, call `LlmpBroker::loop_forever()`.
|
Finally, call `LlmpBroker::loop_forever()`.
|
||||||
|
|
||||||
### B2B: Connecting Fuzzers via TCP
|
### B2B: Connecting Fuzzers via TCP
|
||||||
|
@ -4,18 +4,18 @@ Multiple fuzzer instances can be spawned using different ways.
|
|||||||
|
|
||||||
## Manually, via a TCP port
|
## Manually, via a TCP port
|
||||||
|
|
||||||
The straightforward way to do Multi-Threading is to use the [`LlmpRestartingEventManager`](https://docs.rs/libafl/latest/libafl/events/llmp/restarting/struct.LlmpRestartingEventManager.html), specifically to use [`setup_restarting_mgr_std`](https://docs.rs/libafl/latest/libafl/events/llmp/restarting/fn.setup_restarting_mgr_std.html).
|
The straightforward way to do Multi-Threading is to use the `LlmpRestartingEventManager`, specifically to use `setup_restarting_mgr_std`.
|
||||||
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,17 +42,13 @@ 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.
|
||||||
If the launcher uses `fork`, it will hide child output, unless the settings indicate otherwise, or the `LIBAFL_DEBUG_OUTPUT` env variable is set.
|
On Windows, the Launcher will restart each client, while on Unix, it will use `fork`.
|
||||||
On Windows, the Launcher will restart each client, while on Unix-alikes, it will use `fork`.
|
|
||||||
|
|
||||||
Advanced use-cases:
|
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.
|
||||||
4. On `Unix`, you can chose between a forking and non-forking version of Launcher by setting the `fork` feature in LibAFL. Some targets may not like forking, but it is faster than restarting processes from scratch. Windows will never fork.
|
|
||||||
5. For simple debugging, first set the `LIBAFL_DEBUG_OUTPUT` env variable to see if a child process printed anything.
|
|
||||||
6. For further debugging of fuzzer failures, it may make sense to replace `Launcher` temporarily with a [`SimpleEventManager`](https://docs.rs/libafl/latest/libafl/events/simple/struct.SimpleEventManager.html#method.new) and call your harness fn (`run_client(None, mgr, 0);`) directly, so that fuzzing runs in the same thread and is easier to debug, before moving back to `Launcher` after the bugfix.
|
|
||||||
|
|
||||||
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).
|
||||||
|
|
||||||
|
@ -5,4 +5,4 @@
|
|||||||
> This section is under construction.
|
> This section is under construction.
|
||||||
> Please check back later (or open a PR)
|
> Please check back later (or open a PR)
|
||||||
>
|
>
|
||||||
> In the meantime, find the final Lain-based fuzzer in [the fuzzers folder](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/baby/tutorial)
|
> In the meantime, find the final Lain-based fuzzer in [the fuzzers folder](https://github.com/AFLplusplus/LibAFL/tree/main/fuzzers/tutorial)
|
||||||
|
@ -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.
|
||||||
|
@ -1,197 +0,0 @@
|
|||||||
# FRET Fuzzer Software Architecture
|
|
||||||
|
|
||||||
FRET (FreeRTOS Real-Time) is a sophisticated greybox fuzzer designed for testing real-time operating systems, specifically FreeRTOS kernels. It leverages QEMU system-mode emulation to provide binary-only coverage-guided fuzzing with advanced system state tracking and real-time analysis capabilities.
|
|
||||||
|
|
||||||
## High-Level Architecture
|
|
||||||
|
|
||||||
The FRET fuzzer follows a multi-layered architecture with the following key components:
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────────────────────────────┐
|
|
||||||
│ FRET Fuzzer │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ CLI Interface (cli.rs) & Configuration (config.rs) │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ Main Fuzzer Loop (fuzzer.rs) │
|
|
||||||
│ ├── LibAFL Framework Integration │
|
|
||||||
│ ├── QEMU System-Mode Emulation │
|
|
||||||
│ └── Target Binary Loading & Symbol Resolution │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ System State Tracking (systemstate/) │
|
|
||||||
│ ├── Target OS Abstraction (target_os/) │
|
|
||||||
│ │ └── FreeRTOS Implementation (freertos/) │
|
|
||||||
│ ├── State Transition Graph (stg.rs) │
|
|
||||||
│ ├── System State Feedbacks (feedbacks.rs) │
|
|
||||||
│ ├── Custom Mutation Strategies (mutational.rs) │
|
|
||||||
│ └── Specialized Schedulers (schedulers.rs) │
|
|
||||||
├─────────────────────────────────────────────────────────────────┤
|
|
||||||
│ Timing Analysis (time/) │
|
|
||||||
│ ├── Clock Management (clock.rs) │
|
|
||||||
│ ├── QEMU State Management (qemustate.rs) │
|
|
||||||
│ └── Basic Worst-Case Heuristics (worst.rs) │
|
|
||||||
└─────────────────────────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
## Core Components
|
|
||||||
|
|
||||||
### 1. Main Fuzzer Engine (`fuzzer.rs`)
|
|
||||||
|
|
||||||
The main fuzzer engine coordinates all components and implements the core fuzzing loop:
|
|
||||||
|
|
||||||
- **QEMU Integration**: Uses LibAFL's QEMU integration for system-mode emulation
|
|
||||||
- **Symbol Resolution**: Resolves kernel symbols and sets up memory mappings
|
|
||||||
- **Input Generation**: Manages interrupt timing and system inputs
|
|
||||||
- **Corpus Management**: Maintains test cases with execution time metadata
|
|
||||||
- **Feedback Orchestration**: Coordinates multiple feedback mechanisms
|
|
||||||
|
|
||||||
### 2. System State Tracking (`systemstate/`)
|
|
||||||
|
|
||||||
This is the heart of FRET's innovation - tracking and analyzing real-time system states:
|
|
||||||
|
|
||||||
#### 2.1 Target OS Abstraction (`target_os/`)
|
|
||||||
|
|
||||||
Provides a generic interface for different RTOS implementations:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
trait TargetSystem {
|
|
||||||
type State: SystemState;
|
|
||||||
type TCB: TaskControlBlock;
|
|
||||||
type TraceData: SystemTraceData;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2.2 FreeRTOS Implementation (`target_os/freertos/`)
|
|
||||||
|
|
||||||
- **QEMU Module** (`qemu_module.rs`): Hooks into QEMU execution to capture system states
|
|
||||||
- **State Capture**: Records task control blocks, ready queues, delay lists
|
|
||||||
- **Symbol Resolution** (`config.rs`): Maps kernel symbols to addresses
|
|
||||||
- **Post-processing**: Converts raw states into refined system representations
|
|
||||||
|
|
||||||
#### 2.3 System State Representation
|
|
||||||
|
|
||||||
**Hierarchical Data Structures:**
|
|
||||||
- `RawFreeRTOSSystemState`: Raw data captured from QEMU at specific instants
|
|
||||||
- `FreeRTOSSystemState`: Refined system state without execution context
|
|
||||||
- `ExecInterval`: Execution intervals between system state changes
|
|
||||||
- `AtomicBasicBlock`: Single-entry multiple-exit code regions
|
|
||||||
- `RTOSJob`: Complete task execution with timing information
|
|
||||||
- `RTOSTask`: Generalized task representation
|
|
||||||
|
|
||||||
### 3. State Transition Graph (STG) (`stg.rs`)
|
|
||||||
|
|
||||||
The STG is a key innovation that models system execution as a directed graph:
|
|
||||||
|
|
||||||
- **Nodes**: Represent system states with associated atomic basic blocks
|
|
||||||
- **Edges**: Represent state transitions with execution timing
|
|
||||||
- **Path Analysis**: Tracks execution paths and identifies worst-case scenarios
|
|
||||||
- **Scheduling Analysis**: Models task scheduling decisions and preemption
|
|
||||||
|
|
||||||
**STG Features:**
|
|
||||||
- State deduplication using hash-based node identification
|
|
||||||
- Edge weight tracking for timing analysis
|
|
||||||
- Path-based coverage feedback
|
|
||||||
- Integration with corpus scheduling
|
|
||||||
|
|
||||||
### 4. Feedback Mechanisms (`feedbacks.rs`)
|
|
||||||
|
|
||||||
FRET implements multiple specialized feedback mechanisms:
|
|
||||||
|
|
||||||
- **STG-based Feedback**: Uses state transition graph coverage
|
|
||||||
- **Timing Feedback**: Focuses on worst-case execution time
|
|
||||||
- **System State Feedback**: Tracks unique system configurations
|
|
||||||
- **Traditional Coverage**: Standard edge coverage for comparison
|
|
||||||
|
|
||||||
### 5. Custom Mutation Strategies (`mutational.rs`)
|
|
||||||
|
|
||||||
Specialized mutation operators for real-time systems:
|
|
||||||
|
|
||||||
- **Interrupt Timing Mutation**: Modifies interrupt arrival times
|
|
||||||
- **STG-guided Mutation**: Uses state transition graph to guide mutations
|
|
||||||
- **System State Mutation**: Targets specific system configurations
|
|
||||||
|
|
||||||
### 6. Timing Analysis (`time/`)
|
|
||||||
|
|
||||||
Comprehensive timing analysis for real-time systems:
|
|
||||||
|
|
||||||
- **Clock Management**: Tracks QEMU instruction counts and timing
|
|
||||||
- **WCET Analysis**: Identifies worst-case execution times
|
|
||||||
- **Response Time Analysis**: Measures task response times
|
|
||||||
- **Temporal Schedulers**: Prioritize inputs based on timing properties
|
|
||||||
|
|
||||||
## Information Flow
|
|
||||||
|
|
||||||
### System State Capture Flow
|
|
||||||
|
|
||||||
1. **Symbol Resolution** (`target_os::freertos::config.rs`):
|
|
||||||
- Resolves kernel symbols (task control blocks, queues, etc.)
|
|
||||||
- Creates address ranges for API functions and ISR handlers
|
|
||||||
|
|
||||||
2. **Runtime Capture** (`target_os::freertos::qemu_module::FreeRTOSSystemStateHelper`):
|
|
||||||
- Hooks QEMU execution at critical points (syscalls, interrupts)
|
|
||||||
- Captures `RawFreeRTOSSystemState` snapshots
|
|
||||||
- Records memory reads and execution intervals
|
|
||||||
|
|
||||||
3. **State Processing**:
|
|
||||||
- Converts raw states to `FreeRTOSSystemState` (refined representation)
|
|
||||||
- Generates `ExecInterval` objects for execution flow
|
|
||||||
- Identifies `AtomicBasicBlock` regions
|
|
||||||
|
|
||||||
4. **STG Construction** (`stg::StgFeedback`):
|
|
||||||
- **Core Classes**:
|
|
||||||
- `STGFeedbackState<SYS>`: Maintains the state transition graph and metadata
|
|
||||||
- `STGNode<SYS>`: Represents graph nodes containing system state hash and atomic basic block
|
|
||||||
- `STGEdge`: Represents graph edges with execution timing and transition information
|
|
||||||
- `StgFeedback<SYS>`: Main feedback mechanism that builds and updates the STG
|
|
||||||
- **Process**:
|
|
||||||
- Analyzes `ExecInterval` sequences from trace data
|
|
||||||
- Creates `STGNode` instances for unique system state + ABB combinations
|
|
||||||
- Adds `STGEdge` instances for state transitions with timing weights
|
|
||||||
- Uses `HashMap<u64, NodeIndex>` for efficient node deduplication
|
|
||||||
- Integrates with `DiGraph<STGNode<SYS>, STGEdge>` from petgraph library
|
|
||||||
|
|
||||||
5. **Feedback Integration**:
|
|
||||||
- **Timing-Based Feedbacks**:
|
|
||||||
- `ClockTimeFeedback<SYS>`: Tracks QEMU instruction count increases
|
|
||||||
- `QemuClockIncreaseFeedback<SYS>`: Monitors clock progression patterns
|
|
||||||
- `ExecTimeIncFeedback<SYS>`: Focuses on execution time improvements
|
|
||||||
- **Corpus Schedulers**:
|
|
||||||
- `TimeMaximizerCorpusScheduler<CS, O>`: Prioritizes inputs with longer execution times
|
|
||||||
- `TimeStateMaximizerCorpusScheduler<CS, O, SYS>`: Combines timing and system state coverage
|
|
||||||
- `LongestTraceScheduler<CS, SYS>`: Schedules based on trace length metrics
|
|
||||||
- `GenerationScheduler<S>`: Implements generation-based scheduling strategies
|
|
||||||
- **Mutation Strategies**:
|
|
||||||
- `InterruptShiftStage<E, EM, Z, SYS>`: Mutates interrupt timing sequences
|
|
||||||
- `STGSnippetStage<E, EM, Z, SYS>`: Uses STG paths to guide mutation decisions
|
|
||||||
- **Integration Process**:
|
|
||||||
- Multiple feedback mechanisms run in parallel during corpus evaluation
|
|
||||||
- `StgFeedback` updates the state transition graph with new paths
|
|
||||||
- Timing feedbacks identify inputs that trigger longer execution paths
|
|
||||||
- Schedulers use combined metrics to prioritize corpus entries for mutation
|
|
||||||
|
|
||||||
### Execution Flow
|
|
||||||
|
|
||||||
1. **Initialization**:
|
|
||||||
- Load target kernel binary
|
|
||||||
- Resolve symbols and setup memory mappings
|
|
||||||
- Initialize QEMU system-mode emulation
|
|
||||||
- Setup hooks for system state capture
|
|
||||||
|
|
||||||
2. **Fuzzing Loop**:
|
|
||||||
- Generate/mutate input (interrupt timing sequence)
|
|
||||||
- Execute target in QEMU with system state tracking
|
|
||||||
- Capture execution trace and timing information
|
|
||||||
- Build/update state transition graph
|
|
||||||
- Evaluate feedback mechanisms
|
|
||||||
- Update corpus and select next input
|
|
||||||
|
|
||||||
3. **Runtime Monitoring and Analysis**:
|
|
||||||
- Display fuzzing progress, coverage metrics, and timing statistics
|
|
||||||
- Maintain and prioritize test cases based on coverage and timing feedback
|
|
||||||
|
|
||||||
4. **Output Generation**
|
|
||||||
- **Time Dumps** (`--dump-times`, `-t`): Export execution timing data for offline analysis
|
|
||||||
- **Worst-Case Dumps** (`--dump-cases`, `-a`): Save inputs that trigger worst-case execution scenarios
|
|
||||||
- **Trace Dumps** (`--dump-traces`, `-r`): Export detailed execution traces including system state transitions
|
|
||||||
- **Graph Dumps** (`--dump-graph`, `-g`): Output state transition graphs in DOT format for visualization
|
|
||||||
- **Task-Specific Analysis** (`--select-task`, `-s`): Focus measurements on specific RTOS tasks
|
|
||||||
- **Configurable Output Prefix** (`--dump-name`, `-n`): Set custom prefixes for all output files
|
|
@ -1,63 +1,30 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "fret"
|
name = "fret"
|
||||||
version = "0.8.2"
|
version = "0.8.2"
|
||||||
authors = ["Alwin Berger <alwin.berger@tu-dortmund.de>"]
|
authors = ["Andrea Fioraldi <andreafioraldi@gmail.com>", "Dominik Maier <domenukk@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std", "snapshot_fast", "restarting", "do_hash_notify_state", "do_hash_notify_value", "config_stg", "fuzz_int", "shortcut", "trace_job_response_times" ]
|
default = ["std", "snapshot_restore", "singlecore", "restarting", "feed_systemtrace", "fuzz_int" ]
|
||||||
std = []
|
std = []
|
||||||
# Exec environemnt basics
|
|
||||||
snapshot_restore = []
|
snapshot_restore = []
|
||||||
snapshot_fast = [ "snapshot_restore" ]
|
snapshot_fast = [ "snapshot_restore" ]
|
||||||
singlecore = []
|
singlecore = []
|
||||||
restarting = ['singlecore']
|
restarting = ['singlecore']
|
||||||
run_until_saturation = []
|
trace_abbs = []
|
||||||
fuzz_int = []
|
systemstate = []
|
||||||
shortcut = []
|
feed_systemgraph = [ "systemstate" ]
|
||||||
# information capture
|
feed_systemtrace = [ "systemstate" ]
|
||||||
observe_edges = [] # observe cfg edges
|
|
||||||
observe_hitcounts = [ "observe_edges" ] # reduces edge granularity
|
|
||||||
observe_systemstate = []
|
|
||||||
do_hash_notify_state = []
|
|
||||||
do_hash_notify_value = []
|
|
||||||
trace_job_response_times = [ "trace_stg" ]
|
|
||||||
trace_stg = [ "observe_systemstate" ]
|
|
||||||
trace_reads = [ "trace_stg", "trace_job_response_times" ]
|
|
||||||
# feedbacks
|
|
||||||
feed_stg = [ "trace_stg", "observe_systemstate" ]
|
|
||||||
feed_stg_edge = [ "feed_stg"]
|
|
||||||
feed_stg_abb_woet = [ "feed_stg"]
|
|
||||||
feed_stg_pathhash = [ "feed_stg"]
|
|
||||||
feed_stg_abbhash = [ "feed_stg"]
|
|
||||||
feed_stg_aggregatehash = [ "feed_stg"]
|
|
||||||
feed_job_woet = [ "trace_job_response_times"]
|
|
||||||
feed_job_wort = [ "trace_job_response_times"]
|
|
||||||
mutate_stg = [ "observe_systemstate", "trace_reads" ]
|
|
||||||
feed_longest = [ ]
|
feed_longest = [ ]
|
||||||
feed_afl = [ "observe_edges" ]
|
feed_afl = [ ]
|
||||||
feed_genetic = [ ]
|
feed_genetic = [ ]
|
||||||
|
fuzz_int = [ ]
|
||||||
gensize_1 = [ ]
|
gensize_1 = [ ]
|
||||||
gensize_10 = [ ]
|
gensize_10 = [ ]
|
||||||
gensize_100 = [ ]
|
gensize_100 = [ ]
|
||||||
gensize_1000 = [ ]
|
observer_hitcounts = []
|
||||||
# schedulers
|
no_hash_state = []
|
||||||
sched_genetic = []
|
run_until_saturation = []
|
||||||
sched_afl = []
|
|
||||||
sched_stg = []
|
|
||||||
sched_stg_edge = ['sched_stg'] # every edge in the stg
|
|
||||||
sched_stg_pathhash = ['sched_stg'] # every path in the stg
|
|
||||||
sched_stg_abbhash = ['sched_stg'] # every path of abbs
|
|
||||||
sched_stg_aggregatehash = ['sched_stg'] # every aggregated path (order independent)
|
|
||||||
# overall_configs
|
|
||||||
config_genetic = ["feed_genetic","sched_genetic","trace_stg"]
|
|
||||||
config_afl = ["feed_afl","sched_afl","trace_stg"]
|
|
||||||
config_frafl = ["feed_afl","sched_afl","feed_longest","trace_stg"]
|
|
||||||
config_stg = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg","feed_job_wort"]
|
|
||||||
config_stg_woet = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg","feed_job_wort","feed_job_woet","feed_stg_abb_woet"]
|
|
||||||
# config_stg_aggregate = ["feed_stg_aggregatehash","sched_stg_aggregatehash","mutate_stg"]
|
|
||||||
config_stg_abbpath = ["feed_stg_abbhash","sched_stg_abbhash","mutate_stg"]
|
|
||||||
config_stg_edge = ["feed_stg_edge","sched_stg_edge","mutate_stg"]
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = true
|
lto = true
|
||||||
@ -65,18 +32,10 @@ codegen-units = 1
|
|||||||
debug = true
|
debug = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
libafl = { path = "../../libafl/", features = ["multipart_inputs", "prelude"] }
|
libafl = { path = "../../libafl/" }
|
||||||
libafl_bolts = { path = "../../libafl_bolts/" }
|
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"] }
|
||||||
libafl_targets = { path = "../../libafl_targets/" }
|
|
||||||
libafl_qemu = { path = "../../libafl_qemu/", features = ["arm", "systemmode"], default-features = false }
|
|
||||||
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
serde = { version = "1.0", default-features = false, features = ["alloc"] } # serialization lib
|
||||||
serde_json = { version = "1.0", default-features = false, features = ["alloc"] }
|
hashbrown = { version = "0.12", features = ["serde", "ahash-compile-time-rng"] } # A faster hashmap, nostd compatible
|
||||||
hashbrown = { version = "0.14.0", features = ["serde"] } # A faster hashmap, nostd compatible
|
petgraph = { version="0.6.0", features = ["serde-1"] }
|
||||||
petgraph = { version="0.6.5", features = ["serde-1"] }
|
|
||||||
ron = "0.7" # write serialized data - including hashmaps
|
ron = "0.7" # write serialized data - including hashmaps
|
||||||
rand = "0.5"
|
rand = "0.5"
|
||||||
clap = { version = "4.4.11", features = ["derive"] }
|
|
||||||
csv = "1.3.0"
|
|
||||||
log = "0.4"
|
|
||||||
simple_moving_average = "1.0.2"
|
|
||||||
itertools = "0.13.0"
|
|
||||||
|
26
fuzzers/FRET/README.md
Normal file
26
fuzzers/FRET/README.md
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# Qemu systemmode with launcher
|
||||||
|
|
||||||
|
This folder contains an example fuzzer for the qemu systemmode, using LLMP for fast multi-process fuzzing and crash detection.
|
||||||
|
|
||||||
|
## Build
|
||||||
|
|
||||||
|
To build this example, run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cargo build --release
|
||||||
|
cd example; sh build.sh; cd ..
|
||||||
|
```
|
||||||
|
|
||||||
|
This will build the the fuzzer (src/fuzzer.rs) and a small example binary based on FreeRTOS, which can run under a qemu emulation target.
|
||||||
|
|
||||||
|
## Run
|
||||||
|
|
||||||
|
Since the instrumentation is based on snapshtos QEMU needs a virtual drive (even if it is unused...).
|
||||||
|
Create on and then run the fuzzer:
|
||||||
|
```bash
|
||||||
|
# create an image
|
||||||
|
qemu-img create -f qcow2 dummy.qcow2 32M
|
||||||
|
# run the fuzzer
|
||||||
|
KERNEL=./example/example.elf target/release/qemu_systemmode -icount shift=auto,align=off,sleep=off -machine mps2-an385 -monitor null -kernel ./example/example.elf -serial null -nographic -snapshot -drive if=none,format=qcow2,file=dummy.qcow2 -S
|
||||||
|
```
|
||||||
|
Currently the ``KERNEL`` variable is needed because the fuzzer does not parse QEMUs arguments to find the binary.
|
5
fuzzers/FRET/benchmark/.gitignore
vendored
5
fuzzers/FRET/benchmark/.gitignore
vendored
@ -10,8 +10,3 @@ bins
|
|||||||
.snakemake
|
.snakemake
|
||||||
*.zip
|
*.zip
|
||||||
*.tar.*
|
*.tar.*
|
||||||
*.sqlite
|
|
||||||
eval*
|
|
||||||
test_*
|
|
||||||
bench_*
|
|
||||||
results_*
|
|
||||||
|
@ -1,328 +1,281 @@
|
|||||||
import csv
|
import csv
|
||||||
import os
|
import os
|
||||||
envvars:
|
def_flags="--no-default-features --features std,snapshot_restore,singlecore,restarting,run_until_saturation"
|
||||||
"BENCHDIR"
|
remote="timedump_253048_1873f6_all/"
|
||||||
def_flags="--release --no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,do_hash_notify_value,fuzz_int,trace_job_response_times"
|
RUNTIME=10
|
||||||
benchdir=os.environ["BENCHDIR"]
|
TARGET_REPS_A=2
|
||||||
RUNTIME=int(os.environ["RUNTIME"]) if "RUNTIME" in os.environ else (3600*24)
|
TARGET_REPS_B=2
|
||||||
TARGET_SET=['feedgeneration100', 'stgwoet', 'frafl']
|
NUM_NODES=2
|
||||||
TARGET_REPLICA_NUMBER=int(os.environ["TARGET_REPLICA_NUMBER"]) if "TARGET_REPLICA_NUMBER" in os.environ else 10
|
REP_PER_NODE_A=int(TARGET_REPS_A/NUM_NODES)
|
||||||
RANDOM_REPLICA_NUMBER=int(os.environ["RANDOM_REPLICA_NUMBER"]) if "RANDOM_REPLICA_NUMBER" in os.environ else 1
|
REP_PER_NODE_B=int(TARGET_REPS_B/NUM_NODES)
|
||||||
MULTIJOB_REPLICA_NUMBER=int(os.environ["MULTIJOB_REPLICA_NUMBER"]) if "MULTIJOB_REPLICA_NUMBER" in os.environ else 3
|
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)
|
||||||
rule build_kernels:
|
MY_RANGE_B=range(NODE_ID*REP_PER_NODE_B,(NODE_ID+1)*REP_PER_NODE_B)
|
||||||
shell:
|
|
||||||
"bash scripts/build_all_demos.sh"
|
|
||||||
|
|
||||||
rule copy_kernel:
|
|
||||||
input:
|
|
||||||
"build/{target}.elf"
|
|
||||||
output:
|
|
||||||
"{benchdir}/build/{target}.elf"
|
|
||||||
shell:
|
|
||||||
"mkdir -p {benchdir}/build && cp {input} {output}"
|
|
||||||
|
|
||||||
rule rebuild_qemu:
|
|
||||||
shell:
|
|
||||||
"unset CUSTOM_QEMU_NO_BUILD CUSTOM_QEMU_NO_CONFIGURE && cargo build"
|
|
||||||
|
|
||||||
rule build_tools:
|
|
||||||
output:
|
|
||||||
directory("../tools/bin")
|
|
||||||
shell:
|
|
||||||
"../tools/build.sh"
|
|
||||||
|
|
||||||
rule build_default:
|
|
||||||
input:
|
|
||||||
"../Cargo.toml",
|
|
||||||
"../src"
|
|
||||||
output:
|
|
||||||
directory("{benchdir}/bins/target_default")
|
|
||||||
shell:
|
|
||||||
"cargo build --target-dir {output} {def_flags}"
|
|
||||||
|
|
||||||
rule build_showmap:
|
rule build_showmap:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_showmap")
|
directory("bins/target_showmap")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg"
|
"cargo build --target-dir {output} {def_flags},systemstate"
|
||||||
|
|
||||||
rule build_random:
|
rule build_random:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_random")
|
directory("bins/target_random")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_longest"
|
"cargo build --target-dir {output} {def_flags},feed_longest"
|
||||||
|
|
||||||
|
rule build_feedlongest:
|
||||||
|
output:
|
||||||
|
directory("bins/target_feedlongest")
|
||||||
|
shell:
|
||||||
|
"cargo build --target-dir {output} {def_flags},feed_longest"
|
||||||
|
|
||||||
rule build_frafl:
|
rule build_frafl:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_frafl")
|
directory("bins/target_frafl")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_frafl,feed_longest"
|
"cargo build --target-dir {output} {def_flags},feed_afl,feed_longest"
|
||||||
|
|
||||||
rule build_afl:
|
rule build_afl:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_afl")
|
directory("bins/target_afl")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_afl"
|
"cargo build --target-dir {output} {def_flags},feed_afl,observer_hitcounts"
|
||||||
|
|
||||||
rule build_stg:
|
rule build_state:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_stg")
|
directory("bins/target_state")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg"
|
"cargo build --target-dir {output} {def_flags},feed_systemtrace"
|
||||||
|
|
||||||
rule build_stgwoet:
|
rule build_nohashstate:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_stgwoet")
|
directory("bins/target_nohashstate")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_woet"
|
"cargo build --target-dir {output} {def_flags},feed_systemtrace,no_hash_state"
|
||||||
|
|
||||||
rule build_stg_abbpath:
|
rule build_graph:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_stg_abbpath")
|
directory("bins/target_graph")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_abbpath"
|
"cargo build --target-dir {output} {def_flags},feed_systemgraph"
|
||||||
|
|
||||||
rule build_stg_edge:
|
rule build_showmap_int:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_stg_edge")
|
directory("bins/target_showmap_int")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_stg_edge"
|
"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:
|
rule build_feedgeneration1:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_feedgeneration1")
|
directory("bins/target_feedgeneration1")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_genetic,gensize_1"
|
"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:
|
rule build_feedgeneration10:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_feedgeneration10")
|
directory("bins/target_feedgeneration10")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},feed_genetic,gensize_10"
|
"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:
|
rule build_feedgeneration100:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_feedgeneration100")
|
directory("bins/target_feedgeneration100")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,gensize_100"
|
"cargo build --target-dir {output} {def_flags},feed_genetic,gensize_100"
|
||||||
|
|
||||||
rule build_genetic100:
|
rule build_feedgeneration100_int:
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
output:
|
||||||
directory("{benchdir}/bins/target_genetic100")
|
directory("bins/target_feedgeneration100_int")
|
||||||
shell:
|
shell:
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_100"
|
"cargo build --target-dir {output} {def_flags},feed_genetic,fuzz_int,gensize_100"
|
||||||
|
|
||||||
rule build_feedgeneration1000:
|
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
|
||||||
directory("{benchdir}/bins/target_feedgeneration1000")
|
|
||||||
shell:
|
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,gensize_1000"
|
|
||||||
|
|
||||||
rule build_genetic1000:
|
|
||||||
input:
|
|
||||||
"{benchdir}/bins/target_default"
|
|
||||||
output:
|
|
||||||
directory("{benchdir}/bins/target_genetic1000")
|
|
||||||
shell:
|
|
||||||
"cp -r -a --reflink=auto {input} {output} && cargo build --target-dir {output} {def_flags},config_genetic,mutate_stg,gensize_1000"
|
|
||||||
|
|
||||||
rule run_bench:
|
rule run_bench:
|
||||||
input:
|
input:
|
||||||
"{benchdir}/build/{target}.elf",
|
"build/{target}.elf",
|
||||||
"{benchdir}/bins/target_{fuzzer}"
|
"bins/target_{fuzzer}"
|
||||||
output:
|
output:
|
||||||
multiext("{benchdir}/timedump/{fuzzer}/{target}#{num}", ".time", ".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)
|
||||||
line = next((x for x in reader if x['\ufeffkernel']==wildcards.target), None)
|
line = next((x for x in reader if x['kernel']==wildcards.target), None)
|
||||||
if line == None:
|
if line == None:
|
||||||
return False
|
return False
|
||||||
kernel=line['\ufeffkernel']
|
kernel=line['kernel']
|
||||||
fuzz_main=line['main_function']
|
fuzz_main=line['main_function']
|
||||||
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']
|
||||||
select_task=line['select_task']
|
script="""
|
||||||
|
mkdir -p $(dirname {output[0]})
|
||||||
|
export KERNEL=$(pwd)/{input[0]}
|
||||||
|
export FUZZ_MAIN={fuzz_main}
|
||||||
|
export FUZZ_INPUT={fuzz_input}
|
||||||
|
export FUZZ_INPUT_LEN={fuzz_len}
|
||||||
|
export BREAKPOINT={bkp}
|
||||||
|
export SEED_RANDOM={wildcards.num}
|
||||||
|
export TIME_DUMP=$(pwd)/{output[0]}
|
||||||
|
export CASE_DUMP=$(pwd)/{output[0]}.case
|
||||||
|
export TRACE_DUMP=$(pwd)/{output[0]}.trace
|
||||||
|
export FUZZ_ITERS={RUNTIME}
|
||||||
|
export FUZZER=$(pwd)/{input[1]}/debug/fret
|
||||||
|
set +e
|
||||||
|
../fuzzer.sh > {output[1]} 2>&1
|
||||||
|
exit 0
|
||||||
|
"""
|
||||||
if wildcards.fuzzer.find('random') >= 0:
|
if wildcards.fuzzer.find('random') >= 0:
|
||||||
script="""
|
script="export FUZZ_RANDOM={output[1]}\n"+script
|
||||||
export RUST_BACKTRACE=1
|
|
||||||
mkdir -p $(dirname {output[0]})
|
|
||||||
set +e
|
|
||||||
echo $(pwd)/{input[1]}/release/fret -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num}
|
|
||||||
$(pwd)/{input[1]}/release/fret -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz --random -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1
|
|
||||||
exit 0
|
|
||||||
"""
|
|
||||||
else:
|
|
||||||
script="""
|
|
||||||
export RUST_BACKTRACE=1
|
|
||||||
mkdir -p $(dirname {output[0]})
|
|
||||||
set +e
|
|
||||||
echo $(pwd)/{input[1]}/release/fret -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num}
|
|
||||||
$(pwd)/{input[1]}/release/fret -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num} -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv fuzz -t {RUNTIME} -s {wildcards.num} > {output[1]} 2>&1
|
|
||||||
exit 0
|
|
||||||
"""
|
|
||||||
shell(script)
|
shell(script)
|
||||||
|
|
||||||
rule run_showmap:
|
rule run_showmap:
|
||||||
input:
|
input:
|
||||||
"{benchdir}/build/{target}.elf",
|
"{remote}build/{target}.elf",
|
||||||
"{benchdir}/bins/target_showmap",
|
"bins/target_showmap",
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}.case"
|
"bins/target_showmap_int",
|
||||||
|
"{remote}timedump/{fuzzer}/{target}.{num}.case"
|
||||||
output:
|
output:
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}_case.trace.ron",
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.ron",
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}_case.time",
|
"{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)
|
||||||
line = next((x for x in reader if x['\ufeffkernel']==wildcards.target), None)
|
line = next((x for x in reader if x['kernel']==wildcards.target), None)
|
||||||
if line == None:
|
if line == None:
|
||||||
return False
|
return False
|
||||||
kernel=line['\ufeffkernel']
|
kernel=line['kernel']
|
||||||
fuzz_main=line['main_function']
|
fuzz_main=line['main_function']
|
||||||
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']
|
||||||
select_task=line['select_task']
|
script=""
|
||||||
script="""
|
if wildcards.fuzzer.find('_int') > -1:
|
||||||
export FUZZER=$(pwd)/{input[1]}/release/fret
|
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 FUZZ_MAIN={fuzz_main}
|
||||||
|
export FUZZ_INPUT={fuzz_input}
|
||||||
|
export FUZZ_INPUT_LEN={fuzz_len}
|
||||||
|
export BREAKPOINT={bkp}
|
||||||
|
export TRACE_DUMP=$(pwd)/{output[0]}
|
||||||
|
export DO_SHOWMAP=$(pwd)/{input[3]}
|
||||||
|
export TIME_DUMP=$(pwd)/{output[1]}
|
||||||
set +e
|
set +e
|
||||||
echo $FUZZER -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[2]}
|
../fuzzer.sh
|
||||||
$FUZZER -n $(pwd)/{benchdir}/timedump/{wildcards.fuzzer}/{wildcards.target}#{wildcards.num}_case -s {select_task} -t -a -r -g -k {input[0]} -c ./target_symbols.csv showmap -i {input[2]}
|
|
||||||
exit 0
|
exit 0
|
||||||
"""
|
"""
|
||||||
if wildcards.fuzzer.find('random') >= 0:
|
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 transform_trace:
|
rule tarnsform_trace:
|
||||||
input:
|
input:
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}_case.trace.ron",
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.ron"
|
||||||
"../tools/bin"
|
|
||||||
output:
|
output:
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}_case.jobs.csv",
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.csv"
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}_case.resp.csv",
|
shell:
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}_case.abbs.csv"
|
"$(pwd)/../../../../state2gantt/target/debug/state2gantt {input} > {output[0]}"
|
||||||
run:
|
|
||||||
with open('target_symbols.csv') as csvfile:
|
|
||||||
reader = csv.DictReader(csvfile)
|
|
||||||
line = next((x for x in reader if x['\ufeffkernel']==wildcards.target), None)
|
|
||||||
if line == None:
|
|
||||||
return False
|
|
||||||
kernel=line['\ufeffkernel']
|
|
||||||
fuzz_main=line['main_function']
|
|
||||||
fuzz_input=line['input_symbol']
|
|
||||||
fuzz_len=line['input_size']
|
|
||||||
bkp=line['return_function']
|
|
||||||
select_task=line['select_task']
|
|
||||||
script="""
|
|
||||||
echo ../tools/bin/state2gantt -i {input[0]} -a {output[0]} -r {output[1]} -p {output[2]} -t {select_task}
|
|
||||||
../tools/bin/state2gantt -i {input[0]} -a {output[0]} -r {output[1]} -p {output[2]} -t {select_task}
|
|
||||||
"""
|
|
||||||
shell(script)
|
|
||||||
|
|
||||||
rule trace2gantt:
|
rule trace2gantt:
|
||||||
input:
|
input:
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}_case.jobs.csv",
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.csv"
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}_case.resp.csv",
|
|
||||||
"../tools/bin"
|
|
||||||
output:
|
output:
|
||||||
"{benchdir}/timedump/{fuzzer}/{target}#{num}_case.jobs.html",
|
"{remote}timedump/{fuzzer}/{target}.{num}.trace.csv.png"
|
||||||
shell:
|
shell:
|
||||||
"../tools/bin/plot_gantt.r {input[0]} {input[1]} html"
|
"Rscript --vanilla $(pwd)/../../../../state2gantt/gantt.R {input}"
|
||||||
|
|
||||||
rule quicktest:
|
rule all_main:
|
||||||
params:
|
|
||||||
benchdir=benchdir
|
|
||||||
input:
|
input:
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stg', 'random'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( 1 ))),
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random','afl','feedgeneration10','state'], target=['waters','watersv2'],num=range(0,3))
|
||||||
|
|
||||||
rule eval_bytes:
|
rule all_main_int:
|
||||||
params:
|
|
||||||
benchdir=benchdir
|
|
||||||
input:
|
input:
|
||||||
# waters bytes
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['random_int','afl_int','feedgeneration10_int','state_int'], target=['waters_int','watersv2_int'],num=range(0,4))
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=TARGET_SET, target=['waters'], variant=['_seq_bytes'], num=range(0,int( TARGET_REPLICA_NUMBER ))),
|
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_bytes'], num=range(0,int( RANDOM_REPLICA_NUMBER ))),
|
|
||||||
# polycopter full
|
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=TARGET_SET, target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( TARGET_REPLICA_NUMBER ))),
|
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['polycopter'], variant=['_seq_dataflow_full'], num=range(0,int( RANDOM_REPLICA_NUMBER ))),
|
|
||||||
|
|
||||||
rule eval_int:
|
rule all_compare_feedgeneration:
|
||||||
params:
|
|
||||||
benchdir=benchdir
|
|
||||||
input:
|
input:
|
||||||
# waters int
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1','feedgeneration10','feedgeneration100'], target=['waters_int','watersv2'],num=range(0,10))
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=TARGET_SET, target=['waters'], variant=['_seq_int'], num=range(0,int( TARGET_REPLICA_NUMBER ))),
|
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_int'], num=range(0,int( RANDOM_REPLICA_NUMBER ))),
|
|
||||||
# release int
|
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=TARGET_SET, target=['release'], variant=['_seq_int'], num=range(0,int( TARGET_REPLICA_NUMBER ))),
|
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['release'], variant=['_seq_int'], num=range(0,int( RANDOM_REPLICA_NUMBER ))),
|
|
||||||
|
|
||||||
rule eval_full:
|
rule all_compare_feedgeneration_int:
|
||||||
params:
|
|
||||||
benchdir=benchdir
|
|
||||||
input:
|
input:
|
||||||
# waters full
|
expand("timedump/{fuzzer}/{target}.{num}", fuzzer=['feedgeneration1_int','feedgeneration10_int','feedgeneration100_int'], target=['waters_int','watersv2_int'],num=range(0,10))
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=TARGET_SET, target=['waters'], variant=['_seq_full'], num=range(0,int( TARGET_REPLICA_NUMBER ))),
|
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['waters'], variant=['_seq_full'], num=range(0,int( RANDOM_REPLICA_NUMBER ))),
|
|
||||||
# release full
|
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=TARGET_SET, target=['release'], variant=['_seq_full'], num=range(0,int( TARGET_REPLICA_NUMBER ))),
|
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{variant}#{num}.time", benchdir=benchdir, fuzzer=['random'], target=['release'], variant=['_seq_full'], num=range(0,int( RANDOM_REPLICA_NUMBER ))),
|
|
||||||
|
|
||||||
rule waters_multi:
|
rule all_compare_afl:
|
||||||
params:
|
|
||||||
benchdir=benchdir
|
|
||||||
input:
|
input:
|
||||||
expand("{benchdir}/timedump/{fuzzer}/{target}{chain}{variant}#{num}.time", benchdir=benchdir, fuzzer=['feedgeneration100', 'stgwoet', 'frafl'], target=['waters'], chain=['Ic11','Ic12','Ic13','Ic14','Ic21','Ic22','Ic23','Ic32','Ic33'], variant=['_seq_full'], num=range(0,int( MULTIJOB_REPLICA_NUMBER ))), # 'Ic31'
|
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:
|
||||||
params:
|
|
||||||
benchdir=benchdir
|
|
||||||
input:
|
input:
|
||||||
expand("{benchdir}/bins/target_{target}", benchdir=benchdir, target=TARGET_SET+['random'])
|
expand("bins/target_{target}{flag}",target=['random','afl','frafl','state','feedgeneration100'],flag=['','_int'])
|
||||||
|
|
||||||
rule clean:
|
|
||||||
shell:
|
|
||||||
"rm -rf {benchdir}/timedump"
|
|
||||||
|
|
||||||
rule full_clean:
|
|
||||||
shell:
|
|
||||||
"rm -rf {benchdir}/bins & rm -rf {benchdir}/timedump"
|
|
||||||
|
|
||||||
rule plot_benchmarks:
|
|
||||||
shell:
|
|
||||||
"bash scripts/plot_all_benchmarks.sh {benchdir}"
|
|
||||||
|
|
||||||
rule plot_traces:
|
|
||||||
shell:
|
|
||||||
"bash scripts/plot_all_traces.sh {benchdir}"
|
|
83
fuzzers/FRET/benchmark/plot_comparison.r
Normal file
83
fuzzers/FRET/benchmark/plot_comparison.r
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
library("mosaic")
|
||||||
|
args = commandArgs(trailingOnly=TRUE)
|
||||||
|
|
||||||
|
#myolors=c("#339933","#0066ff","#993300") # grün, balu, rot
|
||||||
|
myolors=c("dark green","dark blue","dark red", "yellow") # grün, balu, rot
|
||||||
|
|
||||||
|
if (length(args)==0) {
|
||||||
|
runtype="timedump"
|
||||||
|
target="waters"
|
||||||
|
filename_1=sprintf("%s.png",target)
|
||||||
|
filename_2=sprintf("%s_maxline.png",target)
|
||||||
|
filename_3=sprintf("%s_hist.png",target)
|
||||||
|
} else {
|
||||||
|
runtype=args[1]
|
||||||
|
target=args[2]
|
||||||
|
filename_1=sprintf("%s.png",args[2])
|
||||||
|
filename_2=sprintf("%s_maxline.png",args[2])
|
||||||
|
filename_3=sprintf("%s_hist.png",args[2])
|
||||||
|
# filename_1=args[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
file_1=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_state",runtype,target)
|
||||||
|
file_2=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_afl",runtype,target)
|
||||||
|
file_3=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_random",runtype,target)
|
||||||
|
file_4=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s/%s_graph",runtype,target)
|
||||||
|
timetrace <- read.table(file_1, quote="\"", comment.char="")
|
||||||
|
timetrace_afl <- read.table(file_2, quote="\"", comment.char="")
|
||||||
|
timetrace_rand <- read.table(file_3, quote="\"", comment.char="")
|
||||||
|
timetrace_graph <- read.table(file_4, quote="\"", comment.char="")
|
||||||
|
timetrace[[2]]=seq_len(length(timetrace[[1]]))
|
||||||
|
timetrace_afl[[2]]=seq_len(length(timetrace_afl[[1]]))
|
||||||
|
timetrace_rand[[2]]=seq_len(length(timetrace_rand[[1]]))
|
||||||
|
timetrace_graph[[2]]=seq_len(length(timetrace_graph[[1]]))
|
||||||
|
names(timetrace)[1] <- "timetrace"
|
||||||
|
names(timetrace)[2] <- "iter"
|
||||||
|
names(timetrace_afl)[1] <- "timetrace"
|
||||||
|
names(timetrace_afl)[2] <- "iter"
|
||||||
|
names(timetrace_rand)[1] <- "timetrace"
|
||||||
|
names(timetrace_rand)[2] <- "iter"
|
||||||
|
names(timetrace_graph)[1] <- "timetrace"
|
||||||
|
names(timetrace_graph)[2] <- "iter"
|
||||||
|
|
||||||
|
png(file=filename_1)
|
||||||
|
# pdf(file=filename_1,width=8, height=8)
|
||||||
|
plot(timetrace[[2]],timetrace[[1]], col=myolors[1], xlab="iters", ylab="wcet", pch='.')
|
||||||
|
points(timetrace_afl[[2]],timetrace_afl[[1]], col=myolors[2], pch='.')
|
||||||
|
points(timetrace_rand[[2]],timetrace_rand[[1]], col=myolors[3], pch='.')
|
||||||
|
points(timetrace_graph[[2]],timetrace_graph[[1]], col=myolors[4], pch='.')
|
||||||
|
abline(lm(timetrace ~ iter, data=timetrace),col=myolors[1])
|
||||||
|
abline(lm(timetrace ~ iter, data=timetrace_afl),col=myolors[2])
|
||||||
|
abline(lm(timetrace ~ iter, data=timetrace_rand),col=myolors[3])
|
||||||
|
dev.off()
|
||||||
|
|
||||||
|
png(file=filename_3)
|
||||||
|
gf_histogram(~ timetrace,data=timetrace, fill=myolors[1]) %>%
|
||||||
|
gf_histogram(~ timetrace,data=timetrace_afl, fill=myolors[2]) %>%
|
||||||
|
gf_histogram(~ timetrace,data=timetrace_rand, fill=myolors[3]) %>%
|
||||||
|
gf_histogram(~ timetrace,data=timetrace_graph, fill=myolors[4])
|
||||||
|
dev.off()
|
||||||
|
|
||||||
|
# Takes a flat list
|
||||||
|
trace2maxline <- function(tr) {
|
||||||
|
maxline = tr
|
||||||
|
for (var in seq_len(length(maxline))[2:length(maxline)]) {
|
||||||
|
maxline[var] = max(maxline[var],maxline[var-1])
|
||||||
|
}
|
||||||
|
#plot(seq_len(length(maxline)),maxline,"l",xlab="Index",ylab="WOET")
|
||||||
|
return(maxline)
|
||||||
|
}
|
||||||
|
timetrace[[1]] <- trace2maxline(timetrace[[1]])
|
||||||
|
timetrace_afl[[1]] <- trace2maxline(timetrace_afl[[1]])
|
||||||
|
timetrace_rand[[1]] <- trace2maxline(timetrace_rand[[1]])
|
||||||
|
timetrace_graph[[1]] <- trace2maxline(timetrace_graph[[1]])
|
||||||
|
|
||||||
|
png(file=filename_2)
|
||||||
|
plot(timetrace[[2]],timetrace[[1]], col=myolors[1], xlab="iters", ylab="wcet", pch='.')
|
||||||
|
points(timetrace_afl[[2]],timetrace_afl[[1]], col=myolors[2], pch='.')
|
||||||
|
points(timetrace_rand[[2]],timetrace_rand[[1]], col=myolors[3], pch='.')
|
||||||
|
points(timetrace_graph[[2]],timetrace_graph[[1]], col=myolors[4], pch='.')
|
||||||
|
#abline(lm(timetrace ~ iter, data=timetrace),col=myolors[1])
|
||||||
|
#abline(lm(timetrace ~ iter, data=timetrace_afl),col=myolors[2])
|
||||||
|
#abline(lm(timetrace ~ iter, data=timetrace_rand),col=myolors[3])
|
||||||
|
dev.off()
|
327
fuzzers/FRET/benchmark/plot_multi.r
Normal file
327
fuzzers/FRET/benchmark/plot_multi.r
Normal file
@ -0,0 +1,327 @@
|
|||||||
|
library("mosaic")
|
||||||
|
library("dplyr")
|
||||||
|
library("foreach")
|
||||||
|
library("doParallel")
|
||||||
|
|
||||||
|
#setup parallel backend to use many processors
|
||||||
|
cores=detectCores()
|
||||||
|
cl <- makeCluster(cores[1]-1) #not to overload your computer
|
||||||
|
registerDoParallel(cl)
|
||||||
|
|
||||||
|
args = commandArgs(trailingOnly=TRUE)
|
||||||
|
|
||||||
|
if (length(args)==0) {
|
||||||
|
runtype="timedump_253048_1873f6_all/timedump"
|
||||||
|
target="waters_int"
|
||||||
|
outputpath="~/code/FRET/LibAFL/fuzzers/FRET/benchmark/"
|
||||||
|
#MY_SELECTION <- c('state', 'afl', 'graph', 'random')
|
||||||
|
SAVE_FILE=TRUE
|
||||||
|
} else {
|
||||||
|
runtype=args[1]
|
||||||
|
target=args[2]
|
||||||
|
outputpath=args[3]
|
||||||
|
MY_SELECTION <- args[4:length(args)]
|
||||||
|
SAVE_FILE=TRUE
|
||||||
|
}
|
||||||
|
worst_cases <- list(waters=0, waters_int=0, tmr=405669, micro_longint=0)
|
||||||
|
worst_case <- worst_cases[[target]]
|
||||||
|
if (is.null(worst_case)) {
|
||||||
|
worst_case = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
#MY_COLORS=c("green","blue","red", "orange", "pink", "black")
|
||||||
|
MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown")
|
||||||
|
BENCHDIR=sprintf("~/code/FRET/LibAFL/fuzzers/FRET/benchmark/%s",runtype)
|
||||||
|
BASENAMES=Filter(function(x) x!="" && substr(x,1,1)!='.',list.dirs(BENCHDIR,full.names=FALSE))
|
||||||
|
PATTERNS="%s.[0-9]*$"
|
||||||
|
#RIBBON='sd'
|
||||||
|
#RIBBON='span'
|
||||||
|
RIBBON='both'
|
||||||
|
DRAW_WC = worst_case > 0
|
||||||
|
LEGEND_POS="topright"
|
||||||
|
#LEGEND_POS="bottomright"
|
||||||
|
CONTINUE_LINE_TO_END=FALSE
|
||||||
|
|
||||||
|
# https://www.r-bloggers.com/2013/04/how-to-change-the-alpha-value-of-colours-in-r/
|
||||||
|
alpha <- function(col, alpha=1){
|
||||||
|
if(missing(col))
|
||||||
|
stop("Please provide a vector of colours.")
|
||||||
|
apply(sapply(col, col2rgb)/255, 2,
|
||||||
|
function(x)
|
||||||
|
rgb(x[1], x[2], x[3], alpha=alpha))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Trimm a list of data frames to common length
|
||||||
|
trim_data <- function(input,len=NULL) {
|
||||||
|
if (is.null(len)) {
|
||||||
|
len <- min(sapply(input, function(v) dim(v)[1]))
|
||||||
|
}
|
||||||
|
return(lapply(input, function(d) slice_head(d,n=len)))
|
||||||
|
}
|
||||||
|
|
||||||
|
length_of_data <- function(input) {
|
||||||
|
min(sapply(input, function(v) dim(v)[1]))
|
||||||
|
}
|
||||||
|
|
||||||
|
# Takes a flat list
|
||||||
|
trace2maxline <- function(tr) {
|
||||||
|
maxline = tr
|
||||||
|
for (var in seq_len(length(maxline))[2:length(maxline)]) {
|
||||||
|
#if (maxline[var]>1000000000) {
|
||||||
|
# maxline[var]=maxline[var-1]
|
||||||
|
#} else {
|
||||||
|
maxline[var] = max(maxline[var],maxline[var-1])
|
||||||
|
#}
|
||||||
|
}
|
||||||
|
#plot(seq_len(length(maxline)),maxline,"l",xlab="Index",ylab="WOET")
|
||||||
|
return(maxline)
|
||||||
|
}
|
||||||
|
|
||||||
|
# Take a list of data frames, output same form but maxlines
|
||||||
|
data2maxlines <- function(tr) {
|
||||||
|
min_length <- min(sapply(tr, function(v) dim(v)[1]))
|
||||||
|
maxline <- tr
|
||||||
|
for (var in seq_len(length(tr))) {
|
||||||
|
maxline[[var]][[1]]=trace2maxline(tr[[var]][[1]])
|
||||||
|
}
|
||||||
|
return(maxline)
|
||||||
|
}
|
||||||
|
# Take a multi-column data frame, output same form but maxlines
|
||||||
|
frame2maxlines <- function(tr) {
|
||||||
|
for (var in seq_len(length(tr))) {
|
||||||
|
tr[[var]]=trace2maxline(tr[[var]])
|
||||||
|
}
|
||||||
|
return(tr)
|
||||||
|
}
|
||||||
|
|
||||||
|
trace2maxpoints <- function(tr) {
|
||||||
|
minval = tr[1,1]
|
||||||
|
collect = tr[1,]
|
||||||
|
for (i in seq_len(dim(tr)[1])) {
|
||||||
|
if (minval < tr[i,1]) {
|
||||||
|
collect = rbind(collect,tr[i,])
|
||||||
|
minval = tr[i,1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tmp = tr[dim(tr)[1],]
|
||||||
|
tmp[1] = minval[1]
|
||||||
|
collect = rbind(collect,tmp)
|
||||||
|
return(collect)
|
||||||
|
}
|
||||||
|
|
||||||
|
sample_maxpoints <- function(tr,po) {
|
||||||
|
index = 1
|
||||||
|
collect=NULL
|
||||||
|
endpoint = dim(tr)[1]
|
||||||
|
for (p in po) {
|
||||||
|
if (p<=tr[1,2]) {
|
||||||
|
tmp = tr[index,]
|
||||||
|
tmp[2] = p
|
||||||
|
collect = rbind(collect, tmp)
|
||||||
|
} else if (p>=tr[endpoint,2]) {
|
||||||
|
tmp = tr[endpoint,]
|
||||||
|
tmp[2] = p
|
||||||
|
collect = rbind(collect, tmp)
|
||||||
|
} else {
|
||||||
|
for (i in seq(index,endpoint)-1) {
|
||||||
|
if (p >= tr[i,2] && p<tr[i+1,2]) {
|
||||||
|
tmp = tr[i,]
|
||||||
|
tmp[2] = p
|
||||||
|
collect = rbind(collect, tmp)
|
||||||
|
index = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return(collect)
|
||||||
|
}
|
||||||
|
|
||||||
|
#https://www.r-bloggers.com/2012/01/parallel-r-loops-for-windows-and-linux/
|
||||||
|
all_runtypetables <- foreach (bn=BASENAMES) %do% {
|
||||||
|
runtypefiles <- list.files(file.path(BENCHDIR,bn),pattern=sprintf(PATTERNS,target),full.names = TRUE)
|
||||||
|
if (length(runtypefiles) > 0) {
|
||||||
|
runtypetables_reduced <- foreach(i=seq_len(length(runtypefiles))) %dopar% {
|
||||||
|
rtable = read.csv(runtypefiles[[i]], col.names=c(sprintf("%s%d",bn,i),sprintf("times%d",i)))
|
||||||
|
trace2maxpoints(rtable)
|
||||||
|
}
|
||||||
|
#runtypetables <- lapply(seq_len(length(runtypefiles)),
|
||||||
|
# function(i)read.csv(runtypefiles[[i]], col.names=c(sprintf("%s%d",bn,i),sprintf("times%d",i))))
|
||||||
|
#runtypetables_reduced <- lapply(runtypetables, trace2maxpoints)
|
||||||
|
runtypetables_reduced
|
||||||
|
#all_runtypetables = c(all_runtypetables, list(runtypetables_reduced))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
all_runtypetables = all_runtypetables[lapply(all_runtypetables, length) > 0]
|
||||||
|
all_min_points = foreach(rtt=all_runtypetables,.combine = cbind) %do% {
|
||||||
|
bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1)
|
||||||
|
ret = data.frame(min(unlist(lapply(rtt, function(v) v[dim(v)[1],2]))))
|
||||||
|
names(ret)[1] = bn
|
||||||
|
ret/(3600 * 1000)
|
||||||
|
}
|
||||||
|
all_max_points = foreach(rtt=all_runtypetables,.combine = cbind) %do% {
|
||||||
|
bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1)
|
||||||
|
ret = data.frame(max(unlist(lapply(rtt, function(v) v[dim(v)[1],2]))))
|
||||||
|
names(ret)[1] = bn
|
||||||
|
ret/(3600 * 1000)
|
||||||
|
}
|
||||||
|
all_points = sort(unique(Reduce(c, lapply(all_runtypetables, function(v) Reduce(c, lapply(v, function(w) w[[2]]))))))
|
||||||
|
all_maxlines <- foreach (rtt=all_runtypetables) %do% {
|
||||||
|
bn = substr(names(rtt[[1]])[1],1,nchar(names(rtt[[1]])[1])-1)
|
||||||
|
runtypetables_sampled = foreach(v=rtt) %dopar% {
|
||||||
|
sample_maxpoints(v, all_points)[1]
|
||||||
|
}
|
||||||
|
#runtypetables_sampled = lapply(rtt, function(v) sample_maxpoints(v, all_points)[1])
|
||||||
|
tmp_frame <- Reduce(cbind, runtypetables_sampled)
|
||||||
|
statframe <- data.frame(rowMeans(tmp_frame),apply(tmp_frame, 1, sd),apply(tmp_frame, 1, min),apply(tmp_frame, 1, max), apply(tmp_frame, 1, median))
|
||||||
|
names(statframe) <- c(bn, sprintf("%s_sd",bn), sprintf("%s_min",bn), sprintf("%s_max",bn), sprintf("%s_med",bn))
|
||||||
|
#statframe[sprintf("%s_times",bn)] = all_points
|
||||||
|
round(statframe)
|
||||||
|
#all_maxlines = c(all_maxlines, list(round(statframe)))
|
||||||
|
}
|
||||||
|
one_frame<-data.frame(all_maxlines)
|
||||||
|
one_frame[length(one_frame)+1] <- all_points/(3600 * 1000)
|
||||||
|
names(one_frame)[length(one_frame)] <- 'time'
|
||||||
|
|
||||||
|
typenames = names(one_frame)[which(names(one_frame) != 'time')]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_sd"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_med"))]
|
||||||
|
ylow=min(one_frame[typenames])
|
||||||
|
yhigh=max(one_frame[typenames],worst_case)
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_min"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_max"))]
|
||||||
|
|
||||||
|
ml2lines <- function(ml,lim) {
|
||||||
|
lines = NULL
|
||||||
|
last = 0
|
||||||
|
for (i in seq_len(dim(ml)[1])) {
|
||||||
|
if (!CONTINUE_LINE_TO_END && lim<ml[i,2]) {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
lines = rbind(lines, cbind(X=last, Y=ml[i,1]))
|
||||||
|
lines = rbind(lines, cbind(X=ml[i,2], Y=ml[i,1]))
|
||||||
|
last = ml[i,2]
|
||||||
|
}
|
||||||
|
return(lines)
|
||||||
|
}
|
||||||
|
|
||||||
|
plotting <- function(selection, filename, MY_COLORS_) {
|
||||||
|
# filter out names of iters and sd cols
|
||||||
|
typenames = names(one_frame)[which(names(one_frame) != 'times')]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_sd"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_med"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_min"))]
|
||||||
|
typenames = typenames[which(!endsWith(typenames, "_max"))]
|
||||||
|
typenames = selection[which(selection %in% typenames)]
|
||||||
|
if (length(typenames) == 0) {return()}
|
||||||
|
|
||||||
|
h_ = 500
|
||||||
|
w_ = h_*4/3
|
||||||
|
|
||||||
|
if (SAVE_FILE) {png(file=sprintf("%s%s_%s.png",outputpath,target,filename), width=w_, height=h_)}
|
||||||
|
par(mar=c(4,4,1,1))
|
||||||
|
par(oma=c(0,0,0,0))
|
||||||
|
|
||||||
|
plot(c(1,max(one_frame['time'])),c(ylow,yhigh), col='white', xlab="Time [h]", ylab="WORT [insn]", pch='.')
|
||||||
|
|
||||||
|
for (t in seq_len(length(typenames))) {
|
||||||
|
#proj = one_frame[seq(1, dim(one_frame)[1], by=max(1, length(one_frame[[1]])/(10*w_))),]
|
||||||
|
#points(proj[c('iters',typenames[t])], col=MY_COLORS_[t], pch='.')
|
||||||
|
avglines = ml2lines(one_frame[c(typenames[t],'time')],all_max_points[typenames[t]])
|
||||||
|
#lines(avglines, col=MY_COLORS_[t])
|
||||||
|
medlines = ml2lines(one_frame[c(sprintf("%s_med",typenames[t]),'time')],all_max_points[typenames[t]])
|
||||||
|
lines(medlines, col=MY_COLORS_[t], lty='solid')
|
||||||
|
milines = NULL
|
||||||
|
malines = NULL
|
||||||
|
milines = ml2lines(one_frame[c(sprintf("%s_min",typenames[t]),'time')],all_max_points[typenames[t]])
|
||||||
|
malines = ml2lines(one_frame[c(sprintf("%s_max",typenames[t]),'time')],all_max_points[typenames[t]])
|
||||||
|
if (exists("RIBBON") && ( RIBBON=='max' )) {
|
||||||
|
#lines(milines, col=MY_COLORS_[t], lty='dashed')
|
||||||
|
lines(malines, col=MY_COLORS_[t], lty='dashed')
|
||||||
|
#points(proj[c('iters',sprintf("%s_min",typenames[t]))], col=MY_COLORS_[t], pch='.')
|
||||||
|
#points(proj[c('iters',sprintf("%s_max",typenames[t]))], col=MY_COLORS_[t], pch='.')
|
||||||
|
}
|
||||||
|
if (exists("RIBBON") && RIBBON != '') {
|
||||||
|
for (i in seq_len(dim(avglines)[1]-1)) {
|
||||||
|
if (RIBBON=='both') {
|
||||||
|
# draw boxes
|
||||||
|
x_l <- milines[i,][['X']]
|
||||||
|
x_r <- milines[i+1,][['X']]
|
||||||
|
y_l <- milines[i,][['Y']]
|
||||||
|
y_h <- malines[i,][['Y']]
|
||||||
|
rect(x_l, y_l, x_r, y_h, col=alpha(MY_COLORS_[t], alpha=0.1), lwd=0)
|
||||||
|
}
|
||||||
|
if (FALSE && RIBBON=='span') {
|
||||||
|
# draw boxes
|
||||||
|
x_l <- milines[i,][['X']]
|
||||||
|
x_r <- milines[i+1,][['X']]
|
||||||
|
y_l <- milines[i,][['Y']]
|
||||||
|
y_h <- malines[i,][['Y']]
|
||||||
|
rect(x_l, y_l, x_r, y_h, col=alpha(MY_COLORS_[t], alpha=0.1), lwd=0)
|
||||||
|
}
|
||||||
|
#if (FALSE && RIBBON=='both' || RIBBON=='sd') {
|
||||||
|
# # draw sd
|
||||||
|
# x_l <- avglines[i,][['X']]
|
||||||
|
# x_r <- avglines[i+1,][['X']]
|
||||||
|
# y_l <- avglines[i,][['Y']]-one_frame[ceiling(i/2),][[sprintf("%s_sd",typenames[t])]]
|
||||||
|
# y_h <- avglines[i,][['Y']]+one_frame[ceiling(i/2),][[sprintf("%s_sd",typenames[t])]]
|
||||||
|
# if (x_r != x_l) {
|
||||||
|
# rect(x_l, y_l, x_r, y_h, col=alpha(MY_COLORS_[t], alpha=0.1), lwd=0)
|
||||||
|
# }
|
||||||
|
#}
|
||||||
|
#sd_ <- row[sprintf("%s_sd",typenames[t])][[1]]
|
||||||
|
#min_ <- row[sprintf("%s_min",typenames[t])][[1]]
|
||||||
|
#max_ <- row[sprintf("%s_max",typenames[t])][[1]]
|
||||||
|
#if (exists("RIBBON")) {
|
||||||
|
# switch (RIBBON,
|
||||||
|
# 'sd' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03)),
|
||||||
|
# 'both' = arrows(x_, y_-sd_, x_, y_+sd_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.05)),
|
||||||
|
# 'span' = #arrows(x_, min_, x_, max_, length=0, angle=90, code=3, col=alpha(MY_COLORS_[t], alpha=0.03))
|
||||||
|
# )
|
||||||
|
#}
|
||||||
|
##arrows(x_, y_-sd_, x_, y_+sd_, length=0.05, angle=90, code=3, col=alpha(MY_COLORS[t], alpha=0.1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
leglines=typenames
|
||||||
|
if (DRAW_WC) {
|
||||||
|
lines(c(0,length(one_frame[[1]])),y=c(worst_case,worst_case), lty='dotted')
|
||||||
|
leglines=c(typenames, 'worst observed')
|
||||||
|
}
|
||||||
|
legend(LEGEND_POS, legend=leglines,#"topleft"
|
||||||
|
col=c(MY_COLORS_[1:length(typenames)],"black"),
|
||||||
|
lty=c(rep("solid",length(typenames)),"dotted"))
|
||||||
|
|
||||||
|
if (SAVE_FILE) {dev.off()}
|
||||||
|
}
|
||||||
|
stopCluster(cl)
|
||||||
|
|
||||||
|
par(mar=c(3.8,3.8,0,0))
|
||||||
|
par(oma=c(0,0,0,0))
|
||||||
|
|
||||||
|
#RIBBON='both'
|
||||||
|
#MY_SELECTION = c('state_int','generation100_int')
|
||||||
|
#MY_SELECTION = c('state_int','frafl_int')
|
||||||
|
|
||||||
|
if (exists("MY_SELECTION")) {
|
||||||
|
plotting(MY_SELECTION, 'custom', MY_COLORS[c(1,2)])
|
||||||
|
} else {
|
||||||
|
# MY_SELECTION=c('state', 'afl', 'random', 'feedlongest', 'feedgeneration', 'feedgeneration10')
|
||||||
|
#MY_SELECTION=c('state_int', 'afl_int', 'random_int', 'feedlongest_int', 'feedgeneration_int', 'feedgeneration10_int')
|
||||||
|
#MY_SELECTION=c('state', 'frAFL', 'statenohash', 'feedgeneration10')
|
||||||
|
#MY_SELECTION=c('state_int', 'frAFL_int', 'statenohash_int', 'feedgeneration10_int')
|
||||||
|
MY_SELECTION=typenames
|
||||||
|
RIBBON='both'
|
||||||
|
for (i in seq_len(length(MY_SELECTION))) {
|
||||||
|
n <- MY_SELECTION[i]
|
||||||
|
plotting(c(n), n, c(MY_COLORS[i]))
|
||||||
|
}
|
||||||
|
RIBBON='max'
|
||||||
|
plotting(MY_SELECTION,'all', MY_COLORS)
|
||||||
|
}
|
||||||
|
|
||||||
|
for (t in seq_len(length(typenames))) {
|
||||||
|
li = one_frame[dim(one_frame)[1],]
|
||||||
|
pear = (li[[typenames[[t]]]]-li[[sprintf("%s_med",typenames[[t]])]])/li[[sprintf("%s_sd",typenames[[t]])]]
|
||||||
|
print(sprintf("%s pearson: %g",typenames[[t]],pear))
|
||||||
|
}
|
@ -1,5 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
export TOPLEVEL="remote/timedump"
|
|
||||||
[ -d "$TOPLEVEL/feedgeneration100" ] && mv $TOPLEVEL/feedgeneration100 $TOPLEVEL/evolutionary
|
|
||||||
[ -d "$TOPLEVEL/stg" ] && mv $TOPLEVEL/stg $TOPLEVEL/fret
|
|
||||||
[ -d "$TOPLEVEL/frafl" ] && mv $TOPLEVEL/frafl $TOPLEVEL/coverage
|
|
@ -1,15 +0,0 @@
|
|||||||
def_flags="--no-default-features --features std,snapshot_fast,restarting,do_hash_notify_state,trace_job_response_times,fuzz_int"
|
|
||||||
set -e
|
|
||||||
cargo build --target-dir ./bins/target_showmap ${def_flags},config_stg
|
|
||||||
cargo build --target-dir ./bins/target_random ${def_flags},feed_longest
|
|
||||||
cargo build --target-dir ./bins/target_frafl ${def_flags},config_frafl,feed_longest
|
|
||||||
cargo build --target-dir ./bins/target_afl ${def_flags},config_afl,observe_hitcounts
|
|
||||||
cargo build --target-dir ./bins/target_stg ${def_flags},config_stg
|
|
||||||
cargo build --target-dir ./bins/target_stgpath ${def_flags},feed_stg_abbhash,sched_stg_abbhash,mutate_stg
|
|
||||||
cargo build --target-dir ./bins/target_feedgeneration1 ${def_flags},feed_genetic,gensize_1
|
|
||||||
cargo build --target-dir ./bins/target_feedgeneration10 ${def_flags},feed_genetic,gensize_10
|
|
||||||
cargo build --target-dir ./bins/target_feedgeneration100 ${def_flags},feed_genetic,gensize_100
|
|
||||||
cargo build --target-dir ./bins/target_feedgeneration1000 ${def_flags},feed_genetic,gensize_1000
|
|
||||||
cargo build --target-dir ./bins/target_genetic100 ${def_flags},feed_genetic,mutate_stg,gensize_100
|
|
||||||
cargo build --target-dir ./bins/target_genetic1000 ${def_flags},feed_genetic,mutate_stg,gensize_1000
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
export INSERT_WC=${2:-0}
|
|
||||||
export BUILD_DIR=${1:-build}
|
|
||||||
mkdir -p $BUILD_DIR
|
|
||||||
|
|
||||||
build () {
|
|
||||||
make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC clean && make -C ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC $1=1 IGNORE_INTERRUPTS=$IGNORE_INTERRUPTS IGNORE_BYTES=$IGNORE_BYTES IGNORE_INTERNAL_STATE=$IGNORE_INTERNAL_STATE INSERT_WC=$INSERT_WC $EXTRA_MAKE_ARGS
|
|
||||||
cp ../../../../FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/build/RTOSDemo.axf $BUILD_DIR/$(echo $1 | cut -d_ -f1 | tr '[:upper:]' '[:lower:]')$EXTRA_NAME_SUFFIX$2.elf
|
|
||||||
}
|
|
||||||
|
|
||||||
mkdir -p build
|
|
||||||
|
|
||||||
# Sequential inputs!
|
|
||||||
export PARTITION_INPUT=0
|
|
||||||
unset SPECIAL_CFLAGS
|
|
||||||
|
|
||||||
# Baseline
|
|
||||||
## Don't keep rng states
|
|
||||||
export IGNORE_INTERNAL_STATE=1
|
|
||||||
### Only bytes
|
|
||||||
export IGNORE_INTERRUPTS=1 IGNORE_BYTES=0 SUFFIX="_seq_bytes"
|
|
||||||
build WATERS_DEMO $SUFFIX
|
|
||||||
build RELEASE_DEMO $SUFFIX
|
|
||||||
build COPTER_DEMO $SUFFIX
|
|
||||||
### Only interrupts
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=1 SUFFIX="_seq_int"
|
|
||||||
build WATERS_DEMO $SUFFIX
|
|
||||||
build RELEASE_DEMO $SUFFIX
|
|
||||||
build COPTER_DEMO $SUFFIX
|
|
||||||
### Full
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_full"
|
|
||||||
build WATERS_DEMO $SUFFIX
|
|
||||||
build RELEASE_DEMO $SUFFIX
|
|
||||||
build COPTER_DEMO $SUFFIX
|
|
||||||
build POLYCOPTER_DEMO $SUFFIX
|
|
||||||
|
|
||||||
# Stateful -> presumably bad for us
|
|
||||||
## keep rng states
|
|
||||||
export IGNORE_INTERNAL_STATE=0
|
|
||||||
### Full
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_stateful_full"
|
|
||||||
build WATERS_DEMO $SUFFIX
|
|
||||||
build RELEASE_DEMO $SUFFIX
|
|
||||||
build COPTER_DEMO $SUFFIX
|
|
||||||
|
|
||||||
# Paritioned inputs
|
|
||||||
export PARTITION_INPUT=1
|
|
||||||
|
|
||||||
# Alternative input scheme
|
|
||||||
## Don't keep rng states
|
|
||||||
export IGNORE_INTERNAL_STATE=1
|
|
||||||
### Only bytes
|
|
||||||
export IGNORE_INTERRUPTS=1 IGNORE_BYTES=0 SUFFIX="_par_bytes"
|
|
||||||
build WATERS_DEMO $SUFFIX
|
|
||||||
build RELEASE_DEMO $SUFFIX
|
|
||||||
build COPTER_DEMO $SUFFIX
|
|
||||||
### Only interrupts
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=1 SUFFIX="_par_int"
|
|
||||||
build WATERS_DEMO $SUFFIX
|
|
||||||
build RELEASE_DEMO $SUFFIX
|
|
||||||
build COPTER_DEMO $SUFFIX
|
|
||||||
### Full
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_full"
|
|
||||||
build WATERS_DEMO $SUFFIX
|
|
||||||
build RELEASE_DEMO $SUFFIX
|
|
||||||
build COPTER_DEMO $SUFFIX
|
|
||||||
build POLYCOPTER_DEMO $SUFFIX
|
|
||||||
|
|
||||||
# Stateful -> presumably bad for us
|
|
||||||
## keep rng states
|
|
||||||
export IGNORE_INTERNAL_STATE=0
|
|
||||||
### Full
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_stateful_full"
|
|
||||||
build WATERS_DEMO $SUFFIX
|
|
||||||
build RELEASE_DEMO $SUFFIX
|
|
||||||
build COPTER_DEMO $SUFFIX
|
|
||||||
|
|
||||||
# Stateful -> presumably bad for us
|
|
||||||
## keep rng states
|
|
||||||
export IGNORE_INTERNAL_STATE=0
|
|
||||||
export PARTITION_INPUT=0
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_stateful_full"
|
|
||||||
build POLYCOPTER_DEMO $SUFFIX
|
|
||||||
|
|
||||||
# stateless + dataflow
|
|
||||||
export PARTITION_INPUT=0
|
|
||||||
export IGNORE_INTERNAL_STATE=1
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_dataflow_full"
|
|
||||||
export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1"
|
|
||||||
build POLYCOPTER_DEMO $SUFFIX
|
|
||||||
unset SPECIAL_CFLAGS
|
|
||||||
|
|
||||||
export PARTITION_INPUT=0
|
|
||||||
export IGNORE_INTERNAL_STATE=1
|
|
||||||
export IGNORE_INTERRUPTS=1 IGNORE_BYTES=0 SUFFIX="_seq_dataflow_bytes"
|
|
||||||
export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1"
|
|
||||||
build POLYCOPTER_DEMO $SUFFIX
|
|
||||||
unset SPECIAL_CFLAGS
|
|
||||||
|
|
||||||
# stateless + dataflow
|
|
||||||
export PARTITION_INPUT=1
|
|
||||||
export IGNORE_INTERNAL_STATE=1
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_par_dataflow_full"
|
|
||||||
export SPECIAL_CFLAGS="-DCOPTER_DATAFLOW=1"
|
|
||||||
build POLYCOPTER_DEMO $SUFFIX
|
|
||||||
unset SPECIAL_CFLAGS
|
|
||||||
|
|
||||||
|
|
||||||
# special waters with no synchronization
|
|
||||||
export PARTITION_INPUT=0
|
|
||||||
export IGNORE_INTERNAL_STATE=1
|
|
||||||
export IGNORE_INTERRUPTS=0 IGNORE_BYTES=0 SUFFIX="_seq_unsync_full"
|
|
||||||
export SPECIAL_CFLAGS="-DWATERS_UNSYNCHRONIZED=1"
|
|
||||||
build WATERS_DEMO $SUFFIX
|
|
||||||
unset SPECIAL_CFLAGS
|
|
||||||
|
|
||||||
# Create copies with special names
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc12_seq_full.elf
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc13_seq_full.elf
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc14_seq_full.elf
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc11_seq_full.elf
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc21_seq_full.elf
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc22_seq_full.elf
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc23_seq_full.elf
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc31_seq_full.elf
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc32_seq_full.elf
|
|
||||||
cp -f $BUILD_DIR/waters_seq_full.elf $BUILD_DIR/watersIc33_seq_full.elf
|
|
@ -1,8 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
find $1 -type 'f' -iname "${2}#*.log" | while IFS="" read -r p || [ -n "$p" ]
|
|
||||||
do
|
|
||||||
LINE=$(tail -n 100 $p | grep -io "run time: .* corpus: [0-9]*" | tail -n 1)
|
|
||||||
echo $p: $LINE
|
|
||||||
LINE=$(grep -i "interesting corpus elements" $p | tail -n 1)
|
|
||||||
echo $p: $LINE
|
|
||||||
done
|
|
@ -1,14 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
if [[ -n "$1" ]]; then
|
|
||||||
TARGET="$1"
|
|
||||||
else
|
|
||||||
TARGET=$BENCHDIR
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if bench.sqlite needs to be updated
|
|
||||||
if [[ ! -f $TARGET/bench.sqlite || $(find $TARGET/timedump -name '.*[0-9]+\.time' -newer $TARGET/bench.sqlite | wc -l) -gt 0 ]]; then
|
|
||||||
number_cruncher -i $TARGET/timedump -o $TARGET/bench.sqlite
|
|
||||||
fi
|
|
||||||
|
|
||||||
Rscript scripts/plot_sqlite.r $TARGET/bench.sqlite $TARGET
|
|
||||||
Rscript scripts/plot_diffs.r $TARGET/bench.sqlite $TARGET
|
|
@ -1,13 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
./sem.sh /tmp/plot reset 20
|
|
||||||
declare -a PLOTS
|
|
||||||
COUNT=0
|
|
||||||
while IFS="" read -r p || [ -n "$p" ];
|
|
||||||
do
|
|
||||||
if [[ -z "$p" ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
PLOTS[$COUNT]="$p"
|
|
||||||
COUNT=$((COUNT+1))
|
|
||||||
../../../../state2gantt/driver_sem.sh $p &
|
|
||||||
done < <(find $BENCHDIR/timedump -maxdepth 2 -type 'f' -iregex '.*icounttrace.ron$')
|
|
@ -1,33 +0,0 @@
|
|||||||
get_max_nodecount () {
|
|
||||||
rm -f sizecomp && for sizefile in $BENCHDIR/timedump/**/$1*.stgsize;do echo "$(tail -n 1 $sizefile),${sizefile}" >> sizecomp; done; sort -n sizecomp | tail -n 1
|
|
||||||
}
|
|
||||||
|
|
||||||
get_largest_files () {
|
|
||||||
T=$(get_max_nodecount $1)
|
|
||||||
echo $T | cut -d',' -f6
|
|
||||||
}
|
|
||||||
|
|
||||||
perform () {
|
|
||||||
T=$(get_max_nodecount $1)
|
|
||||||
echo $T | cut -d',' -f6
|
|
||||||
echo $T | cut -d',' -f6 | xargs -I {} ./plot_stgsize.r {}
|
|
||||||
mv "$(echo $T | cut -d',' -f6 | xargs -I {} basename -s .stgsize {})_nodes.png" $1_nodes.png
|
|
||||||
}
|
|
||||||
|
|
||||||
# perform copter
|
|
||||||
# perform release
|
|
||||||
# perform waters
|
|
||||||
A=$(get_largest_files polycopter_seq_dataflow_full)
|
|
||||||
B=$(get_largest_files release_seq_full)
|
|
||||||
C=$(get_largest_files waters_seq_full)
|
|
||||||
# A_="$(echo $A | sed 's/polycopter_seq_dataflow_full/UAV w. hid. com./')"
|
|
||||||
# B_="$(echo $B | sed 's/release_seq_full/Async. rel./')"
|
|
||||||
# C_="$(echo $C | sed 's/waters_seq_full/Waters ind. ch./')"
|
|
||||||
A_="UAV"
|
|
||||||
B_="Async. rel."
|
|
||||||
C_="Waters ind. ch."
|
|
||||||
echo $A_ $B_ $C_
|
|
||||||
cp $A "$A_"
|
|
||||||
cp $B "$B_"
|
|
||||||
cp $C "$C_"
|
|
||||||
./plot_stgsize_multi.r "$A_" "$B_" "$C_"
|
|
@ -1,28 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
declare -a PLOTS
|
|
||||||
COUNT=0
|
|
||||||
while IFS="" read -r p || [ -n "$p" ];
|
|
||||||
do
|
|
||||||
if [[ -z "$p" ]]; then
|
|
||||||
continue
|
|
||||||
fi
|
|
||||||
N="$(dirname "$p")/$(basename -s .case "$p")"
|
|
||||||
T="${N}_case.trace.ron"
|
|
||||||
P="${N}_case"
|
|
||||||
H="${N}_case.jobs.html"
|
|
||||||
echo "$COUNT $p -> $H"
|
|
||||||
IFS=" "
|
|
||||||
# PLOTS+=("$H")
|
|
||||||
PLOTS[$COUNT]="$H"
|
|
||||||
COUNT=$((COUNT+1))
|
|
||||||
|
|
||||||
# if [ ! -f "$T" ]; then
|
|
||||||
# snakemake -c1 "$T"
|
|
||||||
# fi
|
|
||||||
# if [ ! -f "$P.html" ]; then
|
|
||||||
# ~/code/FRET/state2gantt/driver.sh "$T"
|
|
||||||
# fi
|
|
||||||
done < <(find $BENCHDIR/timedump -maxdepth 2 -type 'f' -iregex '.*[0-9]+\.case')
|
|
||||||
|
|
||||||
echo "${PLOTS[@]}"
|
|
||||||
snakemake -c 20 --rerun-incomplete --keep-incomplete "${PLOTS[@]}"
|
|
@ -1,235 +0,0 @@
|
|||||||
# install.packages(c("mosaic", "dplyr", "DBI", "tikzDevice", "colorspace", "heatmaply", "RColorBrewer", "RSQLite"))
|
|
||||||
library("mosaic")
|
|
||||||
library("dplyr")
|
|
||||||
library("DBI")
|
|
||||||
library("tikzDevice") # Add this line to include the tikzDevice library
|
|
||||||
library("colorspace")
|
|
||||||
library("heatmaply")
|
|
||||||
library("RColorBrewer")
|
|
||||||
|
|
||||||
args = commandArgs(trailingOnly=TRUE)
|
|
||||||
|
|
||||||
TOOL_TRANSLATION <- list(
|
|
||||||
feedgeneration100 = "evolution",
|
|
||||||
frafl = "coverage",
|
|
||||||
random = "random",
|
|
||||||
stgwoet = "FRET"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
KNOWN_WCRT <- list(
|
|
||||||
waters_seq_bytes=0, # via INSERT_WC
|
|
||||||
waters_seq_int=0, # via INSERT_WC + manual interrupt
|
|
||||||
#waters_seq_int=219542, # via INSERT_WC + manual interrupt
|
|
||||||
waters_seq_full=0,# via INSERT_WC + manual interrupt
|
|
||||||
waters_seq_unsync_full=0,# via INSERT_WC + manual interrupt
|
|
||||||
polycopter_seq_dataflow_full=0, # via INSERT_WC + manual interrupt
|
|
||||||
polycopter_seq_dataflow_int=0, # via INSERT_WC + manual interrupt
|
|
||||||
release_seq_int=0, # via fuzzer, equals to manual interrupts; Bug: Task3 y=0
|
|
||||||
release_seq_full=0 # via INSERT_WC + manual interrupt; Bug: Task3 y=0
|
|
||||||
)
|
|
||||||
|
|
||||||
STATIC_WCRT <- list(
|
|
||||||
waters_seq_bytes=256632,
|
|
||||||
waters_seq_int=256632,
|
|
||||||
waters_seq_full=256632,
|
|
||||||
waters_seq_unsync_full=272091,
|
|
||||||
polycopter_seq_dataflow_full=373628,
|
|
||||||
polycopter_seq_dataflow_int=373628,
|
|
||||||
release_seq_int=921360,
|
|
||||||
release_seq_full=921360
|
|
||||||
)
|
|
||||||
|
|
||||||
# ISNS_PER_US = (10**3)/(2**5)
|
|
||||||
# print(list(sapply(STATIC_WCRT, function(x) x/ISNS_PER_US)))
|
|
||||||
# quit()
|
|
||||||
|
|
||||||
STATIC_WCRT <- list(
|
|
||||||
waters_seq_bytes=0,
|
|
||||||
waters_seq_int=0,
|
|
||||||
waters_seq_full=0,
|
|
||||||
waters_seq_unsync_full=0,
|
|
||||||
polycopter_seq_dataflow_full=0,
|
|
||||||
polycopter_seq_dataflow_int=0,
|
|
||||||
release_seq_int=0,
|
|
||||||
release_seq_full=0
|
|
||||||
)
|
|
||||||
|
|
||||||
MIN_Y <- list(
|
|
||||||
waters_seq_bytes=0,
|
|
||||||
waters_seq_int=0,
|
|
||||||
waters_seq_full=0,
|
|
||||||
waters_seq_unsync_full=0,
|
|
||||||
polycopter_seq_dataflow_full=0,
|
|
||||||
polycopter_seq_dataflow_int=0,
|
|
||||||
release_seq_int=0,
|
|
||||||
release_seq_full=0
|
|
||||||
)
|
|
||||||
|
|
||||||
LEG_POS <- list(
|
|
||||||
waters_seq_bytes="bottomright",
|
|
||||||
waters_seq_int="bottomright",
|
|
||||||
waters_seq_full="bottomright",
|
|
||||||
waters_seq_unsync_full="bottomright",
|
|
||||||
polycopter_seq_dataflow_full="bottomright",
|
|
||||||
polycopter_seq_dataflow_int="bottomright",
|
|
||||||
release_seq_int="bottomright",
|
|
||||||
release_seq_full="bottomright"
|
|
||||||
)
|
|
||||||
|
|
||||||
NAME_MAP <- list(
|
|
||||||
watersIc11_seq_full="t1 10ms",
|
|
||||||
watersIc12_seq_full="t2 10ms",
|
|
||||||
watersIc13_seq_full="t3 10ms",
|
|
||||||
watersIc14_seq_full="t4 10ms",
|
|
||||||
watersIc31_seq_full="t5 spro",
|
|
||||||
watersIc32_seq_full="t6 2ms",
|
|
||||||
watersIc33_seq_full="t7 50ms",
|
|
||||||
watersIc21_seq_full="t9 100ms",
|
|
||||||
watersIc22_seq_full="t10 10ms",
|
|
||||||
watersIc23_seq_full="t11 2ms"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Read the first command line argument as an sqlite file
|
|
||||||
if (length(args) > 0) {
|
|
||||||
sqlite_file <- args[1]
|
|
||||||
con <- dbConnect(RSQLite::SQLite(), sqlite_file)
|
|
||||||
} else {
|
|
||||||
print("No sqlite file provided, assume defaults")
|
|
||||||
args = c("bench.sqlite", "remote")
|
|
||||||
sqlite_file <- args[1]
|
|
||||||
con <- dbConnect(RSQLite::SQLite(), sqlite_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
combos <- dbGetQuery(con, "SELECT * FROM combos")
|
|
||||||
casenames <- dbGetQuery(con, "SELECT casename FROM combos WHERE casename LIKE 'watersIc_%' GROUP BY casename")
|
|
||||||
#casenames <- dbGetQuery(con, "SELECT casename FROM combos GROUP BY casename")
|
|
||||||
toolnames <- dbGetQuery(con, "SELECT toolname FROM combos GROUP BY toolname")
|
|
||||||
|
|
||||||
ml2lines <- function(ml, casename) {
|
|
||||||
lines = NULL
|
|
||||||
last = 0
|
|
||||||
for (i in seq_len(dim(ml)[1])) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
# BREW=RdYlGn(8)
|
|
||||||
BREW=Spectral(8)
|
|
||||||
|
|
||||||
# MY_COLORS <- c(BREW[[4]], BREW[[3]], BREW[[2]], BREW[[1]], "cyan", "pink", "gray", "orange", "black", "yellow","brown")
|
|
||||||
MY_COLORS=BREW
|
|
||||||
|
|
||||||
# draw limit
|
|
||||||
max_x <- 12
|
|
||||||
min_y <- -2500
|
|
||||||
max_y <- 2500
|
|
||||||
|
|
||||||
LEGEND_POS = "bottomright"
|
|
||||||
ISNS_PER_US = (10**3)/(2**5)
|
|
||||||
|
|
||||||
print(casenames[['casename']])
|
|
||||||
|
|
||||||
legend_names <- sapply(casenames[['casename']], function(x) NAME_MAP[[x]] %||% x)
|
|
||||||
legend_colors <- BREW
|
|
||||||
legend_styles <- c(rep("solid",10),"dotted","dashed")
|
|
||||||
|
|
||||||
|
|
||||||
h_ = 300
|
|
||||||
w_ = h_*4/3
|
|
||||||
|
|
||||||
png(file=sprintf("%s/all_tasks.png", args[2]), width=w_, height=h_)
|
|
||||||
#tikz(file=sprintf("%s/all_tasks.tex", args[2]), width=0.6*w_/72, height=0.6*h_/72)
|
|
||||||
#pdf(file=sprintf("%s/all_tasks.pdf", args[2]), width=w_/72, height=h_/72)
|
|
||||||
|
|
||||||
|
|
||||||
# plot setup
|
|
||||||
par(mar=c(4,4,1,1))
|
|
||||||
par(oma=c(0,0,0,0))
|
|
||||||
|
|
||||||
plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="FRET's improvement over competitors [µs]", pch='.')
|
|
||||||
|
|
||||||
draw_plot <- function(data, casename, color) {
|
|
||||||
# evo, cov, random, fret
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Pre-calculate all malines and medlines
|
|
||||||
malines_list <- list()
|
|
||||||
medlines_list <- list()
|
|
||||||
for (n in seq_along(data)) {
|
|
||||||
d <- data[[n]]
|
|
||||||
malines_list[[names(data)[n]]] <- ml2lines(d[c('max','timestamp')])
|
|
||||||
medlines_list[[names(data)[n]]] <- ml2lines(d[c('median','timestamp')])
|
|
||||||
}
|
|
||||||
|
|
||||||
# Plot the difference between malines['stgwoet'] (FRET) and malines['random']
|
|
||||||
if ("stgwoet" %in% names(malines_list) && "feedgeneration100" %in% names(malines_list)) {
|
|
||||||
fret_malines <- malines_list[["stgwoet"]]
|
|
||||||
compare_malines1 <- malines_list[["feedgeneration100"]]
|
|
||||||
compare_malines2 <- malines_list[["frafl"]]
|
|
||||||
fret_medlines <- medlines_list[["stgwoet"]]
|
|
||||||
compare_medlines1 <- medlines_list[["feedgeneration100"]]
|
|
||||||
compare_medlines2 <- medlines_list[["frafl"]]
|
|
||||||
|
|
||||||
# Ensure all have the same number of rows and matching X
|
|
||||||
min_len <- min(nrow(fret_malines), nrow(compare_malines1), nrow(compare_malines2))
|
|
||||||
# For each point, take the max of the two compare malines
|
|
||||||
compare_max_Y <- pmax(compare_malines1[1:min_len, "Y"], compare_malines2[1:min_len, "Y"])
|
|
||||||
diff_lines_ma <- data.frame(
|
|
||||||
X = fret_malines[1:min_len, "X"],
|
|
||||||
Y = fret_malines[1:min_len, "Y"] - compare_max_Y
|
|
||||||
)
|
|
||||||
lines(diff_lines_ma, col=color, lty="solid", lwd=2)
|
|
||||||
|
|
||||||
# Same for medlines
|
|
||||||
compare_max_med_Y <- pmax(compare_medlines1[1:min_len, "Y"], compare_medlines2[1:min_len, "Y"])
|
|
||||||
diff_lines_med <- data.frame(
|
|
||||||
X = fret_medlines[1:min_len, "X"],
|
|
||||||
Y = fret_medlines[1:min_len, "Y"] - compare_max_med_Y
|
|
||||||
)
|
|
||||||
lines(diff_lines_med, col=color, lty="dashed", lwd=2)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for (i in seq_len(length(casenames[['casename']]))) {
|
|
||||||
cn =casenames[['casename']][i]
|
|
||||||
color = MY_COLORS[i]
|
|
||||||
tables <- dbGetQuery(con, sprintf("SELECT * FROM combos WHERE casename == '%s'", cn[[1]]))
|
|
||||||
table_list <- list()
|
|
||||||
for (row in 1:nrow(tables)) {
|
|
||||||
table_name <- tables[row, 'fullname']
|
|
||||||
tool_name <- tables[row, 'toolname']
|
|
||||||
table_data <- dbGetQuery(con, sprintf("SELECT * FROM '%s'", table_name))
|
|
||||||
table_list[[tool_name]] <- table_data
|
|
||||||
}
|
|
||||||
# Convert timestamp from microseconds to hours
|
|
||||||
for (n in seq_len(length(table_list))) {
|
|
||||||
table_list[[n]]$timestamp <- table_list[[n]]$timestamp / 3600000
|
|
||||||
table_list[[n]]$min <- table_list[[n]]$min / ISNS_PER_US
|
|
||||||
table_list[[n]]$max <- table_list[[n]]$max / ISNS_PER_US
|
|
||||||
table_list[[n]]$median <- table_list[[n]]$median / ISNS_PER_US
|
|
||||||
table_list[[n]]$mean <- table_list[[n]]$mean / ISNS_PER_US
|
|
||||||
table_list[[n]]$sdiv <- table_list[[n]]$sdiv / ISNS_PER_US
|
|
||||||
}
|
|
||||||
|
|
||||||
table_list <- table_list[c('stgwoet', 'feedgeneration100', 'frafl', 'random')] # manual re-order
|
|
||||||
table_list <- table_list[!sapply(table_list, is.null)] # remove NULL entries
|
|
||||||
draw_plot(table_list, cn[[1]], color)
|
|
||||||
}
|
|
||||||
legend(LEGEND_POS, legend=legend_names,#"bottomright",
|
|
||||||
col=legend_colors,
|
|
||||||
lty=legend_styles,
|
|
||||||
lwd=2, ncol=2)
|
|
||||||
|
|
||||||
par(las = 2, mar = c(10, 5, 1, 1))
|
|
||||||
|
|
||||||
# png
|
|
||||||
## normal
|
|
||||||
dev.off()
|
|
||||||
|
|
||||||
dbDisconnect(con)
|
|
@ -1,238 +0,0 @@
|
|||||||
# install.packages(c("mosaic", "dplyr", "DBI", "tikzDevice", "colorspace", "heatmaply", "RColorBrewer", "RSQLite"))
|
|
||||||
library("mosaic")
|
|
||||||
library("dplyr")
|
|
||||||
library("DBI")
|
|
||||||
library("tikzDevice") # Add this line to include the tikzDevice library
|
|
||||||
library("colorspace")
|
|
||||||
library("heatmaply")
|
|
||||||
library("RColorBrewer")
|
|
||||||
|
|
||||||
args = commandArgs(trailingOnly=TRUE)
|
|
||||||
|
|
||||||
TOOL_TRANSLATION <- list(
|
|
||||||
feedgeneration100 = "evolution",
|
|
||||||
frafl = "coverage",
|
|
||||||
random = "random",
|
|
||||||
stgwoet = "FRET"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
KNOWN_WCRT <- list(
|
|
||||||
waters_seq_bytes=212252, # via INSERT_WC
|
|
||||||
waters_seq_int=0, # via INSERT_WC + manual interrupt
|
|
||||||
#waters_seq_int=219542, # via INSERT_WC + manual interrupt
|
|
||||||
waters_seq_full=219542,# via INSERT_WC + manual interrupt
|
|
||||||
waters_seq_unsync_full=234439,# via INSERT_WC + manual interrupt
|
|
||||||
polycopter_seq_dataflow_full=174866, # via INSERT_WC + manual interrupt
|
|
||||||
polycopter_seq_dataflow_int=174866, # via INSERT_WC + manual interrupt
|
|
||||||
release_seq_int=582699, # via fuzzer, equals to manual interrupts; Bug: Task3 y=0
|
|
||||||
release_seq_full=614583 # via INSERT_WC + manual interrupt; Bug: Task3 y=0
|
|
||||||
)
|
|
||||||
|
|
||||||
STATIC_WCRT <- list(
|
|
||||||
waters_seq_bytes=256632,
|
|
||||||
waters_seq_int=256632,
|
|
||||||
waters_seq_full=256632,
|
|
||||||
waters_seq_unsync_full=272091,
|
|
||||||
polycopter_seq_dataflow_full=373628,
|
|
||||||
polycopter_seq_dataflow_int=373628,
|
|
||||||
release_seq_int=921360,
|
|
||||||
release_seq_full=921360
|
|
||||||
)
|
|
||||||
|
|
||||||
# ISNS_PER_US = (10**3)/(2**5)
|
|
||||||
# print(list(sapply(STATIC_WCRT, function(x) x/ISNS_PER_US)))
|
|
||||||
# quit()
|
|
||||||
|
|
||||||
STATIC_WCRT <- list(
|
|
||||||
waters_seq_bytes=0,
|
|
||||||
waters_seq_int=0,
|
|
||||||
waters_seq_full=0,
|
|
||||||
waters_seq_unsync_full=0,
|
|
||||||
polycopter_seq_dataflow_full=0,
|
|
||||||
polycopter_seq_dataflow_int=0,
|
|
||||||
release_seq_int=0,
|
|
||||||
release_seq_full=0
|
|
||||||
)
|
|
||||||
|
|
||||||
MIN_Y <- list(
|
|
||||||
waters_seq_bytes=5250,
|
|
||||||
waters_seq_int=5700,
|
|
||||||
waters_seq_full=5250,
|
|
||||||
waters_seq_unsync_full=0,
|
|
||||||
polycopter_seq_dataflow_full=0,
|
|
||||||
polycopter_seq_dataflow_int=0,
|
|
||||||
release_seq_int=16500,
|
|
||||||
release_seq_full=16500
|
|
||||||
)
|
|
||||||
|
|
||||||
LEG_POS <- list(
|
|
||||||
waters_seq_bytes="bottomright",
|
|
||||||
waters_seq_int="bottomright",
|
|
||||||
waters_seq_full="bottomright",
|
|
||||||
waters_seq_unsync_full="bottomright",
|
|
||||||
polycopter_seq_dataflow_full="bottomright",
|
|
||||||
polycopter_seq_dataflow_int="bottomright",
|
|
||||||
release_seq_int="bottomright",
|
|
||||||
release_seq_full="bottomright"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Read the first command line argument as an sqlite file
|
|
||||||
if (length(args) > 0) {
|
|
||||||
sqlite_file <- args[1]
|
|
||||||
con <- dbConnect(RSQLite::SQLite(), sqlite_file)
|
|
||||||
} else {
|
|
||||||
print("No sqlite file provided, assume defaults")
|
|
||||||
args = c("bench.sqlite", "remote")
|
|
||||||
sqlite_file <- args[1]
|
|
||||||
con <- dbConnect(RSQLite::SQLite(), sqlite_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
combos <- dbGetQuery(con, "SELECT * FROM combos")
|
|
||||||
casenames <- dbGetQuery(con, "SELECT casename FROM combos WHERE NOT casename LIKE 'watersIc_%' GROUP BY casename")
|
|
||||||
# casenames <- dbGetQuery(con, "SELECT casename FROM combos GROUP BY casename")
|
|
||||||
toolnames <- dbGetQuery(con, "SELECT toolname FROM combos GROUP BY toolname")
|
|
||||||
|
|
||||||
ml2lines <- function(ml, casename) {
|
|
||||||
lines = NULL
|
|
||||||
last = 0
|
|
||||||
for (i in seq_len(dim(ml)[1])) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
BREW=RdYlGn(4)
|
|
||||||
# BREW=Spectral(4)
|
|
||||||
|
|
||||||
draw_plot <- function(data, casename) {
|
|
||||||
# evo, cov, random, fret
|
|
||||||
MY_COLORS <- c(BREW[[4]], BREW[[3]], BREW[[2]], BREW[[1]], "cyan", "pink", "gray", "orange", "black", "yellow","brown")
|
|
||||||
# MY_COLORS <- c("orange", "blue", "red", "green", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown")
|
|
||||||
# MY_COLORS <- c("green", "blue", "red", "magenta", "orange", "cyan", "pink", "gray", "orange", "black", "yellow","brown")
|
|
||||||
LEGEND_POS=LEG_POS[[casename]]
|
|
||||||
if (is.null(LEGEND_POS)) {
|
|
||||||
LEGEND_POS = "bottomright"
|
|
||||||
}
|
|
||||||
|
|
||||||
ISNS_PER_US = (10**3)/(2**5)
|
|
||||||
|
|
||||||
# Convert timestamp from microseconds to hours
|
|
||||||
for (n in seq_len(length(data))) {
|
|
||||||
data[[n]]$timestamp <- data[[n]]$timestamp / 3600000
|
|
||||||
data[[n]]$min <- data[[n]]$min / ISNS_PER_US
|
|
||||||
data[[n]]$max <- data[[n]]$max / ISNS_PER_US
|
|
||||||
data[[n]]$median <- data[[n]]$median / ISNS_PER_US
|
|
||||||
data[[n]]$mean <- data[[n]]$mean / ISNS_PER_US
|
|
||||||
data[[n]]$sdiv <- data[[n]]$sdiv / ISNS_PER_US
|
|
||||||
}
|
|
||||||
|
|
||||||
data <- data[c('stgwoet', 'feedgeneration100', 'frafl', 'random')] # manual re-order
|
|
||||||
data <- data[!sapply(data, is.null)] # remove NULL entries
|
|
||||||
|
|
||||||
wcrt = KNOWN_WCRT[[casename]]
|
|
||||||
if (!is.null(wcrt)) {
|
|
||||||
wcrt = wcrt / ISNS_PER_US
|
|
||||||
} else {
|
|
||||||
wcrt = 0
|
|
||||||
}
|
|
||||||
static_wcrt = STATIC_WCRT[[casename]]
|
|
||||||
if (!is.null(static_wcrt)) {
|
|
||||||
static_wcrt = static_wcrt / ISNS_PER_US
|
|
||||||
} else {
|
|
||||||
static_wcrt = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
# draw limits
|
|
||||||
max_x <- max(sapply(data, function(tbl) max(tbl$timestamp, na.rm = TRUE)))
|
|
||||||
max_x <- min(max_x, 24) # quick fix, cap to 16h
|
|
||||||
max_y <- max(wcrt,max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE))))
|
|
||||||
min_y <- min(sapply(data, function(tbl) min(tbl$min, na.rm = TRUE)))
|
|
||||||
min_y <- max(min_y, MIN_Y[[casename]])
|
|
||||||
|
|
||||||
# draw static wcrt
|
|
||||||
max_y <- max(max_y, static_wcrt)
|
|
||||||
|
|
||||||
# plot setup
|
|
||||||
par(mar=c(4,4,1,1))
|
|
||||||
par(oma=c(0,0,0,0))
|
|
||||||
|
|
||||||
plot(c(0,max_x),c(min_y,max_y), col='white', xlab="Time [h]", ylab="WORT [µs]", pch='.')
|
|
||||||
|
|
||||||
# plot data
|
|
||||||
for (n in seq_len(length(data))) {
|
|
||||||
d <- data[[n]]
|
|
||||||
malines = ml2lines(d[c('max','timestamp')])
|
|
||||||
lines(malines, col=MY_COLORS[[n]], lty='solid', lwd=2) # Increase line width
|
|
||||||
medlines = ml2lines(d[c('median','timestamp')])
|
|
||||||
lines(medlines, col=MY_COLORS[[n]], lty='dashed', lwd=2) # Increase line width
|
|
||||||
# milines = ml2lines(d[c('min','timestamp')])
|
|
||||||
# lines(milines, col=MY_COLORS[[n]], lty='dashed', lwd=2) # Increase line width
|
|
||||||
}
|
|
||||||
|
|
||||||
legend_names <- sapply(names(data), function(n) TOOL_TRANSLATION[[n]])
|
|
||||||
legend_colors <- c(MY_COLORS[1:length(data)],"grey","grey")
|
|
||||||
legend_styles <- c(rep("solid",length(data)),"dotted","dashed")
|
|
||||||
|
|
||||||
if (wcrt > 0) {
|
|
||||||
# abline(h=wcrt, col='grey', lty='dotted', lwd=3)
|
|
||||||
abline(h=max(wcrt,max(sapply(data, function(tbl) max(tbl$max, na.rm = TRUE)))), col='grey', lty='dotted', lwd=3) # If the manual WCRT was slightly too low
|
|
||||||
legend_names <- c(legend_names, "WCRT")
|
|
||||||
}
|
|
||||||
if (static_wcrt > 0) {
|
|
||||||
abline(h=static_wcrt, col='grey', lty='dashed', lwd=3)
|
|
||||||
legend_names <- c(legend_names, "static bound")
|
|
||||||
}
|
|
||||||
|
|
||||||
legend(LEGEND_POS, legend=legend_names,#"bottomright",
|
|
||||||
col=legend_colors,
|
|
||||||
lty=legend_styles,
|
|
||||||
lwd=2)
|
|
||||||
|
|
||||||
par(las = 2, mar = c(10, 5, 1, 1))
|
|
||||||
}
|
|
||||||
|
|
||||||
print(casenames[['casename']])
|
|
||||||
for (cn in casenames[['casename']]) {
|
|
||||||
tables <- dbGetQuery(con, sprintf("SELECT * FROM combos WHERE casename == '%s'", cn[[1]]))
|
|
||||||
table_list <- list()
|
|
||||||
for (row in 1:nrow(tables)) {
|
|
||||||
table_name <- tables[row, 'fullname']
|
|
||||||
tool_name <- tables[row, 'toolname']
|
|
||||||
table_data <- dbGetQuery(con, sprintf("SELECT * FROM '%s'", table_name))
|
|
||||||
table_list[[tool_name]] <- table_data
|
|
||||||
}
|
|
||||||
h_ = 300
|
|
||||||
w_ = h_*4/3
|
|
||||||
# png
|
|
||||||
## normal
|
|
||||||
png(file=sprintf("%s/sql_%s.png", args[2],cn[[1]]), width=w_, height=h_)
|
|
||||||
draw_plot(table_list, cn[[1]])
|
|
||||||
dev.off()
|
|
||||||
## wide
|
|
||||||
png(file=sprintf("%s/sql_%s_wide.png", args[2],cn[[1]]), width=2*w_, height=h_)
|
|
||||||
draw_plot(table_list, cn[[1]])
|
|
||||||
dev.off()
|
|
||||||
# tikz
|
|
||||||
## normal
|
|
||||||
tikz(file=sprintf("%s/sql_%s.tex", args[2],cn[[1]]), width=0.6*w_/72, height=0.6*h_/72)
|
|
||||||
draw_plot(table_list, cn[[1]])
|
|
||||||
dev.off()
|
|
||||||
## wide
|
|
||||||
tikz(file=sprintf("%s/sql_%s_wide.tex", args[2],cn[[1]]), width=(w_*2)/72, height=h_/72)
|
|
||||||
draw_plot(table_list, cn[[1]])
|
|
||||||
dev.off()
|
|
||||||
# pdf
|
|
||||||
## normal
|
|
||||||
pdf(file=sprintf("%s/sql_%s.pdf", args[2],cn[[1]]), width=w_/72, height=h_/72)
|
|
||||||
draw_plot(table_list, cn[[1]])
|
|
||||||
dev.off()
|
|
||||||
## wide
|
|
||||||
pdf(file=sprintf("%s/sql_%s_wide.pdf", args[2],cn[[1]]), width=2*w_/72, height=h_/72)
|
|
||||||
draw_plot(table_list, cn[[1]])
|
|
||||||
dev.off()
|
|
||||||
}
|
|
||||||
|
|
||||||
dbDisconnect(con)
|
|
@ -1,23 +0,0 @@
|
|||||||
#!/usr/bin/env Rscript
|
|
||||||
# Load necessary libraries
|
|
||||||
library(ggplot2)
|
|
||||||
|
|
||||||
# Define the function to load CSV and plot
|
|
||||||
plot_stgsize <- function(file_path) {
|
|
||||||
print(file_path)
|
|
||||||
# Read the CSV file without headers
|
|
||||||
data <- read.csv(file_path, header = FALSE)
|
|
||||||
data['V5'] <- data['V5']/(3600*1000)
|
|
||||||
|
|
||||||
# Plot the line chart
|
|
||||||
p <- ggplot(data, aes(x = V5, y = V2)) +
|
|
||||||
geom_line() +
|
|
||||||
labs(x = "runtime [h]", y = "# of nodes") + #, title = "Number of nodes over time.") +
|
|
||||||
theme_minimal()
|
|
||||||
|
|
||||||
output_file <- sub("\\.stgsize$", paste0("_nodes.png"), file_path)
|
|
||||||
ggsave(basename(output_file), plot = p + theme_bw(base_size = 10), width = 3.5, height = 2, dpi = 300, units = "in", device = "png")
|
|
||||||
}
|
|
||||||
|
|
||||||
args <- commandArgs(trailingOnly = TRUE)
|
|
||||||
plot_stgsize(args[1])
|
|
@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env Rscript
|
|
||||||
library(ggplot2)
|
|
||||||
|
|
||||||
# Function to plot multiple files
|
|
||||||
plot_multiple_files <- function(file_paths) {
|
|
||||||
all_data <- data.frame()
|
|
||||||
|
|
||||||
for (file_path in file_paths) {
|
|
||||||
# Read the CSV file without headers
|
|
||||||
data <- read.csv(file_path, header = FALSE)
|
|
||||||
data['V5'] <- data['V5']/(3600*1000)
|
|
||||||
|
|
||||||
# Extract the name for the line
|
|
||||||
application <- sub("_.*", "", basename(file_path))
|
|
||||||
data$application <- application
|
|
||||||
|
|
||||||
# Combine data
|
|
||||||
all_data <- rbind(all_data, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Plot the line chart
|
|
||||||
p <- ggplot(all_data, aes(x = V5, y = V2, color = application)) +
|
|
||||||
geom_line() +
|
|
||||||
labs(x = "runtime [h]", y = "# of nodes") +
|
|
||||||
theme_minimal()
|
|
||||||
|
|
||||||
# Save the plot
|
|
||||||
ggsave("stg_node_sizes.png", plot = p + theme_bw(base_size = 10), width = 4, height = 1.5, dpi = 300, units = "in", device = "png")
|
|
||||||
}
|
|
||||||
|
|
||||||
# Example usage
|
|
||||||
file_paths <- commandArgs(trailingOnly = TRUE)
|
|
||||||
plot_multiple_files(file_paths)
|
|
@ -1,60 +1,24 @@
|
|||||||
kernel,main_function,input_symbol,input_size,return_function,select_task,interrupts
|
kernel,main_function,input_symbol,input_size,return_function
|
||||||
waters_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
mpeg2,mpeg2_main,mpeg2_oldorgframe,90112,mpeg2_return
|
||||||
waters_seq_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
audiobeam,audiobeam_main,audiobeam_input,11520,audiobeam_return
|
||||||
waters_seq_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,
|
epic,epic_main,epic_image,4096,epic_return
|
||||||
waters_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
dijkstra,dijkstra_main,dijkstra_AdjMatrix,10000,dijkstra_return
|
||||||
waters_par_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
fft,fft_main,fft_twidtable,2046,fft_return
|
||||||
waters_par_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,
|
bsort,bsort_main,bsort_Array,400,bsort_return
|
||||||
waters_seq_stateful_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
insertsort,insertsort_main,insertsort_a,400,insertsort_return
|
||||||
waters_seq_stateful_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
g723_enc,g723_enc_main,g723_enc_INPUT,1024,g723_enc_return
|
||||||
waters_seq_stateful_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,
|
rijndael_dec,rijndael_dec_main,rijndael_dec_data,32768,rijndael_dec_return
|
||||||
waters_par_stateful_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
rijndael_enc,rijndael_enc_main,rijndael_enc_data,31369,rijndael_enc_return
|
||||||
waters_par_stateful_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
huff_dec,huff_dec_main,huff_dec_encoded,419,huff_dec_return
|
||||||
waters_par_stateful_bytes,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,
|
huff_enc,huff_enc_main,huff_enc_plaintext,600,huff_enc_return
|
||||||
release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000
|
gsm_enc,gsm_enc_main,gsm_enc_pcmdata,6400,gsm_enc_return
|
||||||
release_seq_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000
|
tmr,main,FUZZ_INPUT,32,trigger_Qemu_break
|
||||||
release_seq_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000
|
tacle_rtos,prvStage0,FUZZ_INPUT,604,trigger_Qemu_break
|
||||||
release_seq_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,
|
lift,main_lift,FUZZ_INPUT,100,trigger_Qemu_break
|
||||||
release_par_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000
|
waters,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
|
||||||
release_par_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000
|
watersv2,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
|
||||||
release_par_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,
|
waters_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
|
||||||
release_seq_stateful_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000
|
watersv2_int,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break
|
||||||
release_seq_stateful_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000
|
micro_branchless,main_branchless,FUZZ_INPUT,4,trigger_Qemu_break
|
||||||
release_seq_stateful_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,
|
micro_int,main_int,FUZZ_INPUT,16,trigger_Qemu_break
|
||||||
release_par_stateful_full,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000
|
micro_longint,main_micro_longint,FUZZ_INPUT,16,trigger_Qemu_break
|
||||||
release_par_stateful_int,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,0#30000;1#5000
|
|
||||||
release_par_stateful_bytes,main_release,FUZZ_INPUT,4096,trigger_Qemu_break,T3,
|
|
||||||
copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_seq_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_seq_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,
|
|
||||||
copter_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_par_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_par_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,
|
|
||||||
copter_seq_stateful_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_seq_stateful_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_seq_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,
|
|
||||||
copter_par_stateful_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_par_stateful_int,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_par_stateful_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,
|
|
||||||
copter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
copter_par_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
polycopter_par_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
polycopter_seq_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
polycopter_par_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
polycopter_seq_dataflow_full,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,0#20000
|
|
||||||
polycopter_seq_dataflow_bytes,main_osek,FUZZ_INPUT,4096,trigger_Qemu_break,FC,
|
|
||||||
watersc14_par_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000
|
|
||||||
watersc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000
|
|
||||||
waters_seq_unsync_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
|
||||||
watersgen1_par_bytes,main_waters,FUZZ_INPUT,40960,trigger_Qemu_break,T_24,0#10000;1#10000;2#10000;3#10000
|
|
||||||
watersIc11_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C11,0#1000
|
|
||||||
watersIc12_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C12,0#1000
|
|
||||||
watersIc13_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C13,0#1000
|
|
||||||
watersIc14_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C14,0#1000
|
|
||||||
watersIc21_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C21,0#1000
|
|
||||||
watersIc22_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C22,0#1000
|
|
||||||
watersIc23_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C23,0#1000
|
|
||||||
watersIc31_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C31,0#1000
|
|
||||||
watersIc32_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C32,0#1000
|
|
||||||
watersIc33_seq_full,main_waters,FUZZ_INPUT,4096,trigger_Qemu_break,C33,0#1000
|
|
|
25
fuzzers/FRET/fuzzer.sh
Executable file
25
fuzzers/FRET/fuzzer.sh
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
|
||||||
|
cd "$parent_path"
|
||||||
|
|
||||||
|
[ -n "$1" -a "$1" != "+" -a -z "$KERNEL" ] && export KERNEL="$1"
|
||||||
|
[ -n "$2" -a "$2" != "+" -a -z "$FUZZ_MAIN" ] && export FUZZ_MAIN="$2"
|
||||||
|
[ -n "$3" -a "$3" != "+" -a -z "$FUZZ_INPUT" ] && export FUZZ_INPUT="$3"
|
||||||
|
[ -n "$4" -a "$4" != "+" -a -z "$FUZZ_INPUT_LEN" ] && export FUZZ_INPUT_LEN="$4"
|
||||||
|
[ -n "$5" -a "$5" != "+" -a -z "$BREAKPOINT" ] && export BREAKPOINT="$5"
|
||||||
|
[ -n "$6" -a "$6" != "+" -a -z "$FUZZ_ITERS" ] && export FUZZ_ITERS="$6"
|
||||||
|
[ -n "$7" -a "$7" != "+" -a -z "$TIME_DUMP" ] && export TIME_DUMP="$7"
|
||||||
|
[ -n "$8" -a "$8" != "+" -a -z "$CASE_DUMP" ] && export CASE_DUMP="$8"
|
||||||
|
[ -n "$9" -a "$9" != "+" -a -z "$DO_SHOWMAP" ] && export DO_SHOWMAP="$9"
|
||||||
|
[ -n "${10}" -a "${10}" != "+" -a -z "$SHOWMAP_TEXTINPUT" ] && export SHOWMAP_TEXTINPUT="${10}"
|
||||||
|
[ -n "${11}" -a "${11}" != "+" -a -z "$TRACE_DUMP" ] && export TRACE_DUMP="${11}"
|
||||||
|
|
||||||
|
[ -z "$FUZZER" ] && export FUZZER=target/debug/fret
|
||||||
|
set +e
|
||||||
|
$FUZZER -icount shift=4,align=off,sleep=off -machine mps2-an385 -monitor null -kernel $KERNEL -serial null -nographic -S -semihosting --semihosting-config enable=on,target=native -snapshot -drive if=none,format=qcow2,file=dummy.qcow2
|
||||||
|
if [ "$exitcode" = "101" ]
|
||||||
|
then
|
||||||
|
exit 101
|
||||||
|
else
|
||||||
|
exit 0
|
||||||
|
fi
|
@ -1,121 +0,0 @@
|
|||||||
use clap::{Parser, Subcommand};
|
|
||||||
use std::path::PathBuf;
|
|
||||||
|
|
||||||
// Argument parsing ================================================================================
|
|
||||||
|
|
||||||
#[derive(Parser,Debug)]
|
|
||||||
#[command(author, version, about, long_about = None)]
|
|
||||||
pub struct Cli {
|
|
||||||
/// Kernel Image
|
|
||||||
#[arg(short, long, value_name = "FILE")]
|
|
||||||
pub kernel: PathBuf,
|
|
||||||
|
|
||||||
/// Sets a custom config file
|
|
||||||
#[arg(short, long, value_name = "FILE")]
|
|
||||||
pub config: PathBuf,
|
|
||||||
|
|
||||||
/// Sets the prefix of dumed files
|
|
||||||
#[arg(short='n', long, value_name = "FILENAME")]
|
|
||||||
pub dump_name: Option<PathBuf>,
|
|
||||||
|
|
||||||
/// do time dumps
|
|
||||||
#[arg(short='t', long)]
|
|
||||||
pub dump_times: bool,
|
|
||||||
|
|
||||||
/// do worst-case dumps
|
|
||||||
#[arg(short='a', long)]
|
|
||||||
pub dump_cases: bool,
|
|
||||||
|
|
||||||
/// do trace dumps (if supported)
|
|
||||||
#[arg(short='r', long)]
|
|
||||||
pub dump_traces: bool,
|
|
||||||
|
|
||||||
/// do graph dumps (if supported)
|
|
||||||
#[arg(short='g', long)]
|
|
||||||
pub dump_graph: bool,
|
|
||||||
|
|
||||||
/// select a task for measurments
|
|
||||||
#[arg(short='s', long)]
|
|
||||||
pub select_task: Option<String>,
|
|
||||||
|
|
||||||
#[command(subcommand)]
|
|
||||||
pub command: Commands,
|
|
||||||
}
|
|
||||||
#[derive(Subcommand,Clone,Debug)]
|
|
||||||
pub enum Commands {
|
|
||||||
/// run a single input
|
|
||||||
Showmap {
|
|
||||||
/// take this input
|
|
||||||
#[arg(short, long)]
|
|
||||||
input: PathBuf,
|
|
||||||
},
|
|
||||||
/// start fuzzing campaign
|
|
||||||
Fuzz {
|
|
||||||
/// disable heuristic
|
|
||||||
#[arg(short, long)]
|
|
||||||
random: bool,
|
|
||||||
/// seed for randomness
|
|
||||||
#[arg(short, long)]
|
|
||||||
seed: Option<u64>,
|
|
||||||
/// runtime in seconds
|
|
||||||
#[arg(short, long)]
|
|
||||||
time: Option<u64>,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn set_env_from_config(kernel : &PathBuf, path : &PathBuf) {
|
|
||||||
let is_csv = path.as_path().extension().map_or(false, |x| x=="csv");
|
|
||||||
if !is_csv {
|
|
||||||
let lines = std::fs::read_to_string(path).expect("Config file not found");
|
|
||||||
let lines = lines.lines().filter(
|
|
||||||
|x| x.len()>0
|
|
||||||
);
|
|
||||||
for l in lines {
|
|
||||||
let pair = l.split_once('=').expect("Non VAR=VAL line in config");
|
|
||||||
std::env::set_var(pair.0, pair.1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let mut reader = csv::Reader::from_path(path).expect("CSV read from config failed");
|
|
||||||
let p = kernel.as_path();
|
|
||||||
let stem = p.file_stem().expect("Kernel filename error").to_str().unwrap();
|
|
||||||
let mut found = false;
|
|
||||||
for r in reader.records() {
|
|
||||||
let rec = r.expect("CSV entry error");
|
|
||||||
if stem == &rec[0] {
|
|
||||||
println!("Config from file {:?}", rec);
|
|
||||||
found = true;
|
|
||||||
std::env::set_var("FUZZ_MAIN", &rec[1]);
|
|
||||||
std::env::set_var("FUZZ_INPUT", &rec[2]);
|
|
||||||
std::env::set_var("FUZZ_INPUT_LEN", &rec[3]);
|
|
||||||
std::env::set_var("BREAKPOINT", &rec[4]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !found {
|
|
||||||
eprintln!("No config found for kernel {:?}", stem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_interrupt_config(kernel : &PathBuf, path : &PathBuf) -> Vec<(usize,u32)>{
|
|
||||||
let is_csv = path.as_path().extension().map_or(false, |x| x=="csv");
|
|
||||||
if !is_csv {
|
|
||||||
panic!("Interrupt config must be inside a CSV file");
|
|
||||||
} else {
|
|
||||||
let mut reader = csv::Reader::from_path(path).expect("CSV read from config failed");
|
|
||||||
let p = kernel.as_path();
|
|
||||||
let stem = p.file_stem().expect("Kernel filename error").to_str().unwrap();
|
|
||||||
for r in reader.records() {
|
|
||||||
let rec = r.expect("CSV entry error");
|
|
||||||
if stem == &rec[0] {
|
|
||||||
let ret = rec[6].split(';').filter(|x| x != &"").map(|x| {
|
|
||||||
let pair = x.split_once('#').expect("Interrupt config error");
|
|
||||||
(pair.0.parse().expect("Interrupt config error"), pair.1.parse().expect("Interrupt config error"))
|
|
||||||
}).collect();
|
|
||||||
println!("Interrupt config {:?}", ret);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Vec::new();
|
|
||||||
}
|
|
344
fuzzers/FRET/src/clock.rs
Normal file
344
fuzzers/FRET/src/clock.rs
Normal file
@ -0,0 +1,344 @@
|
|||||||
|
use hashbrown::{hash_map::Entry, HashMap};
|
||||||
|
use libafl::{
|
||||||
|
bolts::{
|
||||||
|
current_nanos,
|
||||||
|
rands::StdRand,
|
||||||
|
tuples::{tuple_list},
|
||||||
|
},
|
||||||
|
executors::{ExitKind},
|
||||||
|
fuzzer::{StdFuzzer},
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
observers::{Observer,VariableMapObserver},
|
||||||
|
state::{StdState, HasNamedMetadata},
|
||||||
|
Error,
|
||||||
|
observers::ObserversTuple, prelude::UsesInput, impl_serdeany,
|
||||||
|
};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{cell::UnsafeCell, cmp::max, env, fs::OpenOptions, io::Write, time::Instant};
|
||||||
|
use libafl::bolts::tuples::Named;
|
||||||
|
|
||||||
|
use libafl_qemu::{
|
||||||
|
emu,
|
||||||
|
emu::Emulator,
|
||||||
|
executor::QemuExecutor,
|
||||||
|
helper::{QemuHelper, QemuHelperTuple, QemuInstrumentationFilter},
|
||||||
|
};
|
||||||
|
use libafl::events::EventFirer;
|
||||||
|
use libafl::state::HasClientPerfMonitor;
|
||||||
|
use libafl::inputs::Input;
|
||||||
|
use libafl::feedbacks::Feedback;
|
||||||
|
use libafl::SerdeAny;
|
||||||
|
use libafl::state::HasMetadata;
|
||||||
|
use libafl::corpus::testcase::Testcase;
|
||||||
|
use core::{fmt::Debug, time::Duration};
|
||||||
|
// use libafl::feedbacks::FeedbackState;
|
||||||
|
// use libafl::state::HasFeedbackStates;
|
||||||
|
use libafl::bolts::tuples::MatchName;
|
||||||
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
|
|
||||||
|
pub static mut FUZZ_START_TIMESTAMP : SystemTime = UNIX_EPOCH;
|
||||||
|
|
||||||
|
//========== Metadata
|
||||||
|
#[derive(Debug, SerdeAny, Serialize, Deserialize)]
|
||||||
|
pub struct QemuIcountMetadata {
|
||||||
|
runtime: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Metadata for [`QemuClockIncreaseFeedback`]
|
||||||
|
#[derive(Debug, Serialize, Deserialize, SerdeAny)]
|
||||||
|
pub struct MaxIcountMetadata {
|
||||||
|
pub max_icount_seen: u64,
|
||||||
|
pub name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
// impl FeedbackState for MaxIcountMetadata
|
||||||
|
// {
|
||||||
|
// fn reset(&mut self) -> Result<(), Error> {
|
||||||
|
// self.max_icount_seen = 0;
|
||||||
|
// Ok(())
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl Named for MaxIcountMetadata
|
||||||
|
{
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
self.name.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaxIcountMetadata
|
||||||
|
{
|
||||||
|
/// Create new `MaxIcountMetadata`
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(name: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
max_icount_seen: 0,
|
||||||
|
name: name.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for MaxIcountMetadata {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new("MaxClock")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A piece of metadata tracking all icounts
|
||||||
|
#[derive(Debug, SerdeAny, Serialize, Deserialize)]
|
||||||
|
pub struct IcHist (pub Vec<(u64, u128)>, pub (u64,u128));
|
||||||
|
|
||||||
|
//========== Observer
|
||||||
|
|
||||||
|
/// A simple observer, just overlooking the runtime of the target.
|
||||||
|
#[derive(Serialize, Deserialize, Debug, Clone)]
|
||||||
|
pub struct QemuClockObserver {
|
||||||
|
name: String,
|
||||||
|
start_tick: u64,
|
||||||
|
end_tick: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuClockObserver {
|
||||||
|
/// Creates a new [`QemuClockObserver`] with the given name.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(name: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
name: name.to_string(),
|
||||||
|
start_tick: 0,
|
||||||
|
end_tick: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets the runtime for the last execution of this target.
|
||||||
|
#[must_use]
|
||||||
|
pub fn last_runtime(&self) -> u64 {
|
||||||
|
self.end_tick - self.start_tick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Observer<S> for QemuClockObserver
|
||||||
|
where
|
||||||
|
S: UsesInput + HasMetadata,
|
||||||
|
{
|
||||||
|
fn pre_exec(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
|
// Only remember the pre-run ticks if presistent mode ist used
|
||||||
|
#[cfg(not(feature = "snapshot_restore"))]
|
||||||
|
unsafe {
|
||||||
|
self.start_tick=emu::icount_get_raw();
|
||||||
|
self.end_tick=self.start_tick;
|
||||||
|
}
|
||||||
|
// unsafe {
|
||||||
|
// println!("clock pre {}",emu::icount_get_raw());
|
||||||
|
// }
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn post_exec(&mut self, _state: &mut S, _input: &S::Input, _exit_kind: &ExitKind) -> Result<(), Error> {
|
||||||
|
unsafe { self.end_tick = emu::icount_get_raw() };
|
||||||
|
// println!("clock post {}", self.end_tick);
|
||||||
|
// println!("Number of Ticks: {} <- {} {}",self.end_tick - self.start_tick, self.end_tick, self.start_tick);
|
||||||
|
let metadata =_state.metadata_mut();
|
||||||
|
let hist = metadata.get_mut::<IcHist>();
|
||||||
|
let timestamp = SystemTime::now().duration_since(unsafe {FUZZ_START_TIMESTAMP}).unwrap().as_millis();
|
||||||
|
match hist {
|
||||||
|
None => {
|
||||||
|
metadata.insert(IcHist(vec![(self.end_tick - self.start_tick, timestamp)],
|
||||||
|
(self.end_tick - self.start_tick, timestamp)));
|
||||||
|
}
|
||||||
|
Some(v) => {
|
||||||
|
v.0.push((self.end_tick - self.start_tick, timestamp));
|
||||||
|
if (v.1.0 < self.end_tick-self.start_tick) {
|
||||||
|
v.1 = (self.end_tick - self.start_tick, timestamp);
|
||||||
|
}
|
||||||
|
if v.0.len() >= 100 {
|
||||||
|
if let Ok(td) = env::var("TIME_DUMP") {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(td).expect("Could not open timedump");
|
||||||
|
let newv : Vec<(u64, u128)> = Vec::with_capacity(100);
|
||||||
|
for i in std::mem::replace(&mut v.0, newv).into_iter() {
|
||||||
|
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// If we don't write out values we don't need to remember them at all
|
||||||
|
v.0.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for QemuClockObserver {
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for QemuClockObserver {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
name: String::from("clock"),
|
||||||
|
start_tick: 0,
|
||||||
|
end_tick: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//========== Feedback
|
||||||
|
/// Nop feedback that annotates execution time in the new testcase, if any
|
||||||
|
/// for this Feedback, the testcase is never interesting (use with an OR).
|
||||||
|
/// It decides, if the given [`QemuClockObserver`] value of a run is interesting.
|
||||||
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
|
pub struct ClockTimeFeedback {
|
||||||
|
exec_time: Option<Duration>,
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for ClockTimeFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasClientPerfMonitor + HasMetadata,
|
||||||
|
{
|
||||||
|
#[allow(clippy::wrong_self_convention)]
|
||||||
|
fn is_interesting<EM, OT>(
|
||||||
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
|
_manager: &mut EM,
|
||||||
|
_input: &S::Input,
|
||||||
|
observers: &OT,
|
||||||
|
_exit_kind: &ExitKind,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
EM: EventFirer<State = S>,
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
{
|
||||||
|
// TODO Replace with match_name_type when stable
|
||||||
|
let observer = observers.match_name::<QemuClockObserver>(self.name()).unwrap();
|
||||||
|
self.exec_time = Some(Duration::from_nanos(observer.last_runtime() << 4)); // Assume a somewhat realistic multiplier of clock, it does not matter
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||||
|
#[inline]
|
||||||
|
fn append_metadata(
|
||||||
|
&mut self,
|
||||||
|
_state: &mut S,
|
||||||
|
testcase: &mut Testcase<S::Input>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
*testcase.exec_time_mut() = self.exec_time;
|
||||||
|
self.exec_time = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||||
|
#[inline]
|
||||||
|
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
|
self.exec_time = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for ClockTimeFeedback {
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
self.name.as_str()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ClockTimeFeedback {
|
||||||
|
/// Creates a new [`ClockFeedback`], deciding if the value of a [`QemuClockObserver`] with the given `name` of a run is interesting.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(name: &'static str) -> Self {
|
||||||
|
Self {
|
||||||
|
exec_time: None,
|
||||||
|
name: name.to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new [`ClockFeedback`], deciding if the given [`QemuClockObserver`] value of a run is interesting.
|
||||||
|
#[must_use]
|
||||||
|
pub fn new_with_observer(observer: &QemuClockObserver) -> Self {
|
||||||
|
Self {
|
||||||
|
exec_time: None,
|
||||||
|
name: observer.name().to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [`Feedback`] rewarding increasing the execution cycles on Qemu.
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct QemuClockIncreaseFeedback {
|
||||||
|
name: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Feedback<S> for QemuClockIncreaseFeedback
|
||||||
|
where
|
||||||
|
S: UsesInput + HasNamedMetadata + HasClientPerfMonitor + Debug,
|
||||||
|
{
|
||||||
|
fn is_interesting<EM, OT>(
|
||||||
|
&mut self,
|
||||||
|
state: &mut S,
|
||||||
|
_manager: &mut EM,
|
||||||
|
_input: &S::Input,
|
||||||
|
_observers: &OT,
|
||||||
|
_exit_kind: &ExitKind,
|
||||||
|
) -> Result<bool, Error>
|
||||||
|
where
|
||||||
|
EM: EventFirer<State = S>,
|
||||||
|
OT: ObserversTuple<S>,
|
||||||
|
{
|
||||||
|
let observer = _observers.match_name::<QemuClockObserver>("clock")
|
||||||
|
.expect("QemuClockObserver not found");
|
||||||
|
let clock_state = state
|
||||||
|
.named_metadata_mut()
|
||||||
|
.get_mut::<MaxIcountMetadata>(&self.name)
|
||||||
|
.unwrap();
|
||||||
|
if observer.last_runtime() > clock_state.max_icount_seen {
|
||||||
|
// println!("Clock improving {}",observer.last_runtime());
|
||||||
|
clock_state.max_icount_seen = observer.last_runtime();
|
||||||
|
return Ok(true);
|
||||||
|
}
|
||||||
|
Ok(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Append to the testcase the generated metadata in case of a new corpus item
|
||||||
|
#[inline]
|
||||||
|
fn append_metadata(&mut self, _state: &mut S, testcase: &mut Testcase<S::Input>) -> Result<(), Error> {
|
||||||
|
// testcase.metadata_mut().insert(QemuIcountMetadata{runtime: self.last_runtime});
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Discard the stored metadata in case that the testcase is not added to the corpus
|
||||||
|
#[inline]
|
||||||
|
fn discard_metadata(&mut self, _state: &mut S, _input: &S::Input) -> Result<(), Error> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Named for QemuClockIncreaseFeedback {
|
||||||
|
#[inline]
|
||||||
|
fn name(&self) -> &str {
|
||||||
|
&self.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl QemuClockIncreaseFeedback {
|
||||||
|
/// Creates a new [`HitFeedback`]
|
||||||
|
#[must_use]
|
||||||
|
pub fn new(name: &'static str) -> Self {
|
||||||
|
Self {name: String::from(name)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for QemuClockIncreaseFeedback {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new("MaxClock")
|
||||||
|
}
|
||||||
|
}
|
@ -1,95 +0,0 @@
|
|||||||
use hashbrown::HashMap;
|
|
||||||
use libafl_qemu::{elf::EasyElf, GuestAddr};
|
|
||||||
use std::env;
|
|
||||||
|
|
||||||
use crate::systemstate::helpers::{load_symbol, try_load_symbol};
|
|
||||||
|
|
||||||
pub fn get_target_symbols(elf: &EasyElf) -> HashMap<&'static str, GuestAddr> {
|
|
||||||
let mut addrs = HashMap::new();
|
|
||||||
|
|
||||||
addrs.insert(
|
|
||||||
"__APP_CODE_START__",
|
|
||||||
load_symbol(&elf, "__APP_CODE_START__", false),
|
|
||||||
);
|
|
||||||
addrs.insert(
|
|
||||||
"__APP_CODE_END__",
|
|
||||||
load_symbol(&elf, "__APP_CODE_END__", false),
|
|
||||||
);
|
|
||||||
addrs.insert(
|
|
||||||
"__API_CODE_START__",
|
|
||||||
load_symbol(&elf, "__API_CODE_START__", false),
|
|
||||||
);
|
|
||||||
addrs.insert(
|
|
||||||
"__API_CODE_END__",
|
|
||||||
load_symbol(&elf, "__API_CODE_END__", false),
|
|
||||||
);
|
|
||||||
addrs.insert(
|
|
||||||
"trigger_job_done",
|
|
||||||
load_symbol(&elf, "trigger_job_done", false),
|
|
||||||
);
|
|
||||||
|
|
||||||
crate::systemstate::target_os::freertos::config::add_target_symbols(elf, &mut addrs);
|
|
||||||
|
|
||||||
// the main address where the fuzzer starts
|
|
||||||
// if this is set for freeRTOS it has an influence on where the data will have to be written,
|
|
||||||
// since the startup routine copies the data segemnt to it's virtual address
|
|
||||||
let main_addr = elf.resolve_symbol(
|
|
||||||
&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
if let Some(main_addr) = main_addr {
|
|
||||||
addrs.insert("FUZZ_MAIN", main_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
let input_addr = load_symbol(
|
|
||||||
&elf,
|
|
||||||
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
addrs.insert("FUZZ_INPUT", input_addr);
|
|
||||||
|
|
||||||
let input_length_ptr = try_load_symbol(
|
|
||||||
&elf,
|
|
||||||
&env::var("FUZZ_LENGTH").unwrap_or_else(|_| "FUZZ_LENGTH".to_owned()),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
if let Some(input_length_ptr) = input_length_ptr {
|
|
||||||
addrs.insert("FUZZ_LENGTH", input_length_ptr);
|
|
||||||
}
|
|
||||||
let input_counter_ptr = try_load_symbol(
|
|
||||||
&elf,
|
|
||||||
&env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()),
|
|
||||||
true,
|
|
||||||
);
|
|
||||||
if let Some(input_counter_ptr) = input_counter_ptr {
|
|
||||||
addrs.insert("FUZZ_POINTER", input_counter_ptr);
|
|
||||||
}
|
|
||||||
addrs.insert(
|
|
||||||
"BREAKPOINT",
|
|
||||||
elf.resolve_symbol(
|
|
||||||
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
|
||||||
0,
|
|
||||||
)
|
|
||||||
.expect("Symbol or env BREAKPOINT not found"),
|
|
||||||
);
|
|
||||||
|
|
||||||
addrs
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_target_ranges(
|
|
||||||
_elf: &EasyElf,
|
|
||||||
symbols: &HashMap<&'static str, GuestAddr>,
|
|
||||||
) -> HashMap<&'static str, std::ops::Range<GuestAddr>> {
|
|
||||||
let mut ranges = HashMap::new();
|
|
||||||
|
|
||||||
ranges.insert(
|
|
||||||
"APP_CODE",
|
|
||||||
symbols["__APP_CODE_START__"]..symbols["__APP_CODE_END__"],
|
|
||||||
);
|
|
||||||
ranges.insert(
|
|
||||||
"API_CODE",
|
|
||||||
symbols["__API_CODE_START__"]..symbols["__API_CODE_END__"],
|
|
||||||
);
|
|
||||||
|
|
||||||
ranges
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
use libafl::*;
|
|
||||||
use libafl_bolts::*;
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use serde::*;
|
|
||||||
use serde::ser::Serialize;
|
|
||||||
use libafl::prelude::Feedback;
|
|
||||||
use libafl::prelude::Testcase;
|
|
||||||
use libafl::prelude::*;
|
|
||||||
use std::marker::PhantomData;
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub struct DebugMetadata {
|
|
||||||
pub val: bool
|
|
||||||
}
|
|
||||||
libafl_bolts::impl_serdeany!(DebugMetadata);
|
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
/// The [`DebugFeedback`] reports the same value, always.
|
|
||||||
/// It can be used to enable or disable feedback results through composition.
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Copy, Debug, PartialEq, Eq)]
|
|
||||||
pub enum DebugFeedback {
|
|
||||||
/// Always returns `true`
|
|
||||||
True,
|
|
||||||
/// Alsways returns `false`
|
|
||||||
False,
|
|
||||||
}
|
|
||||||
|
|
||||||
static mut counter : usize = 10;
|
|
||||||
|
|
||||||
impl<EM, I, OT, S> Feedback<EM, I, OT, S> for DebugFeedback
|
|
||||||
where
|
|
||||||
S: State,
|
|
||||||
{
|
|
||||||
#[inline]
|
|
||||||
#[allow(clippy::wrong_self_convention)]
|
|
||||||
fn is_interesting(
|
|
||||||
&mut self,
|
|
||||||
_state: &mut S,
|
|
||||||
_manager: &mut EM,
|
|
||||||
_input: &I,
|
|
||||||
_observers: &OT,
|
|
||||||
_exit_kind: &ExitKind,
|
|
||||||
) -> Result<bool, Error>
|
|
||||||
where
|
|
||||||
EM: EventFirer<State = S>,
|
|
||||||
OT: ObserversTuple<I, S>,
|
|
||||||
{
|
|
||||||
if unsafe { counter } > 0 {
|
|
||||||
unsafe { counter -= 1; }
|
|
||||||
return Ok(true);
|
|
||||||
} else {
|
|
||||||
return Ok(false);
|
|
||||||
}
|
|
||||||
Ok((*self).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "track_hit_feedbacks")]
|
|
||||||
fn last_result(&self) -> Result<bool, Error> {
|
|
||||||
Ok((*self).into())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn append_metadata(
|
|
||||||
&mut self,
|
|
||||||
state: &mut S,
|
|
||||||
_manager: &mut EM,
|
|
||||||
_observers: &OT,
|
|
||||||
testcase: &mut Testcase<<S>::Input>,
|
|
||||||
) -> Result<(), Error>
|
|
||||||
where
|
|
||||||
OT: ObserversTuple<I, S>,
|
|
||||||
EM: EventFirer<State = S>,
|
|
||||||
{
|
|
||||||
testcase.metadata_map_mut().insert(DebugMetadata { val: true });
|
|
||||||
eprintln!("Attach: {:?}",testcase.metadata::<DebugMetadata>());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Named for DebugFeedback {
|
|
||||||
#[inline]
|
|
||||||
fn name(&self) -> &Cow<'static, str> {
|
|
||||||
static NAME: Cow<'static, str> = Cow::Borrowed("DebugFeedback");
|
|
||||||
&NAME
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DebugFeedback {
|
|
||||||
/// Creates a new [`DebugFeedback`] from the given boolean
|
|
||||||
#[must_use]
|
|
||||||
pub fn new(val: bool) -> Self {
|
|
||||||
Self::from(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<bool> for DebugFeedback {
|
|
||||||
fn from(val: bool) -> Self {
|
|
||||||
if val {
|
|
||||||
Self::True
|
|
||||||
} else {
|
|
||||||
Self::False
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<DebugFeedback> for bool {
|
|
||||||
fn from(value: DebugFeedback) -> Self {
|
|
||||||
match value {
|
|
||||||
DebugFeedback::True => true,
|
|
||||||
DebugFeedback::False => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//==================================================================================================
|
|
||||||
|
|
||||||
/// The default mutational stage
|
|
||||||
#[derive(Clone, Debug, Default)]
|
|
||||||
pub struct DebugStage<E, OT> {
|
|
||||||
#[allow(clippy::type_complexity)]
|
|
||||||
phantom: PhantomData<(E, OT)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, OT> UsesState for DebugStage<E, OT>
|
|
||||||
where
|
|
||||||
E: UsesState,
|
|
||||||
{
|
|
||||||
type State = E::State;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, OT> DebugStage<E, OT>
|
|
||||||
{
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self { phantom: PhantomData}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E, EM, OT, Z> Stage<E, EM, Z> for DebugStage<E, OT>
|
|
||||||
where
|
|
||||||
E: Executor<EM, Z> + HasObservers<Observers = OT>,
|
|
||||||
EM: EventFirer<State = Self::State>,
|
|
||||||
OT: ObserversTuple<Self::State>,
|
|
||||||
Self::State: HasCorpus + HasMetadata + HasNamedMetadata + HasExecutions,
|
|
||||||
Z: Evaluator<E, EM, State = Self::State>,
|
|
||||||
{
|
|
||||||
fn perform(
|
|
||||||
&mut self,
|
|
||||||
fuzzer: &mut Z,
|
|
||||||
executor: &mut E,
|
|
||||||
state: &mut Self::State,
|
|
||||||
manager: &mut EM
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
// eprintln!("DebugStage {:?}", state.current_testcase());
|
|
||||||
let testcase = state.current_testcase()?;
|
|
||||||
eprintln!("Stage: {:?}",testcase.metadata::<DebugMetadata>());
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn restart_progress_should_run(&mut self, state: &mut Self::State) -> Result<bool, Error> {
|
|
||||||
Ok(true)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn clear_restart_progress(&mut self, state: &mut Self::State) -> Result<(), Error> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,192 +1,164 @@
|
|||||||
//! 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}, cmp::{min, max}, mem::transmute_copy, ptr::addr_of_mut, ffi::OsStr};
|
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 hashbrown::HashMap;
|
|
||||||
use libafl_bolts::{
|
|
||||||
core_affinity::Cores, ownedref::OwnedMutSlice, rands::StdRand, shmem::{ShMemProvider, StdShMemProvider}, tuples::tuple_list, AsSlice, SimpleStderrLogger
|
|
||||||
};
|
|
||||||
use libafl::{
|
use libafl::{
|
||||||
common::{HasMetadata, HasNamedMetadata}, corpus::{Corpus, InMemoryCorpus, OnDiskCorpus}, events::{launcher::Launcher, EventConfig}, executors::ExitKind, feedback_or, feedback_or_fast, feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback}, fuzzer::{Fuzzer, StdFuzzer}, inputs::{multi::MultipartInput, BytesInput, HasTargetBytes, Input}, monitors::MultiMonitor, observers::{CanTrack, VariableMapObserver}, prelude::{havoc_mutations, minimizer::TopRatedsMetadata, CorpusId, Generator, HitcountsMapObserver, RandBytesGenerator, SimpleEventManager, SimpleMonitor, SimplePrintingMonitor, SimpleRestartingEventManager, StdScheduledMutator}, schedulers::QueueScheduler, stages::StdMutationalStage, state::{HasCorpus, StdState}, Error, Evaluator
|
bolts::{
|
||||||
|
core_affinity::Cores,
|
||||||
|
current_nanos,
|
||||||
|
launcher::Launcher,
|
||||||
|
rands::StdRand,
|
||||||
|
shmem::{ShMemProvider, StdShMemProvider},
|
||||||
|
tuples::tuple_list,
|
||||||
|
AsSlice,
|
||||||
|
},
|
||||||
|
corpus::{Corpus, InMemoryCorpus, OnDiskCorpus},
|
||||||
|
events::EventConfig,
|
||||||
|
executors::{ExitKind, TimeoutExecutor},
|
||||||
|
feedback_or,
|
||||||
|
feedback_or_fast,
|
||||||
|
feedbacks::{CrashFeedback, MaxMapFeedback, TimeoutFeedback},
|
||||||
|
fuzzer::{Fuzzer, StdFuzzer},
|
||||||
|
inputs::{BytesInput, HasTargetBytes},
|
||||||
|
monitors::MultiMonitor,
|
||||||
|
observers::{VariableMapObserver},
|
||||||
|
schedulers::{IndexesLenTimeMinimizerScheduler, QueueScheduler},
|
||||||
|
state::{HasCorpus, StdState, HasMetadata, HasNamedMetadata},
|
||||||
|
Error,
|
||||||
|
prelude::{SimpleMonitor, SimpleEventManager, AsMutSlice, RandBytesGenerator, Generator, SimpleRestartingEventManager, HasBytesVec, minimizer::TopRatedsMetadata, havoc_mutations, StdScheduledMutator, HitcountsMapObserver}, Evaluator, stages::StdMutationalStage,
|
||||||
};
|
};
|
||||||
use libafl_qemu::{
|
use libafl_qemu::{
|
||||||
elf::EasyElf, emu::Emulator, modules::{edges::{self}, EdgeCoverageModule, FilterList, StdAddressFilter, StdEdgeCoverageModule}, GuestAddr, GuestPhysAddr, QemuExecutor, QemuExitReason, QemuHooks, Regs
|
edges, edges::QemuEdgeCoverageHelper, elf::EasyElf, emu::Emulator, GuestPhysAddr, QemuExecutor,
|
||||||
|
QemuHooks, Regs, QemuInstrumentationFilter, GuestAddr,
|
||||||
|
emu::libafl_qemu_set_native_breakpoint, emu::libafl_qemu_remove_native_breakpoint,
|
||||||
};
|
};
|
||||||
use libafl_targets::{edges_map_mut_ptr, EDGES_MAP_DEFAULT_SIZE, MAX_EDGES_FOUND};
|
|
||||||
use rand::{SeedableRng, StdRng, Rng};
|
use rand::{SeedableRng, StdRng, Rng};
|
||||||
use crate::{
|
use crate::{
|
||||||
config::{get_target_ranges, get_target_symbols}, systemstate::{self, feedbacks::{DumpSystraceFeedback, SystraceErrorFeedback}, helpers::{get_function_range, input_bytes_to_interrupt_times, load_symbol, try_load_symbol}, mutational::{InterruptShiftStage, STGSnippetStage}, schedulers::{GenerationScheduler, LongestTraceScheduler}, stg::{stg_map_mut_slice, GraphMaximizerCorpusScheduler, STGEdge, STGNode, StgFeedback, MAX_STG_NUM}, target_os::freertos::{config::get_range_groups, qemu_module::FreeRTOSSystemStateHelper, FreeRTOSSystem}}, time::{
|
clock::{QemuClockObserver, ClockTimeFeedback, QemuClockIncreaseFeedback, IcHist, FUZZ_START_TIMESTAMP},
|
||||||
clock::{ClockTimeFeedback, IcHist, QemuClockIncreaseFeedback, QemuClockObserver, FUZZ_START_TIMESTAMP, QEMU_ICOUNT_SHIFT, QEMU_ISNS_PER_MSEC, QEMU_ISNS_PER_USEC}, qemustate::QemuStateRestoreHelper, worst::{AlwaysTrueFeedback, ExecTimeIncFeedback, RateLimitedMonitor, TimeMaximizerCorpusScheduler, TimeProbMassScheduler, TimeStateMaximizerCorpusScheduler}
|
qemustate::QemuStateRestoreHelper,
|
||||||
}
|
systemstate::{helpers::QemuSystemStateHelper, observers::QemuSystemStateObserver, feedbacks::{DumpSystraceFeedback, NovelSystemStateFeedback}, graph::{SysMapFeedback, SysGraphFeedbackState, GraphMaximizerCorpusScheduler}, schedulers::{LongestTraceScheduler, GenerationScheduler}}, worst::{TimeMaximizerCorpusScheduler, ExecTimeIncFeedback, TimeStateMaximizerCorpusScheduler, AlwaysTrueFeedback},
|
||||||
|
mutational::MyStateStage,
|
||||||
|
mutational::{MINIMUM_INTER_ARRIVAL_TIME},
|
||||||
};
|
};
|
||||||
use std::time::SystemTime;
|
use std::time::{SystemTime, UNIX_EPOCH};
|
||||||
use petgraph::dot::Dot;
|
|
||||||
use crate::systemstate::stg::STGFeedbackState;
|
|
||||||
use libafl::inputs::HasMutatorBytes;
|
|
||||||
use libafl_qemu::Qemu;
|
|
||||||
use crate::cli::Cli;
|
|
||||||
use crate::cli::Commands;
|
|
||||||
use crate::cli::set_env_from_config;
|
|
||||||
use clap::Parser;
|
|
||||||
use log;
|
|
||||||
use rand::RngCore;
|
|
||||||
use crate::templates;
|
|
||||||
use std::ops::Range;
|
|
||||||
|
|
||||||
// Constants ================================================================================
|
|
||||||
|
|
||||||
pub static mut RNG_SEED: u64 = 1;
|
pub static mut RNG_SEED: u64 = 1;
|
||||||
|
|
||||||
pub const FIRST_INT : u32 = 200000;
|
pub static mut LIMIT : u32 = u32::MAX;
|
||||||
|
|
||||||
pub const MAX_NUM_INTERRUPT: usize = 128;
|
pub const MAX_NUM_INTERRUPT: usize = 32;
|
||||||
pub const NUM_INTERRUPT_SOURCES: usize = 6; // Keep in sync with qemu-libafl-bridge/hw/timer/armv7m_systick.c:319 and FreeRTOS/FreeRTOS/Demo/CORTEX_M3_MPS2_QEMU_GCC/init/startup.c:216
|
pub const DO_NUM_INTERRUPT: usize = 32;
|
||||||
pub const DO_NUM_INTERRUPT: usize = 128;
|
pub static mut MAX_INPUT_SIZE: usize = 32;
|
||||||
pub static mut MAX_INPUT_SIZE: usize = 1024;
|
/// Read ELF program headers to resolve physical load addresses.
|
||||||
|
fn virt2phys(vaddr: GuestPhysAddr, tab: &EasyElf) -> GuestPhysAddr {
|
||||||
pub fn get_all_fn_symbol_ranges(elf: &EasyElf, range: std::ops::Range<GuestAddr>) -> HashMap<String,std::ops::Range<GuestAddr>> {
|
let ret;
|
||||||
let mut ret : HashMap<String,std::ops::Range<GuestAddr>> = HashMap::new();
|
for i in &tab.goblin().program_headers {
|
||||||
|
if i.vm_range().contains(&vaddr.try_into().unwrap()) {
|
||||||
let gob = elf.goblin();
|
ret = vaddr - TryInto::<GuestPhysAddr>::try_into(i.p_vaddr).unwrap()
|
||||||
|
+ TryInto::<GuestPhysAddr>::try_into(i.p_paddr).unwrap();
|
||||||
let mut funcs : Vec<_> = gob.syms.iter().filter(|x| x.is_function() && range.contains(&x.st_value.try_into().unwrap())).collect();
|
return ret - (ret % 2);
|
||||||
funcs.sort_unstable_by(|x,y| x.st_value.cmp(&y.st_value));
|
|
||||||
|
|
||||||
for sym in &funcs {
|
|
||||||
let sym_name = gob.strtab.get_at(sym.st_name);
|
|
||||||
if let Some(sym_name) = sym_name {
|
|
||||||
// if ISR_SYMBOLS.contains(&sym_name) {continue;}; // skip select symbols, which correspond to ISR-safe system calls
|
|
||||||
if let Some(r) = get_function_range(elf, sym_name) {
|
|
||||||
ret.insert(sym_name.to_string(), r);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return vaddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
static mut libafl_interrupt_offsets : [[u32; MAX_NUM_INTERRUPT]; NUM_INTERRUPT_SOURCES];
|
static mut libafl_interrupt_offsets : [u32; 32];
|
||||||
static mut libafl_num_interrupts : [u64; NUM_INTERRUPT_SOURCES];
|
static mut libafl_num_interrupts : usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Takes a state, cli and a suffix, writes out the current worst case
|
|
||||||
macro_rules! do_dump_case {
|
|
||||||
( $s:expr,$cli:expr, $c:expr) => {
|
|
||||||
if ($cli.dump_cases) {
|
|
||||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"case"} else {$c});
|
|
||||||
println!("Dumping worst case to {:?}", &dump_path);
|
|
||||||
let corpus = $s.corpus();
|
|
||||||
let mut worst = Duration::new(0,0);
|
|
||||||
let mut worst_input = None;
|
|
||||||
for i in 0..corpus.count() {
|
|
||||||
let tc = corpus.get(corpus.nth(i.into())).expect("Could not get element from corpus").borrow();
|
|
||||||
if worst < tc.exec_time().expect("Testcase missing duration") {
|
|
||||||
worst_input = Some(tc.input().as_ref().unwrap().clone());
|
|
||||||
worst = tc.exec_time().expect("Testcase missing duration");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if let Some(wi) = worst_input {
|
|
||||||
wi.to_file(dump_path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes a state, cli and a suffix, appends icount history
|
|
||||||
macro_rules! do_dump_times {
|
|
||||||
($state:expr, $cli:expr, $c:expr) => {
|
|
||||||
if $cli.dump_times {
|
|
||||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"time"} else {$c});
|
|
||||||
let mut file = std::fs::OpenOptions::new()
|
|
||||||
.read(true)
|
|
||||||
.write(true)
|
|
||||||
.create(true)
|
|
||||||
.append(true)
|
|
||||||
.open(dump_path).expect("Could not open timedump");
|
|
||||||
if let Ok(ichist) = $state.metadata_mut::<IcHist>() {
|
|
||||||
for i in ichist.0.drain(..) {
|
|
||||||
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes a state and a bool, writes out the current graph
|
|
||||||
macro_rules! do_dump_stg {
|
|
||||||
($state:expr, $cli:expr, $c:expr) => {
|
|
||||||
#[cfg(feature = "trace_stg")]
|
|
||||||
if $cli.dump_graph {
|
|
||||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"dot"} else {$c});
|
|
||||||
println!("Dumping graph to {:?}", &dump_path);
|
|
||||||
if let Some(md) = $state.named_metadata_map_mut().get_mut::<STGFeedbackState<FreeRTOSSystem>>("stgfeedbackstate") {
|
|
||||||
let out = md.graph.map(|_i,x| x.color_print(&md.systemstate_index), |_i,x| x.color_print());
|
|
||||||
let outs = Dot::with_config(&out, &[]).to_string();
|
|
||||||
let outs = outs.replace("\\\"","\"");
|
|
||||||
let outs = outs.replace(';',"\\n");
|
|
||||||
fs::write(dump_path,outs).expect("Failed to write graph");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Takes a state and a bool, writes out top rated inputs
|
|
||||||
macro_rules! do_dump_toprated {
|
|
||||||
($state:expr, $cli:expr, $c:expr) => {
|
|
||||||
if $cli.dump_cases {
|
|
||||||
{
|
|
||||||
let dump_path = $cli.dump_name.clone().unwrap().with_extension(if $c=="" {"toprated"} else {$c});
|
|
||||||
println!("Dumping toprated to {:?}", &dump_path);
|
|
||||||
if let Some(md) = $state.metadata_map_mut().get_mut::<TopRatedsMetadata>() {
|
|
||||||
let mut uniq: Vec<CorpusId> = md.map.values().map(|x| x.clone()).collect();
|
|
||||||
uniq.sort();
|
|
||||||
uniq.dedup();
|
|
||||||
fs::write(dump_path,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Fuzzer setup ================================================================================
|
|
||||||
|
|
||||||
#[allow(unused)]
|
|
||||||
pub fn fuzz() {
|
pub fn fuzz() {
|
||||||
log::set_max_level(log::LevelFilter::Info);
|
|
||||||
SimpleStderrLogger::set_logger().unwrap();
|
|
||||||
let cli = Cli::parse();
|
|
||||||
dbg!(&cli);
|
|
||||||
set_env_from_config(&cli.kernel, &cli.config);
|
|
||||||
let interrupt_config = crate::cli::get_interrupt_config(&cli.kernel, &cli.config);
|
|
||||||
unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();}
|
unsafe {FUZZ_START_TIMESTAMP = SystemTime::now();}
|
||||||
if cli.dump_name.is_none() && (cli.dump_times || cli.dump_cases || cli.dump_traces || cli.dump_graph) {
|
|
||||||
panic!("Dump name not give but dump is requested");
|
|
||||||
}
|
|
||||||
let mut starttime = std::time::Instant::now();
|
let mut starttime = std::time::Instant::now();
|
||||||
|
if let Ok(s) = env::var("FUZZ_SIZE") {
|
||||||
|
str::parse::<usize>(&s).expect("FUZZ_SIZE was not a number");
|
||||||
|
};
|
||||||
// Hardcoded parameters
|
// Hardcoded parameters
|
||||||
let timeout = Duration::from_secs(10);
|
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")];
|
||||||
let objective_dir = PathBuf::from(cli.dump_name.clone().map(|x| x.with_extension("crashes")).unwrap_or("./crashes".try_into().unwrap()));
|
let objective_dir = PathBuf::from("./crashes");
|
||||||
|
|
||||||
let mut elf_buffer = Vec::new();
|
let mut elf_buffer = Vec::new();
|
||||||
let elf = EasyElf::from_file(
|
let elf = EasyElf::from_file(
|
||||||
&cli.kernel,
|
env::var("KERNEL").expect("KERNEL env not set"),
|
||||||
&mut elf_buffer,
|
&mut elf_buffer,
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let TARGET_SYMBOLS: HashMap<&'static str, GuestAddr> = get_target_symbols(&elf);
|
// the main address where the fuzzer starts
|
||||||
let TARGET_RANGES: HashMap<&'static str, Range<GuestAddr>> = get_target_ranges(&elf, &TARGET_SYMBOLS);
|
// if this is set for freeRTOS it has an influence on where the data will have to be written,
|
||||||
let TARGET_GROUPS: HashMap<&'static str, HashMap<String, Range<GuestAddr>>> = get_range_groups(&elf, &TARGET_SYMBOLS, &TARGET_RANGES);
|
// since the startup routine copies the data segemnt to it's virtual address
|
||||||
|
let main_addr = elf
|
||||||
|
.resolve_symbol(&env::var("FUZZ_MAIN").unwrap_or_else(|_| "FUZZ_MAIN".to_owned()), 0);
|
||||||
|
if let Some(main_addr) = main_addr {
|
||||||
|
println!("main address = {:#x}", main_addr);
|
||||||
|
}
|
||||||
|
|
||||||
|
let input_addr = elf
|
||||||
|
.resolve_symbol(
|
||||||
|
&env::var("FUZZ_INPUT").unwrap_or_else(|_| "FUZZ_INPUT".to_owned()),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("Symbol or env FUZZ_INPUT not found") as GuestPhysAddr;
|
||||||
|
let input_addr = virt2phys(input_addr,&elf) as GuestPhysAddr;
|
||||||
|
println!("FUZZ_INPUT @ {:#x}", input_addr);
|
||||||
|
|
||||||
|
let test_length_ptr = elf
|
||||||
|
.resolve_symbol("FUZZ_LENGTH", 0).map(|x| x as GuestPhysAddr);
|
||||||
|
let test_length_ptr = Option::map_or(test_length_ptr, None, |x| Some(virt2phys(x,&elf)));
|
||||||
|
|
||||||
|
let input_counter_ptr = elf
|
||||||
|
.resolve_symbol(&env::var("FUZZ_POINTER").unwrap_or_else(|_| "FUZZ_POINTER".to_owned()), 0)
|
||||||
|
.map(|x| x as GuestPhysAddr);
|
||||||
|
let input_counter_ptr = Option::map_or(input_counter_ptr, None, |x| Some(virt2phys(x,&elf)));
|
||||||
|
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let curr_tcb_pointer = elf // loads to the address specified in elf, without respecting program headers
|
||||||
|
.resolve_symbol("pxCurrentTCB", 0)
|
||||||
|
.expect("Symbol pxCurrentTCBC not found");
|
||||||
|
// let curr_tcb_pointer = virt2phys(curr_tcb_pointer,&elf);
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
println!("TCB pointer at {:#x}", curr_tcb_pointer);
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let task_queue_addr = elf
|
||||||
|
.resolve_symbol("pxReadyTasksLists", 0)
|
||||||
|
.expect("Symbol pxReadyTasksLists not found");
|
||||||
|
// let task_queue_addr = virt2phys(task_queue_addr,&elf.goblin());
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
println!("Task Queue at {:#x}", task_queue_addr);
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let svh = elf
|
||||||
|
.resolve_symbol("xPortPendSVHandler", 0)
|
||||||
|
.expect("Symbol xPortPendSVHandler not found");
|
||||||
|
// let svh=virt2phys(svh, &elf);
|
||||||
|
// let svh = elf
|
||||||
|
// .resolve_symbol("vPortEnterCritical", 0)
|
||||||
|
// .expect("Symbol vPortEnterCritical not found");
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let app_start = elf
|
||||||
|
.resolve_symbol("__APP_CODE_START__", 0)
|
||||||
|
.expect("Symbol __APP_CODE_START__ not found");
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let app_end = elf
|
||||||
|
.resolve_symbol("__APP_CODE_END__", 0)
|
||||||
|
.expect("Symbol __APP_CODE_END__ not found");
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let app_range = app_start..app_end;
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
dbg!(app_range.clone());
|
||||||
|
|
||||||
|
let breakpoint = elf
|
||||||
|
.resolve_symbol(
|
||||||
|
&env::var("BREAKPOINT").unwrap_or_else(|_| "BREAKPOINT".to_owned()),
|
||||||
|
0,
|
||||||
|
)
|
||||||
|
.expect("Symbol or env BREAKPOINT not found");
|
||||||
|
println!("Breakpoint address = {:#x}", breakpoint);
|
||||||
unsafe {
|
unsafe {
|
||||||
libafl_num_interrupts = [0; NUM_INTERRUPT_SOURCES];
|
libafl_num_interrupts = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") {
|
if let Ok(input_len) = env::var("FUZZ_INPUT_LEN") {
|
||||||
@ -198,157 +170,94 @@ if let Ok(seed) = env::var("SEED_RANDOM") {
|
|||||||
unsafe {RNG_SEED = str::parse::<u64>(&seed).expect("SEED_RANDOM must be an integer.");}
|
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 denylist: Vec<_> = TARGET_GROUPS["ISR_FN"].values().map(|x| x.clone()).collect();
|
|
||||||
let denylist = StdAddressFilter::deny_list(denylist); // do not count isr jumps, which are useless
|
|
||||||
|
|
||||||
/// Setup the interrupt inputs. Noop if interrupts are not fuzzed
|
|
||||||
fn setup_interrupt_inputs(mut input : MultipartInput<BytesInput>, interrupt_config : &Vec<(usize,u32)>, mut random: Option<&mut StdRng>) -> MultipartInput<BytesInput> {
|
|
||||||
#[cfg(feature = "fuzz_int")]
|
|
||||||
for (i,_) in interrupt_config {
|
|
||||||
let name = format!("isr_{}_times",i);
|
|
||||||
if input.parts_by_name(&name).next().is_none() {
|
|
||||||
if let Some(random) = random.as_mut() {
|
|
||||||
input.add_part(name, BytesInput::new((0..MAX_NUM_INTERRUPT).map(|_| (random.next_u32()%(100*QEMU_ISNS_PER_MSEC)).to_le_bytes()).flatten().collect()));
|
|
||||||
} else {
|
|
||||||
input.add_part(name, BytesInput::new([0; MAX_NUM_INTERRUPT*4].to_vec()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
input
|
|
||||||
}
|
|
||||||
|
|
||||||
// Client setup ================================================================================
|
|
||||||
|
|
||||||
let run_client = |state: Option<_>, mut mgr, _core_id| {
|
|
||||||
// Initialize QEMU
|
// Initialize QEMU
|
||||||
let args: Vec<String> = vec![
|
let args: Vec<String> = env::args().collect();
|
||||||
"target/debug/fret",
|
|
||||||
"-icount",
|
|
||||||
&format!("shift={},align=off,sleep=off", QEMU_ICOUNT_SHIFT),
|
|
||||||
"-machine",
|
|
||||||
"mps2-an385",
|
|
||||||
"-cpu",
|
|
||||||
"cortex-m3",
|
|
||||||
"-monitor",
|
|
||||||
"null",
|
|
||||||
"-kernel",
|
|
||||||
&cli.kernel.as_os_str().to_str().expect("kernel path is not a string"),
|
|
||||||
"-serial",
|
|
||||||
"null",
|
|
||||||
"-nographic",
|
|
||||||
"-S",
|
|
||||||
// "-semihosting",
|
|
||||||
// "--semihosting-config",
|
|
||||||
// "enable=on,target=native",
|
|
||||||
#[cfg(not(feature = "snapshot_fast"))]
|
|
||||||
"-snapshot",
|
|
||||||
#[cfg(not(feature = "snapshot_fast"))]
|
|
||||||
"-drive",
|
|
||||||
#[cfg(not(feature = "snapshot_fast"))]
|
|
||||||
"if=none,format=qcow2,file=dummy.qcow2",
|
|
||||||
].into_iter().map(String::from).collect();
|
|
||||||
let env: Vec<(String, String)> = env::vars().collect();
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
let qemu = Qemu::init(&args).expect("Emulator creation failed");
|
let emu = Emulator::new(&args, &env);
|
||||||
|
|
||||||
if let Some(&main_addr) = TARGET_SYMBOLS.get("FUZZ_MAIN") {
|
if let Some(main_addr) = main_addr {
|
||||||
qemu.set_breakpoint(main_addr);
|
|
||||||
unsafe {
|
unsafe {
|
||||||
match qemu.run() {
|
libafl_qemu_set_native_breakpoint(main_addr);
|
||||||
Ok(QemuExitReason::Breakpoint(_)) => {}
|
emu.run();
|
||||||
_ => panic!("Unexpected QEMU exit."),
|
libafl_qemu_remove_native_breakpoint(main_addr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
qemu.remove_breakpoint(main_addr);
|
|
||||||
}
|
|
||||||
|
|
||||||
qemu.set_breakpoint(TARGET_SYMBOLS["BREAKPOINT"]); // BREAKPOINT
|
unsafe { libafl_qemu_set_native_breakpoint(breakpoint); }// BREAKPOINT
|
||||||
|
|
||||||
let devices = qemu.list_devices();
|
|
||||||
println!("Devices = {devices:?}");
|
|
||||||
|
|
||||||
#[cfg(feature = "snapshot_fast")]
|
|
||||||
let initial_snap = Some(qemu.create_fast_snapshot(true));
|
|
||||||
#[cfg(not(feature = "snapshot_fast"))]
|
|
||||||
let initial_snap = None;
|
|
||||||
|
|
||||||
let harness_input_addr = TARGET_SYMBOLS["FUZZ_INPUT"];
|
|
||||||
let harness_input_length_ptr = TARGET_SYMBOLS.get("FUZZ_LENGTH").copied();
|
|
||||||
let harness_breakpoint = TARGET_SYMBOLS["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 = |emulator: &mut Emulator<_, _, _, _, _>, state: &mut _, input: &MultipartInput<BytesInput>| {
|
let mut harness = |input: &BytesInput| {
|
||||||
|
let target = input.target_bytes();
|
||||||
|
let mut buf = target.as_slice();
|
||||||
|
let mut len = buf.len();
|
||||||
unsafe {
|
unsafe {
|
||||||
#[cfg(feature = "fuzz_int")]
|
#[cfg(feature = "fuzz_int")]
|
||||||
{
|
{
|
||||||
libafl_interrupt_offsets=[[0;MAX_NUM_INTERRUPT];NUM_INTERRUPT_SOURCES];
|
let mut start_tick : u32 = 0;
|
||||||
for &c in &interrupt_config {
|
for i in 0..DO_NUM_INTERRUPT {
|
||||||
let (i,_) = c;
|
let mut t : [u8; 4] = [0,0,0,0];
|
||||||
let name = format!("isr_{}_times",i);
|
if len > (i+1)*4 {
|
||||||
let input_bytes = input.parts_by_name(&name).next().map(|x| x.1.bytes()).unwrap_or(&[]);
|
for j in 0 as usize..4 as usize {
|
||||||
let t = input_bytes_to_interrupt_times(input_bytes, c);
|
t[j]=buf[i*4+j];
|
||||||
for j in 0..t.len() {libafl_interrupt_offsets[i][j]=t[j];}
|
}
|
||||||
libafl_num_interrupts[i]=t.len() as u64;
|
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());
|
// println!("Load: {:?}", libafl_interrupt_offsets[0..libafl_num_interrupts].to_vec());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut bytes = input.parts_by_name("bytes").next().unwrap().1.bytes();
|
|
||||||
let mut len = bytes.len();
|
|
||||||
if len > MAX_INPUT_SIZE {
|
if len > MAX_INPUT_SIZE {
|
||||||
bytes = &bytes[0..MAX_INPUT_SIZE];
|
buf = &buf[0..MAX_INPUT_SIZE];
|
||||||
len = MAX_INPUT_SIZE;
|
len = MAX_INPUT_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: I could not find a difference between write_mem and write_phys_mem for my usecase
|
emu.write_phys_mem(input_addr, buf);
|
||||||
qemu.write_mem(harness_input_addr, bytes);
|
if let Some(s) = test_length_ptr {
|
||||||
if let Some(s) = harness_input_length_ptr {
|
emu.write_phys_mem(s as u64, &len.to_le_bytes())
|
||||||
qemu.write_mem(s, &(len as u32).to_le_bytes());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qemu.run();
|
emu.run();
|
||||||
|
|
||||||
// If the execution stops at any point other then the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash
|
// If the execution stops at any point other then the designated breakpoint (e.g. a breakpoint on a panic method) we consider it a crash
|
||||||
let mut pcs = (0..qemu.num_cpus())
|
let mut pcs = (0..emu.num_cpus())
|
||||||
.map(|i| qemu.cpu_from_index(i))
|
.map(|i| emu.cpu_from_index(i))
|
||||||
.map(|cpu| -> Result<u32, _> { cpu.read_reg(Regs::Pc) });
|
.map(|cpu| -> Result<u32, String> { cpu.read_reg(Regs::Pc) });
|
||||||
match pcs
|
match pcs
|
||||||
.find(|pc| (harness_breakpoint..harness_breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
|
.find(|pc| (breakpoint..breakpoint + 5).contains(pc.as_ref().unwrap_or(&0)))
|
||||||
{
|
{
|
||||||
Some(_) => ExitKind::Ok,
|
Some(_) => ExitKind::Ok,
|
||||||
Option::None => ExitKind::Crash,
|
None => ExitKind::Crash,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create an observation channel to keep track of the execution time
|
|
||||||
let clock_time_observer = QemuClockObserver::new("clocktime", &cli.select_task); // if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None}
|
|
||||||
|
|
||||||
// Create an observation channel using the coverage map
|
// Create an observation channel using the coverage map
|
||||||
#[cfg(feature = "observe_edges")]
|
let edges = unsafe { &mut edges::EDGES_MAP };
|
||||||
let mut edges_observer = unsafe { VariableMapObserver::from_mut_slice(
|
let edges_counter = unsafe { &mut edges::MAX_EDGES_NUM };
|
||||||
"edges",
|
let edges_observer = VariableMapObserver::new("edges", edges, edges_counter);
|
||||||
OwnedMutSlice::from_raw_parts_mut(edges_map_mut_ptr(), EDGES_MAP_DEFAULT_SIZE),
|
#[cfg(feature = "observer_hitcounts")]
|
||||||
addr_of_mut!(MAX_EDGES_FOUND),
|
let edges_observer = HitcountsMapObserver::new(edges_observer);
|
||||||
)};
|
|
||||||
#[cfg(feature = "observe_hitcounts")]
|
|
||||||
let mut edges_observer = HitcountsMapObserver::new(edges_observer);
|
|
||||||
#[cfg(feature = "observe_edges")]
|
|
||||||
let mut edges_observer = edges_observer.track_indices();
|
|
||||||
|
|
||||||
#[cfg(feature = "observe_systemstate")]
|
// Create an observation channel to keep track of the execution time
|
||||||
let stg_coverage_observer = unsafe { VariableMapObserver::from_mut_slice(
|
let clock_time_observer = QemuClockObserver::new("clocktime");
|
||||||
"stg",
|
|
||||||
stg_map_mut_slice(),
|
let systemstate_observer = QemuSystemStateObserver::new();
|
||||||
addr_of_mut!(MAX_STG_NUM)
|
|
||||||
)}.track_indices();
|
|
||||||
|
|
||||||
// Feedback to rate the interestingness of an input
|
// Feedback to rate the interestingness of an input
|
||||||
// This one is composed by two Feedbacks in OR
|
// This one is composed by two Feedbacks in OR
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
// Time feedback, this one does not need a feedback state
|
// Time feedback, this one does not need a feedback state
|
||||||
ClockTimeFeedback::<FreeRTOSSystem>::new_with_observer(&clock_time_observer, &cli.select_task, if cli.dump_times {cli.dump_name.clone().map(|x| x.with_extension("time"))} else {None})
|
ClockTimeFeedback::new_with_observer(&clock_time_observer)
|
||||||
);
|
);
|
||||||
#[cfg(feature = "feed_genetic")]
|
#[cfg(feature = "feed_genetic")]
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
@ -359,33 +268,34 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
|||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
feedback,
|
feedback,
|
||||||
// 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(&edges_observer)
|
MaxMapFeedback::new_tracking(&edges_observer, true, true)
|
||||||
);
|
);
|
||||||
#[cfg(feature = "feed_longest")]
|
#[cfg(feature = "feed_longest")]
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
// afl feedback needs to be activated first for MapIndexesMetadata
|
// afl feedback needs to be activated first for MapIndexesMetadata
|
||||||
feedback,
|
feedback,
|
||||||
// Feedback to reward any input which increses the execution time
|
// Feedback to reward any input which increses the execution time
|
||||||
ExecTimeIncFeedback::<FreeRTOSSystem>::new()
|
ExecTimeIncFeedback::new()
|
||||||
);
|
);
|
||||||
#[cfg(all(feature = "observe_systemstate"))]
|
#[cfg(all(feature = "systemstate",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))]
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
feedback,
|
feedback,
|
||||||
DumpSystraceFeedback::<FreeRTOSSystem>::with_dump(if cli.dump_traces {cli.dump_name.clone()} else {None})
|
DumpSystraceFeedback::with_dump(env::var("TRACE_DUMP").ok().map(PathBuf::from))
|
||||||
);
|
);
|
||||||
#[cfg(feature = "trace_stg")]
|
#[cfg(feature = "feed_systemtrace")]
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
feedback,
|
feedback,
|
||||||
StgFeedback::<FreeRTOSSystem>::new(cli.select_task.clone(), if cli.dump_graph {cli.dump_name.clone()} else {None})
|
// AlwaysTrueFeedback::new(),
|
||||||
|
NovelSystemStateFeedback::default()
|
||||||
);
|
);
|
||||||
#[cfg(feature = "feed_stg_edge")]
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
let mut feedback = feedback_or!(
|
let mut feedback = feedback_or!(
|
||||||
feedback,
|
feedback,
|
||||||
MaxMapFeedback::new(&stg_coverage_observer)
|
SysMapFeedback::default()
|
||||||
);
|
);
|
||||||
|
|
||||||
// A feedback to choose if an input is producing an error
|
// A feedback to choose if an input is a solution or not
|
||||||
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new(), SystraceErrorFeedback::<FreeRTOSSystem>::new(matches!(cli.command, Commands::Fuzz{..}), Some(10)));
|
let mut objective = feedback_or_fast!(CrashFeedback::new(), TimeoutFeedback::new());
|
||||||
|
|
||||||
// If not restarting, create a State from scratch
|
// If not restarting, create a State from scratch
|
||||||
let mut state = state.unwrap_or_else(|| {
|
let mut state = state.unwrap_or_else(|| {
|
||||||
@ -407,100 +317,94 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// A minimization+queue policy to get testcasess from the corpus
|
// A minimization+queue policy to get testcasess from the corpus
|
||||||
#[cfg(not(any(feature = "sched_afl", feature = "sched_stg", feature = "sched_genetic")))]
|
#[cfg(not(any(feature = "feed_afl",feature = "feed_systemgraph",feature = "feed_systemtrace", feature = "feed_genetic")))]
|
||||||
let scheduler = QueueScheduler::new(); // fallback
|
let scheduler = QueueScheduler::new();
|
||||||
#[cfg(feature = "sched_afl",)]
|
#[cfg(all(feature = "feed_afl",not(any(feature = "feed_systemgraph",feature = "feed_systemtrace"))))]
|
||||||
let scheduler = TimeMaximizerCorpusScheduler::new(&edges_observer,TimeProbMassScheduler::new());
|
let scheduler = TimeMaximizerCorpusScheduler::new(QueueScheduler::new());
|
||||||
#[cfg(feature = "sched_stg")]
|
#[cfg(feature = "feed_systemtrace")]
|
||||||
let mut scheduler = GraphMaximizerCorpusScheduler::non_metadata_removing(&stg_coverage_observer,TimeProbMassScheduler::new());
|
let scheduler = LongestTraceScheduler::new(TimeStateMaximizerCorpusScheduler::new(QueueScheduler::new()));
|
||||||
#[cfg(feature = "sched_stg")]
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
{
|
let scheduler = GraphMaximizerCorpusScheduler::new(QueueScheduler::new());
|
||||||
scheduler.skip_non_favored_prob = 0.8;
|
#[cfg(feature = "feed_genetic")]
|
||||||
}
|
|
||||||
#[cfg(feature = "sched_genetic")]
|
|
||||||
let scheduler = GenerationScheduler::new();
|
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);
|
||||||
|
#[cfg(not(feature = "systemstate"))]
|
||||||
|
let qhelpers = tuple_list!(
|
||||||
|
QemuEdgeCoverageHelper::default(),
|
||||||
|
QemuStateRestoreHelper::new()
|
||||||
|
);
|
||||||
|
#[cfg(feature = "systemstate")]
|
||||||
|
let qhelpers = tuple_list!(
|
||||||
|
QemuEdgeCoverageHelper::default(),
|
||||||
|
QemuStateRestoreHelper::new(),
|
||||||
|
QemuSystemStateHelper::new(svh,curr_tcb_pointer,task_queue_addr,input_counter_ptr,app_range.clone())
|
||||||
|
);
|
||||||
|
let mut hooks = QemuHooks::new(&emu,qhelpers);
|
||||||
|
|
||||||
let qhelpers = tuple_list!();
|
#[cfg(not(feature = "systemstate"))]
|
||||||
#[cfg(feature = "observe_systemstate")]
|
let observer_list = tuple_list!(edges_observer, clock_time_observer);
|
||||||
let qhelpers = (FreeRTOSSystemStateHelper::new(&TARGET_SYMBOLS,&TARGET_RANGES,&TARGET_GROUPS), qhelpers);
|
#[cfg(feature = "systemstate")]
|
||||||
#[cfg(feature = "observe_edges")]
|
let observer_list = tuple_list!(edges_observer, clock_time_observer, systemstate_observer);
|
||||||
let qhelpers = (
|
|
||||||
StdEdgeCoverageModule::builder()
|
|
||||||
.map_observer(edges_observer.as_mut())
|
|
||||||
.address_filter(denylist)
|
|
||||||
.build()
|
|
||||||
.unwrap(), qhelpers);//StdEdgeCoverageModule::new(denylist, FilterList::None), qhelpers);
|
|
||||||
let qhelpers = (QemuStateRestoreHelper::with_fast(initial_snap), qhelpers);
|
|
||||||
|
|
||||||
let emulator = Emulator::empty().qemu(qemu).modules(qhelpers).build().unwrap();
|
|
||||||
|
|
||||||
let observer_list = tuple_list!();
|
|
||||||
#[cfg(feature = "observe_systemstate")]
|
|
||||||
let observer_list = (stg_coverage_observer, observer_list); // must come after clock
|
|
||||||
#[cfg(feature = "observe_edges")]
|
|
||||||
let observer_list = (edges_observer, observer_list);
|
|
||||||
let observer_list = (clock_time_observer, observer_list);
|
|
||||||
|
|
||||||
// Create a QEMU in-process executor
|
// Create a QEMU in-process executor
|
||||||
let mut executor = QemuExecutor::new(
|
let executor = QemuExecutor::new(
|
||||||
emulator,
|
&mut hooks,
|
||||||
&mut harness,
|
&mut harness,
|
||||||
observer_list,
|
observer_list,
|
||||||
&mut fuzzer,
|
&mut fuzzer,
|
||||||
&mut state,
|
&mut state,
|
||||||
&mut mgr,
|
&mut mgr,
|
||||||
timeout
|
|
||||||
)
|
)
|
||||||
.expect("Failed to create QemuExecutor");
|
.expect("Failed to create QemuExecutor");
|
||||||
|
|
||||||
executor.break_on_timeout();
|
// Wrap the executor to keep track of the timeout
|
||||||
|
let mut executor = TimeoutExecutor::new(executor, timeout);
|
||||||
|
|
||||||
let mutations = havoc_mutations();
|
let mutations = havoc_mutations();
|
||||||
// Setup an havoc mutator with a mutational stage
|
// Setup an havoc mutator with a mutational stage
|
||||||
let mutator = StdScheduledMutator::new(mutations);
|
let mutator = StdScheduledMutator::new(mutations);
|
||||||
|
// #[cfg(not(all(feature = "feed_systemtrace", feature = "fuzz_int")))]
|
||||||
let stages = (systemstate::report::SchedulerStatsStage::default(),());
|
// let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
let stages = (StdMutationalStage::new(mutator), stages);
|
// #[cfg(all(feature = "feed_systemtrace", feature = "fuzz_int"))]
|
||||||
#[cfg(feature = "mutate_stg")]
|
|
||||||
let mut stages = (STGSnippetStage::<_,_,_,FreeRTOSSystem>::new(TARGET_SYMBOLS["FUZZ_INPUT"]), stages);
|
|
||||||
#[cfg(feature = "fuzz_int")]
|
#[cfg(feature = "fuzz_int")]
|
||||||
let mut stages = (InterruptShiftStage::<_,_,_,FreeRTOSSystem>::new(&interrupt_config), stages);
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator),MyStateStage::new());
|
||||||
|
#[cfg(not(feature = "fuzz_int"))]
|
||||||
|
let mut stages = tuple_list!(StdMutationalStage::new(mutator));
|
||||||
|
|
||||||
if let Commands::Showmap { input } = cli.command.clone() {
|
if env::var("DO_SHOWMAP").is_ok() {
|
||||||
let s = input.as_os_str();
|
let s = &env::var("DO_SHOWMAP").unwrap();
|
||||||
// let show_input = BytesInput::new(if s=="-" {
|
let show_input = if s=="-" {
|
||||||
// let mut buf = Vec::<u8>::new();
|
let mut buf = Vec::<u8>::new();
|
||||||
// std::io::stdin().read_to_end(&mut buf).expect("Could not read Stdin");
|
std::io::stdin().read_to_end(&mut buf).expect("Could not read Stdin");
|
||||||
// buf
|
buf
|
||||||
// } else if s=="$" {
|
} else if s=="$" {
|
||||||
// env::var("SHOWMAP_TEXTINPUT").expect("SHOWMAP_TEXTINPUT not set").as_bytes().to_owned()
|
env::var("SHOWMAP_TEXTINPUT").expect("SHOWMAP_TEXTINPUT not set").as_bytes().to_owned()
|
||||||
// } else {
|
} else {
|
||||||
// // fs::read(s).expect("Input file for DO_SHOWMAP can not be read")
|
fs::read(s).expect("Input file for DO_SHOWMAP can not be read")
|
||||||
// });
|
|
||||||
let show_input = match MultipartInput::from_file(input.as_os_str()) {
|
|
||||||
Ok(x) => x,
|
|
||||||
Err(_) => {
|
|
||||||
println!("Interpreting input file as raw input");
|
|
||||||
setup_interrupt_inputs(MultipartInput::from([("bytes",BytesInput::new(fs::read(input).expect("Can not read input file")))]), &interrupt_config, None)
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, show_input)
|
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, BytesInput::new(show_input))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
do_dump_times!(state, &cli, "");
|
if let Ok(td) = env::var("TIME_DUMP") {
|
||||||
do_dump_stg!(state, &cli, "");
|
let mut file = OpenOptions::new()
|
||||||
} else if let Commands::Fuzz { random, time, seed } = cli.command {
|
.read(true)
|
||||||
if let Some(se) = seed {
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(td).expect("Could not open timedump");
|
||||||
|
if let Some(ichist) = state.metadata_mut().get_mut::<IcHist>() {
|
||||||
|
for i in ichist.0.drain(..) {
|
||||||
|
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let Ok(_) = env::var("SEED_RANDOM") {
|
||||||
unsafe {
|
unsafe {
|
||||||
let mut rng = StdRng::seed_from_u64(se);
|
let mut rng = StdRng::seed_from_u64(RNG_SEED);
|
||||||
let bound = 10000;
|
for i in 0..100 {
|
||||||
#[cfg(feature = "shortcut")]
|
let inp = BytesInput::new(vec![rng.gen::<u8>(); MAX_INPUT_SIZE]);
|
||||||
let bound = 100;
|
|
||||||
for _ in 0..bound {
|
|
||||||
let inp2 = BytesInput::new((0..MAX_INPUT_SIZE).map(|_| rng.gen::<u8>()).collect());
|
|
||||||
let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config, Some(&mut rng));
|
|
||||||
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap();
|
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -523,16 +427,23 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
|||||||
println!("We imported {} inputs from disk.", state.corpus().count());
|
println!("We imported {} inputs from disk.", state.corpus().count());
|
||||||
}
|
}
|
||||||
|
|
||||||
match time {
|
match env::var("FUZZ_ITERS") {
|
||||||
Option::None => {
|
Err(_) => {
|
||||||
fuzzer
|
fuzzer
|
||||||
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
.fuzz_loop(&mut stages, &mut executor, &mut state, &mut mgr)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
},
|
},
|
||||||
Some(t) => {
|
Ok(t) => {
|
||||||
println!("Iterations {}",t);
|
println!("Iterations {}",t);
|
||||||
let num = t;
|
let num = str::parse::<u64>(&t).expect("FUZZ_ITERS was not a number");
|
||||||
if 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);
|
||||||
@ -541,8 +452,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
|||||||
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 inp2 = BytesInput::new((0..MAX_INPUT_SIZE).map(|_| rng.gen::<u8>()).collect());
|
let inp = BytesInput::new(vec![rng.gen::<u8>(); MAX_INPUT_SIZE]);
|
||||||
let inp = setup_interrupt_inputs(MultipartInput::from([("bytes",inp2)]), &interrupt_config, Some(&mut rng));
|
|
||||||
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap();
|
fuzzer.evaluate_input(&mut state, &mut executor, &mut mgr, inp).unwrap();
|
||||||
}
|
}
|
||||||
}} else {
|
}} else {
|
||||||
@ -553,40 +463,170 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
|||||||
.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")]
|
#[cfg(feature = "run_until_saturation")]
|
||||||
|
{
|
||||||
{
|
{
|
||||||
let mut dumper = |marker : String| {
|
let mut dumper = |marker : String| {
|
||||||
let d = format!("{}.case",marker);
|
if let Ok(td) = env::var("TIME_DUMP") {
|
||||||
do_dump_case!(state, &cli, &d);
|
let mut file = OpenOptions::new()
|
||||||
let _d = format!("{}.dot",marker);
|
.read(true)
|
||||||
do_dump_stg!(state, &cli, &_d);
|
.write(true)
|
||||||
let d = format!("{}.toprated",marker);
|
.create(true)
|
||||||
do_dump_toprated!(state, &cli, &d);
|
.append(true)
|
||||||
|
.open(td).expect("Could not open timedump");
|
||||||
|
if let Some(ichist) = state.metadata_mut().get_mut::<IcHist>() {
|
||||||
|
for i in ichist.0.drain(..) {
|
||||||
|
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(td) = env::var("CASE_DUMP") {
|
||||||
|
println!("Dumping worst case to {:?}", td);
|
||||||
|
let corpus = state.corpus();
|
||||||
|
let mut worst = Duration::new(0,0);
|
||||||
|
let mut worst_input = None;
|
||||||
|
for i in 0..corpus.count() {
|
||||||
|
let tc = corpus.get(i).expect("Could not get element from corpus").borrow();
|
||||||
|
if worst < tc.exec_time().expect("Testcase missing duration") {
|
||||||
|
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
|
||||||
|
worst = tc.exec_time().expect("Testcase missing duration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match worst_input {
|
||||||
|
Some(wi) => {
|
||||||
|
// let cd = format!("{}.case",&td);
|
||||||
|
let mut cd = td.clone();
|
||||||
|
cd.push_str(&marker);
|
||||||
|
fs::write(&cd,wi).expect("Failed to write worst corpus element");
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
gd.push_str(&format!(".graph{}", marker));
|
||||||
|
if let Some(md) = state.named_metadata_mut().get_mut::<SysGraphFeedbackState>("SysMap") {
|
||||||
|
fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
|
||||||
|
let mut uniq: Vec<usize> = md.map.values().map(|x| x.clone()).collect();
|
||||||
|
uniq.sort();
|
||||||
|
uniq.dedup();
|
||||||
|
gd.push_str(&format!(".{}.toprated{}", uniq.len(), marker));
|
||||||
|
fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
dumper(format!(".iter_{}",t));
|
dumper(format!(".iter_{}",t));
|
||||||
do_dump_times!(state, &cli, "");
|
}
|
||||||
|
|
||||||
println!("Start running until saturation");
|
println!("Start running until saturation");
|
||||||
let mut last = state.metadata_map().get::<IcHist>().unwrap().1;
|
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() {
|
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();
|
starttime=starttime.checked_add(Duration::from_secs(30)).unwrap();
|
||||||
fuzzer
|
fuzzer
|
||||||
.fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime)
|
.fuzz_loop_until(&mut stages, &mut executor, &mut state, &mut mgr, starttime)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let after = state.metadata_map().get::<IcHist>().unwrap().1;
|
let after = state.metadata().get::<IcHist>().unwrap().1;
|
||||||
if after.0 > last.0 {
|
if after.0 > last.0 {
|
||||||
last=after;
|
last=after;
|
||||||
}
|
}
|
||||||
do_dump_case!(state, &cli, "");
|
if let Ok(td) = env::var("CASE_DUMP") {
|
||||||
do_dump_stg!(state, &cli, "");
|
println!("Dumping worst case to {:?}", td);
|
||||||
do_dump_toprated!(state, &cli, "");
|
let corpus = state.corpus();
|
||||||
|
let mut worst = Duration::new(0,0);
|
||||||
|
let mut worst_input = None;
|
||||||
|
for i in 0..corpus.count() {
|
||||||
|
let tc = corpus.get(i).expect("Could not get element from corpus").borrow();
|
||||||
|
if worst < tc.exec_time().expect("Testcase missing duration") {
|
||||||
|
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
|
||||||
|
worst = tc.exec_time().expect("Testcase missing duration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match worst_input {
|
||||||
|
Some(wi) => {
|
||||||
|
// let cd = format!("{}.case",&td);
|
||||||
|
let cd = td.clone();
|
||||||
|
fs::write(&cd,wi).expect("Failed to write worst corpus element");
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
gd.push_str(".graph" );
|
||||||
|
if let Some(md) = state.named_metadata_mut().get_mut::<SysGraphFeedbackState>("SysMap") {
|
||||||
|
fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
|
||||||
|
let mut uniq: Vec<usize> = md.map.values().map(|x| x.clone()).collect();
|
||||||
|
uniq.sort();
|
||||||
|
uniq.dedup();
|
||||||
|
gd.push_str(&format!(".{}.toprated", uniq.len()));
|
||||||
|
fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(td) = env::var("TIME_DUMP") {
|
||||||
|
let mut file = OpenOptions::new()
|
||||||
|
.read(true)
|
||||||
|
.write(true)
|
||||||
|
.create(true)
|
||||||
|
.append(true)
|
||||||
|
.open(td).expect("Could not open timedump");
|
||||||
|
if let Some(ichist) = state.metadata_mut().get_mut::<IcHist>() {
|
||||||
|
for i in ichist.0.drain(..) {
|
||||||
|
writeln!(file, "{},{}", i.0, i.1).expect("Write to dump failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Ok(td) = env::var("CASE_DUMP") {
|
||||||
|
println!("Dumping worst case to {:?}", td);
|
||||||
|
let corpus = state.corpus();
|
||||||
|
let mut worst = Duration::new(0,0);
|
||||||
|
let mut worst_input = None;
|
||||||
|
for i in 0..corpus.count() {
|
||||||
|
let tc = corpus.get(i).expect("Could not get element from corpus").borrow();
|
||||||
|
if worst < tc.exec_time().expect("Testcase missing duration") {
|
||||||
|
worst_input = Some(tc.input().as_ref().unwrap().bytes().to_owned());
|
||||||
|
worst = tc.exec_time().expect("Testcase missing duration");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
match worst_input {
|
||||||
|
Some(wi) => {
|
||||||
|
// let cd = format!("{}.case",&td);
|
||||||
|
let cd = td.clone();
|
||||||
|
fs::write(&cd,wi).expect("Failed to write worst corpus element");
|
||||||
|
},
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
#[cfg(feature = "feed_systemgraph")]
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
gd.push_str(".graph");
|
||||||
|
if let Some(md) = state.named_metadata_mut().get_mut::<SysGraphFeedbackState>("SysMap") {
|
||||||
|
fs::write(&gd,ron::to_string(&md).expect("Failed to serialize graph")).expect("Failed to write graph");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
let mut gd = String::from(&td);
|
||||||
|
if let Some(md) = state.metadata_mut().get_mut::<TopRatedsMetadata>() {
|
||||||
|
let mut uniq: Vec<usize> = md.map.values().map(|x| x.clone()).collect();
|
||||||
|
uniq.sort();
|
||||||
|
uniq.dedup();
|
||||||
|
gd.push_str(&format!(".{}.toprated", uniq.len()));
|
||||||
|
fs::write(&gd,ron::to_string(&md.map).expect("Failed to serialize metadata")).expect("Failed to write graph");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
do_dump_times!(state, &cli, "");
|
|
||||||
do_dump_case!(state, &cli, "");
|
|
||||||
do_dump_stg!(state, &cli, "");
|
|
||||||
do_dump_toprated!(state, &cli, "");
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -599,16 +639,16 @@ let 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();
|
||||||
let env: Vec<(String, String)> = env::vars().collect();
|
let env: Vec<(String, String)> = env::vars().collect();
|
||||||
let emu = Qemu::init(&args).expect("Emu creation failed");
|
let emu = Emulator::new(&args, &env);
|
||||||
|
|
||||||
if let Some(&main_addr) = TARGET_SYMBOLS.get("FUZZ_MAIN") {
|
if let Some(main_addr) = main_addr {
|
||||||
emu.set_breakpoint(main_addr); // BREAKPOINT
|
unsafe { libafl_qemu_set_native_breakpoint(main_addr); }// BREAKPOINT
|
||||||
}
|
}
|
||||||
unsafe {
|
unsafe {
|
||||||
emu.run();
|
emu.run();
|
||||||
|
|
||||||
let mut buf = [0u8].repeat(MAX_INPUT_SIZE);
|
let mut buf = [0u8].repeat(MAX_INPUT_SIZE);
|
||||||
emu.read_mem(TARGET_SYMBOLS["FUZZ_INPUT"], buf.as_mut_slice());
|
emu.read_phys_mem(input_addr, buf.as_mut_slice());
|
||||||
|
|
||||||
let dir = env::var("SEED_DIR").map_or("./corpus".to_string(), |x| x);
|
let dir = env::var("SEED_DIR").map_or("./corpus".to_string(), |x| x);
|
||||||
let filename = if input_dump == "" {"input"} else {&input_dump};
|
let filename = if input_dump == "" {"input"} else {&input_dump};
|
||||||
@ -620,7 +660,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
|||||||
|
|
||||||
#[cfg(feature = "singlecore")]
|
#[cfg(feature = "singlecore")]
|
||||||
{
|
{
|
||||||
let monitor = RateLimitedMonitor::new();
|
let monitor = SimpleMonitor::new(|s| println!("{}", s));
|
||||||
#[cfg(not(feature = "restarting"))]
|
#[cfg(not(feature = "restarting"))]
|
||||||
{
|
{
|
||||||
let mgr = SimpleEventManager::new(monitor);
|
let mgr = SimpleEventManager::new(monitor);
|
||||||
@ -630,7 +670,7 @@ let run_client = |state: Option<_>, mut mgr, _core_id| {
|
|||||||
#[cfg(feature = "restarting")]
|
#[cfg(feature = "restarting")]
|
||||||
{
|
{
|
||||||
let mut shmem_provider = StdShMemProvider::new().unwrap();
|
let mut shmem_provider = StdShMemProvider::new().unwrap();
|
||||||
let (state, mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
|
let (state, mut mgr) = match SimpleRestartingEventManager::launch(monitor, &mut shmem_provider)
|
||||||
{
|
{
|
||||||
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
// The restarting state will spawn the same process again as child, then restarted it each time it crashes.
|
||||||
Ok(res) => res,
|
Ok(res) => res,
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
|
#![feature(is_sorted)]
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod fuzzer;
|
mod fuzzer;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub mod time;
|
mod clock;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
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 cli;
|
mod mutational;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub mod templates;
|
mod worst;
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
mod config;
|
|
@ -1,16 +1,17 @@
|
|||||||
|
#![feature(is_sorted)]
|
||||||
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
//! A libfuzzer-like fuzzer using qemu for binary-only coverage
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod fuzzer;
|
mod fuzzer;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod time;
|
mod clock;
|
||||||
|
#[cfg(target_os = "linux")]
|
||||||
|
mod qemustate;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod systemstate;
|
mod systemstate;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod cli;
|
mod worst;
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
mod templates;
|
mod mutational;
|
||||||
#[cfg(target_os = "linux")]
|
|
||||||
mod config;
|
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
pub fn main() {
|
pub fn main() {
|
||||||
|
240
fuzzers/FRET/src/mutational.rs
Normal file
240
fuzzers/FRET/src/mutational.rs
Normal file
@ -0,0 +1,240 @@
|
|||||||
|
//| The [`MutationalStage`] is the default stage used during fuzzing.
|
||||||
|
//! For the current input, it will perform a range of random mutations, and then run them in the executor.
|
||||||
|
|
||||||
|
use core::marker::PhantomData;
|
||||||
|
use std::cmp::{max, min};
|
||||||
|
|
||||||
|
use libafl::{
|
||||||
|
bolts::rands::Rand,
|
||||||
|
corpus::{Corpus, self},
|
||||||
|
fuzzer::Evaluator,
|
||||||
|
mark_feature_time,
|
||||||
|
stages::{Stage},
|
||||||
|
start_timer,
|
||||||
|
state::{HasClientPerfMonitor, HasCorpus, HasRand, UsesState, HasMetadata},
|
||||||
|
Error, prelude::{HasBytesVec, UsesInput, new_hash_feedback, StdRand, RandomSeed, MutationResult, Mutator},
|
||||||
|
};
|
||||||
|
use crate::{systemstate::{FreeRTOSSystemStateMetadata, RefinedFreeRTOSSystemState}, fuzzer::DO_NUM_INTERRUPT, clock::IcHist};
|
||||||
|
|
||||||
|
pub const MINIMUM_INTER_ARRIVAL_TIME : u32 = 700 * 1000 * (1 << 4);
|
||||||
|
|
||||||
|
//======================= Custom mutator
|
||||||
|
|
||||||
|
/// The default mutational stage
|
||||||
|
#[derive(Clone, Debug, Default)]
|
||||||
|
pub struct MyStateStage<E, EM, Z> {
|
||||||
|
#[allow(clippy::type_complexity)]
|
||||||
|
phantom: PhantomData<(E, EM, Z)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, Z> MyStateStage<E, EM, Z>
|
||||||
|
where
|
||||||
|
E: UsesState<State = Z::State>,
|
||||||
|
EM: UsesState<State = Z::State>,
|
||||||
|
Z: Evaluator<E, EM>,
|
||||||
|
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
|
||||||
|
{
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { phantom: PhantomData }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, Z> Stage<E, EM, Z> for MyStateStage<E, EM, Z>
|
||||||
|
where
|
||||||
|
E: UsesState<State = Z::State>,
|
||||||
|
EM: UsesState<State = Z::State>,
|
||||||
|
Z: Evaluator<E, EM>,
|
||||||
|
Z::State: HasClientPerfMonitor + HasCorpus + HasRand + HasMetadata,
|
||||||
|
<Z::State as UsesInput>::Input: HasBytesVec
|
||||||
|
{
|
||||||
|
fn perform(
|
||||||
|
&mut self,
|
||||||
|
fuzzer: &mut Z,
|
||||||
|
executor: &mut E,
|
||||||
|
state: &mut Self::State,
|
||||||
|
manager: &mut EM,
|
||||||
|
corpus_idx: usize,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let mut _input = state
|
||||||
|
.corpus()
|
||||||
|
.get(corpus_idx)?
|
||||||
|
.borrow_mut().clone();
|
||||||
|
let mut newinput = _input.input_mut().as_mut().unwrap().clone();
|
||||||
|
// let mut tmpinput = _input.input_mut().as_mut().unwrap().clone();
|
||||||
|
let mut do_rerun = false;
|
||||||
|
{
|
||||||
|
// need our own random generator, because borrowing rules
|
||||||
|
let mut myrand = StdRand::new();
|
||||||
|
let mut target_bytes : Vec<u8> = vec![];
|
||||||
|
{
|
||||||
|
let input = _input.input_mut().as_ref().unwrap();
|
||||||
|
let tmp = &mut state.rand_mut();
|
||||||
|
myrand.set_seed(tmp.next());
|
||||||
|
target_bytes = input.bytes().to_vec();
|
||||||
|
}
|
||||||
|
|
||||||
|
// produce a slice of absolute interrupt times
|
||||||
|
let mut interrupt_offsets : [u32; 32] = [0u32; 32];
|
||||||
|
let mut num_interrupts : usize = 0;
|
||||||
|
{
|
||||||
|
let mut start_tick : u32 = 0;
|
||||||
|
for i in 0..DO_NUM_INTERRUPT {
|
||||||
|
let mut t : [u8; 4] = [0,0,0,0];
|
||||||
|
if target_bytes.len() > (i+1)*4 {
|
||||||
|
for j in 0 as usize..4 as usize {
|
||||||
|
t[j]=target_bytes[i*4+j];
|
||||||
|
}
|
||||||
|
if i == 0 || true {
|
||||||
|
start_tick = u32::from_le_bytes(t);
|
||||||
|
} else {
|
||||||
|
start_tick = u32::saturating_add(start_tick,max(MINIMUM_INTER_ARRIVAL_TIME,u32::from_le_bytes(t)));
|
||||||
|
}
|
||||||
|
interrupt_offsets[i] = start_tick;
|
||||||
|
num_interrupts = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
interrupt_offsets.sort();
|
||||||
|
|
||||||
|
// println!("Vor Mutator: {:?}", interrupt_offsets[0..num_interrupts].to_vec());
|
||||||
|
// let num_i = min(target_bytes.len() / 4, DO_NUM_INTERRUPT);
|
||||||
|
let mut suffix = target_bytes.split_off(4 * num_interrupts);
|
||||||
|
let mut prefix : Vec<[u8; 4]> = vec![];
|
||||||
|
// let mut suffix : Vec<u8> = vec![];
|
||||||
|
#[cfg(feature = "feed_systemtrace")]
|
||||||
|
{
|
||||||
|
let tmp = _input.metadata().get::<FreeRTOSSystemStateMetadata>();
|
||||||
|
if tmp.is_some() {
|
||||||
|
let trace = tmp.expect("FreeRTOSSystemStateMetadata not found");
|
||||||
|
|
||||||
|
// calculate hits and identify snippets
|
||||||
|
let mut last_m = false;
|
||||||
|
let mut marks : Vec<(&RefinedFreeRTOSSystemState, usize, usize)>= vec![]; // 1: got interrupted, 2: interrupt handler
|
||||||
|
for i in 0..trace.inner.len() {
|
||||||
|
let curr = &trace.inner[i];
|
||||||
|
let m = interrupt_offsets[0..num_interrupts].iter().any(|x| (curr.start_tick..curr.end_tick).contains(&(*x as u64)));
|
||||||
|
if m {
|
||||||
|
marks.push((curr, i, 1));
|
||||||
|
// println!("1: {}",curr.current_task.task_name);
|
||||||
|
} else if last_m {
|
||||||
|
marks.push((curr, i, 2));
|
||||||
|
// println!("2: {}",curr.current_task.task_name);
|
||||||
|
} else {
|
||||||
|
marks.push((curr, i, 0));
|
||||||
|
}
|
||||||
|
last_m = m;
|
||||||
|
}
|
||||||
|
for i in 0..num_interrupts {
|
||||||
|
// bounds based on minimum inter-arrival time
|
||||||
|
let mut lb = 0;
|
||||||
|
let mut ub : u32 = marks[marks.len()-1].0.end_tick.try_into().expect("ticks > u32");
|
||||||
|
if i > 0 {
|
||||||
|
lb = u32::saturating_add(interrupt_offsets[i-1],MINIMUM_INTER_ARRIVAL_TIME);
|
||||||
|
}
|
||||||
|
if i < num_interrupts-1 {
|
||||||
|
ub = u32::saturating_sub(interrupt_offsets[i+1],MINIMUM_INTER_ARRIVAL_TIME);
|
||||||
|
}
|
||||||
|
// get old hit and handler
|
||||||
|
let old_hit = marks.iter().filter(
|
||||||
|
|x| x.0.start_tick < (interrupt_offsets[i] as u64) && (interrupt_offsets[i] as u64) < x.0.end_tick
|
||||||
|
).next();
|
||||||
|
let old_handler = match old_hit {
|
||||||
|
Some(s) => if s.1 < num_interrupts-1 && s.1 < marks.len()-1 {
|
||||||
|
Some(marks[s.1+1])
|
||||||
|
} else {None},
|
||||||
|
None => None
|
||||||
|
};
|
||||||
|
// find reachable alternatives
|
||||||
|
let alternatives : Vec<_> = marks.iter().filter(|x|
|
||||||
|
x.2 != 2 &&
|
||||||
|
(
|
||||||
|
x.0.start_tick < (lb as u64) && (lb as u64) < x.0.end_tick
|
||||||
|
|| x.0.start_tick < (ub as u64) && (ub as u64) < x.0.end_tick )
|
||||||
|
).collect();
|
||||||
|
// in cases there are no alternatives
|
||||||
|
if alternatives.len() == 0 {
|
||||||
|
if old_hit.is_none() {
|
||||||
|
// choose something random
|
||||||
|
let untouched : Vec<_> = marks.iter().filter(
|
||||||
|
|x| x.2 == 0
|
||||||
|
).collect();
|
||||||
|
if untouched.len() > 0 {
|
||||||
|
let tmp = interrupt_offsets[i];
|
||||||
|
let choice = myrand.choose(untouched);
|
||||||
|
interrupt_offsets[i] = myrand.between(choice.0.start_tick, choice.0.end_tick)
|
||||||
|
.try_into().expect("tick > u32");
|
||||||
|
do_rerun = true;
|
||||||
|
}
|
||||||
|
// println!("no alternatives, choose random i: {} {} -> {}",i,tmp,interrupt_offsets[i]);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// do nothing
|
||||||
|
// println!("no alternatives, do nothing i: {} {}",i,interrupt_offsets[i]);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let replacement = myrand.choose(alternatives);
|
||||||
|
if (old_hit.map_or(false, |x| x == replacement)) {
|
||||||
|
// use the old value
|
||||||
|
// println!("chose old value, do nothing i: {} {}",i,interrupt_offsets[i]);
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
let extra = if (old_hit.map_or(false, |x| x.1 < replacement.1)) {
|
||||||
|
// move futher back, respect old_handler
|
||||||
|
old_handler.map_or(0, |x| x.0.end_tick - x.0.start_tick)
|
||||||
|
} else { 0 };
|
||||||
|
let tmp = interrupt_offsets[i];
|
||||||
|
interrupt_offsets[i] = (myrand.between(replacement.0.start_tick,
|
||||||
|
replacement.0.end_tick) + extra).try_into().expect("ticks > u32");
|
||||||
|
// println!("chose new alternative, i: {} {} -> {}",i,tmp, interrupt_offsets[i]);
|
||||||
|
do_rerun = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut numbers : Vec<u32> = interrupt_offsets[0..num_interrupts].to_vec();
|
||||||
|
numbers.sort();
|
||||||
|
// println!("Mutator: {:?}", numbers);
|
||||||
|
let mut start : u32 = 0;
|
||||||
|
// for i in 0..numbers.len() {
|
||||||
|
// let tmp = numbers[i];
|
||||||
|
// numbers[i] = numbers[i]-start;
|
||||||
|
// start = tmp;
|
||||||
|
// }
|
||||||
|
for i in 0..numbers.len() {
|
||||||
|
prefix.push(u32::to_le_bytes(numbers[i]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#[cfg(not(feature = "feed_systemtrace"))]
|
||||||
|
{
|
||||||
|
let metadata = state.metadata();
|
||||||
|
let hist = metadata.get::<IcHist>().unwrap();
|
||||||
|
let maxtick : u64 = hist.1.0;
|
||||||
|
// let maxtick : u64 = (_input.exec_time().expect("No duration found").as_nanos() >> 4).try_into().unwrap();
|
||||||
|
let mut numbers : Vec<u32> = vec![];
|
||||||
|
for i in 0..num_interrupts {
|
||||||
|
prefix.push(u32::to_le_bytes(myrand.between(0, min(maxtick, u32::MAX as u64)).try_into().expect("ticks > u32")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut n : Vec<u8> = vec![];
|
||||||
|
n = [prefix.concat(), suffix].concat();
|
||||||
|
newinput.bytes_mut().clear();
|
||||||
|
newinput.bytes_mut().append(&mut n);
|
||||||
|
}
|
||||||
|
// InterruptShifterMutator::mutate(&mut mymut, state, &mut input, 0)?;
|
||||||
|
if do_rerun {
|
||||||
|
let (_, corpus_idx) = fuzzer.evaluate_input(state, executor, manager, newinput)?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E, EM, Z> UsesState for MyStateStage<E, EM, Z>
|
||||||
|
where
|
||||||
|
E: UsesState<State = Z::State>,
|
||||||
|
EM: UsesState<State = Z::State>,
|
||||||
|
Z: Evaluator<E, EM>,
|
||||||
|
Z::State: HasClientPerfMonitor + HasCorpus + HasRand,
|
||||||
|
{
|
||||||
|
type State = Z::State;
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user