2026-04-13 16:03:20 +08:00
|
|
|
# ── Fuzzing ───────────────────────────────────────────────────────────────────
|
|
|
|
|
export RISC0_DEV_MODE := "1"
|
|
|
|
|
|
2026-04-15 15:47:01 +08:00
|
|
|
# List all registered fuzz targets (reads fuzz/Cargo.toml via cargo-fuzz)
|
|
|
|
|
list-targets:
|
|
|
|
|
cargo fuzz list
|
|
|
|
|
|
|
|
|
|
# Run all fuzz targets for TIME seconds each (default: 30).
|
|
|
|
|
# Targets are discovered automatically from fuzz/Cargo.toml — no edit needed here
|
|
|
|
|
# when a new [[bin]] entry is added.
|
2026-04-13 16:03:20 +08:00
|
|
|
fuzz TIME="30":
|
2026-04-15 15:47:01 +08:00
|
|
|
#!/bin/bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
for target in $(cargo fuzz list 2>/dev/null); do
|
|
|
|
|
echo "=== fuzzing $target for {{TIME}}s ==="
|
|
|
|
|
cargo fuzz run "$target" -- -max_total_time={{TIME}}
|
|
|
|
|
done
|
2026-04-13 16:03:20 +08:00
|
|
|
|
2026-04-15 15:47:01 +08:00
|
|
|
# Re-run the saved corpus for every target (regression mode, no new mutations)
|
2026-04-13 16:03:20 +08:00
|
|
|
fuzz-regression:
|
2026-04-15 15:47:01 +08:00
|
|
|
#!/bin/bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
for target in $(cargo fuzz list 2>/dev/null); do
|
|
|
|
|
echo "=== regression $target ==="
|
|
|
|
|
mkdir -p "fuzz/corpus/$target"
|
|
|
|
|
cargo fuzz run "$target" "fuzz/corpus/$target" -- -runs=0
|
|
|
|
|
done
|
2026-04-13 16:03:20 +08:00
|
|
|
|
|
|
|
|
# Minimise a crash artifact
|
|
|
|
|
# Usage: just fuzz-tmin fuzz_state_transition fuzz/artifacts/fuzz_state_transition/crash-XXX
|
|
|
|
|
fuzz-tmin TARGET ARTIFACT:
|
|
|
|
|
cargo fuzz tmin {{TARGET}} {{ARTIFACT}}
|
|
|
|
|
|
|
|
|
|
# Run the proptest-based property tests
|
|
|
|
|
fuzz-props:
|
|
|
|
|
cargo test -p fuzz_props --release
|
|
|
|
|
|
|
|
|
|
# Pull the latest LEZ changes from the sibling logos-execution-zone directory
|
|
|
|
|
update-lez:
|
|
|
|
|
git -C ../logos-execution-zone pull --ff-only
|
|
|
|
|
|
|
|
|
|
# ── Corpus management ─────────────────────────────────────────────────────────
|
|
|
|
|
|
2026-04-15 15:47:01 +08:00
|
|
|
# Minimise the corpus for all targets (removes dominated inputs)
|
2026-04-13 16:03:20 +08:00
|
|
|
corpus-cmin:
|
2026-04-15 15:47:01 +08:00
|
|
|
#!/bin/bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
for target in $(cargo fuzz list 2>/dev/null); do
|
|
|
|
|
echo "=== cmin $target ==="
|
|
|
|
|
cargo fuzz cmin "$target"
|
|
|
|
|
done
|
2026-04-13 16:03:20 +08:00
|
|
|
|
|
|
|
|
# Minimise the corpus for a single target
|
|
|
|
|
# Usage: just corpus-cmin-target fuzz_state_transition
|
|
|
|
|
corpus-cmin-target TARGET:
|
|
|
|
|
cargo fuzz cmin {{TARGET}}
|
|
|
|
|
|
2026-04-15 15:47:01 +08:00
|
|
|
# ── Adding a new target ───────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
# Scaffold a new fuzz target — fully automated, no manual edits required.
|
|
|
|
|
#
|
|
|
|
|
# Steps performed automatically:
|
|
|
|
|
# 1. Creates fuzz/corpus/<TARGET>/
|
|
|
|
|
# 2. Copies fuzz/fuzz_targets/_template.rs → fuzz/fuzz_targets/<TARGET>.rs
|
|
|
|
|
# 3. Appends the [[bin]] entry to fuzz/Cargo.toml
|
|
|
|
|
# 4. Inserts <TARGET> into every strategy matrix in .github/workflows/fuzz.yml
|
|
|
|
|
#
|
|
|
|
|
# Usage: just new-target my_feature
|
|
|
|
|
# (the "fuzz_" prefix is added automatically)
|
|
|
|
|
new-target NAME:
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
TARGET="fuzz_{{NAME}}"
|
|
|
|
|
TEMPLATE="fuzz/fuzz_targets/_template.rs"
|
|
|
|
|
RS_FILE="fuzz/fuzz_targets/${TARGET}.rs"
|
|
|
|
|
CORPUS_DIR="fuzz/corpus/${TARGET}"
|
|
|
|
|
|
|
|
|
|
# ── 1. Create corpus directory ────────────────────────────────────────────
|
|
|
|
|
mkdir -p "$CORPUS_DIR"
|
|
|
|
|
echo "[1/4] Created corpus directory: $CORPUS_DIR"
|
|
|
|
|
|
|
|
|
|
# ── 2. Copy the typed fuzz target template ────────────────────────────────
|
|
|
|
|
if [ -f "$RS_FILE" ]; then
|
|
|
|
|
echo "SKIP [2/4]: $RS_FILE already exists — not overwriting."
|
|
|
|
|
else
|
|
|
|
|
cp "$TEMPLATE" "$RS_FILE"
|
|
|
|
|
echo "[2/4] Created target from template: $RS_FILE"
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ── 3 & 4. Update Cargo.toml and fuzz.yml automatically ──────────────────
|
|
|
|
|
python3 scripts/add_fuzz_target.py "$TARGET"
|
|
|
|
|
echo ""
|
2026-05-21 17:40:00 +08:00
|
|
|
echo "Done! Verify the libFuzzer build with:"
|
2026-04-15 15:47:01 +08:00
|
|
|
echo " RISC0_DEV_MODE=1 cargo fuzz build ${TARGET}"
|
2026-05-21 17:40:00 +08:00
|
|
|
echo ""
|
|
|
|
|
echo "Verify the AFL++ build with:"
|
|
|
|
|
echo " cd fuzz && cargo afl build --no-default-features --features fuzzer-afl --release --bin ${TARGET}"
|
|
|
|
|
|
|
|
|
|
# ── AFL++ fuzzing ──────────────────────────────────────────────────────────────
|
|
|
|
|
# Prerequisites (install once):
|
|
|
|
|
# macOS: brew install afl-fuzz && cargo install cargo-afl
|
|
|
|
|
# Linux: Build AFL++ from source (recommended — Debian/Ubuntu apt packages are
|
|
|
|
|
# several major versions behind; see https://github.com/AFLplusplus/AFLplusplus):
|
|
|
|
|
# git clone https://github.com/AFLplusplus/AFLplusplus
|
|
|
|
|
# cd AFLplusplus && make distrib && sudo make install
|
|
|
|
|
# Then: cargo install cargo-afl
|
|
|
|
|
|
|
|
|
|
# Build ALL fuzz targets for AFL++ (output: fuzz/target/release/<target>)
|
|
|
|
|
afl-build:
|
|
|
|
|
cd fuzz && cargo afl build --no-default-features --features fuzzer-afl --release
|
|
|
|
|
|
|
|
|
|
# Build a SINGLE fuzz target for AFL++
|
|
|
|
|
# Usage: just afl-build-target fuzz_state_transition
|
|
|
|
|
afl-build-target TARGET:
|
|
|
|
|
cd fuzz && cargo afl build --no-default-features --features fuzzer-afl --release --bin {{TARGET}}
|
|
|
|
|
|
|
|
|
|
# Disable the macOS crash reporter daemon so AFL++ can detect crashes reliably.
|
|
|
|
|
# This is a macOS-only requirement; on Linux this is a no-op.
|
|
|
|
|
# The `fuzz-afl` recipe calls this automatically; run it manually if you want
|
|
|
|
|
# to keep the reporter disabled across multiple just invocations.
|
|
|
|
|
#
|
|
|
|
|
# Re-enable with: just afl-macos-teardown
|
|
|
|
|
afl-macos-setup:
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
if [ "$(uname)" != "Darwin" ]; then echo "Not macOS — nothing to do."; exit 0; fi
|
|
|
|
|
SL=/System/Library; PL=com.apple.ReportCrash
|
|
|
|
|
echo "Disabling macOS crash reporter (required by AFL++)…"
|
|
|
|
|
launchctl unload -w "${SL}/LaunchAgents/${PL}.plist" 2>/dev/null || true
|
|
|
|
|
sudo launchctl unload -w "${SL}/LaunchDaemons/${PL}.Root.plist" 2>/dev/null || true
|
|
|
|
|
echo "Done. Re-enable with: just afl-macos-teardown"
|
|
|
|
|
|
|
|
|
|
# Re-enable the macOS crash reporter after an AFL++ session.
|
|
|
|
|
afl-macos-teardown:
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
if [ "$(uname)" != "Darwin" ]; then echo "Not macOS — nothing to do."; exit 0; fi
|
|
|
|
|
SL=/System/Library; PL=com.apple.ReportCrash
|
|
|
|
|
echo "Re-enabling macOS crash reporter…"
|
|
|
|
|
launchctl load -w "${SL}/LaunchAgents/${PL}.plist" 2>/dev/null || true
|
|
|
|
|
sudo launchctl load -w "${SL}/LaunchDaemons/${PL}.Root.plist" 2>/dev/null || true
|
|
|
|
|
echo "Done."
|
|
|
|
|
|
|
|
|
|
# Run AFL++ on one target or ALL targets when no target is supplied.
|
|
|
|
|
# Builds binaries as needed; syncs the queue to the shared corpus when done.
|
|
|
|
|
#
|
|
|
|
|
# On macOS the crash reporter is disabled automatically for the duration of the
|
|
|
|
|
# run and re-enabled when the script exits (via a shell trap).
|
|
|
|
|
#
|
|
|
|
|
# Requires afl-fuzz and cargo-afl to be installed locally:
|
|
|
|
|
# macOS: brew install afl-fuzz && cargo install cargo-afl
|
|
|
|
|
# Linux: Build AFL++ from source (apt packages are several major versions
|
|
|
|
|
# behind): see https://github.com/AFLplusplus/AFLplusplus
|
|
|
|
|
#
|
|
|
|
|
# Usage: just fuzz-afl # all targets, 120 s each
|
|
|
|
|
# just fuzz-afl "" 60 # all targets, 60 s each
|
|
|
|
|
# just fuzz-afl fuzz_state_transition # single target, 120 s
|
|
|
|
|
# just fuzz-afl fuzz_state_transition 300 # single target, 300 s
|
|
|
|
|
fuzz-afl TARGET="" TIME="120":
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
TARGET="{{TARGET}}"
|
|
|
|
|
TIME="{{TIME}}"
|
|
|
|
|
|
|
|
|
|
# ── Collect targets to run ────────────────────────────────────────────────
|
|
|
|
|
if [ -z "$TARGET" ]; then
|
|
|
|
|
TARGETS=($(cargo fuzz list 2>/dev/null))
|
|
|
|
|
else
|
|
|
|
|
TARGETS=("$TARGET")
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ── Require local AFL++ installation ─────────────────────────────────────
|
|
|
|
|
if ! command -v afl-fuzz &>/dev/null; then
|
|
|
|
|
echo "ERROR: afl-fuzz not found in PATH."
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Install AFL++ before running this recipe:"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " macOS : brew install afl-fuzz"
|
|
|
|
|
echo ""
|
|
|
|
|
echo " Linux : Build from source (apt packages are several major versions behind):"
|
|
|
|
|
echo " git clone https://github.com/AFLplusplus/AFLplusplus"
|
|
|
|
|
echo " cd AFLplusplus && make distrib && sudo make install"
|
|
|
|
|
echo ""
|
|
|
|
|
echo "Also install the cargo-afl build wrapper:"
|
|
|
|
|
echo " cargo install cargo-afl"
|
|
|
|
|
echo ""
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
if ! command -v cargo-afl &>/dev/null && ! cargo afl --version &>/dev/null 2>&1; then
|
|
|
|
|
echo "ERROR: cargo-afl not found."
|
|
|
|
|
echo " cargo install cargo-afl"
|
|
|
|
|
exit 1
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ── macOS: disable crash reporter for the duration of this run ───────────
|
|
|
|
|
if [ "$(uname)" = "Darwin" ]; then
|
|
|
|
|
SL=/System/Library; PL=com.apple.ReportCrash
|
|
|
|
|
echo "macOS: disabling crash reporter (AFL++ requirement)…"
|
|
|
|
|
launchctl unload -w "${SL}/LaunchAgents/${PL}.plist" 2>/dev/null || true
|
|
|
|
|
sudo launchctl unload -w "${SL}/LaunchDaemons/${PL}.Root.plist" 2>/dev/null || true
|
|
|
|
|
# Re-enable on any exit — normal, error, or Ctrl-C
|
|
|
|
|
trap '
|
|
|
|
|
echo "Re-enabling macOS crash reporter…"
|
|
|
|
|
SL=/System/Library; PL=com.apple.ReportCrash
|
|
|
|
|
launchctl load -w "${SL}/LaunchAgents/${PL}.plist" 2>/dev/null || true
|
|
|
|
|
sudo launchctl load -w "${SL}/LaunchDaemons/${PL}.Root.plist" 2>/dev/null || true
|
|
|
|
|
' EXIT
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
# ── Run targets ───────────────────────────────────────────────────────────
|
|
|
|
|
_run_one() {
|
|
|
|
|
local t="$1"
|
|
|
|
|
local BINARY="fuzz/target/release/$t"
|
|
|
|
|
local CORPUS="fuzz/corpus/$t"
|
|
|
|
|
local OUTPUT="afl-output/$t"
|
|
|
|
|
mkdir -p "$CORPUS" "$OUTPUT"
|
|
|
|
|
if [ ! -f "$BINARY" ]; then
|
|
|
|
|
echo "Binary not found — building $t first…"
|
|
|
|
|
just afl-build-target "$t"
|
|
|
|
|
fi
|
|
|
|
|
timeout "$TIME" afl-fuzz -i "$CORPUS" -o "$OUTPUT" -- "$BINARY" || true
|
|
|
|
|
}
|
|
|
|
|
for t in "${TARGETS[@]}"; do
|
|
|
|
|
echo "=== afl++ $t for ${TIME}s ==="
|
|
|
|
|
_run_one "$t"
|
|
|
|
|
done
|
|
|
|
|
just afl-corpus-sync
|
|
|
|
|
|
|
|
|
|
# Run AFL++ with N parallel instances (1 main + N-1 secondary) for TIME seconds.
|
|
|
|
|
# Requires that afl-fuzz is on PATH; all instances share afl-output/{{TARGET}}/.
|
|
|
|
|
# On macOS the crash reporter is disabled automatically for the duration of the
|
|
|
|
|
# run and re-enabled when the script exits.
|
|
|
|
|
#
|
|
|
|
|
# Usage: just fuzz-afl-parallel fuzz_state_transition
|
|
|
|
|
# just fuzz-afl-parallel fuzz_state_transition 8 600
|
|
|
|
|
fuzz-afl-parallel TARGET WORKERS="4" TIME="300":
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
BINARY="fuzz/target/release/{{TARGET}}"
|
|
|
|
|
CORPUS="fuzz/corpus/{{TARGET}}"
|
|
|
|
|
OUTPUT="afl-output/{{TARGET}}"
|
|
|
|
|
mkdir -p "$CORPUS" "$OUTPUT"
|
|
|
|
|
if [ ! -f "$BINARY" ]; then
|
|
|
|
|
echo "Binary not found — building first…"
|
|
|
|
|
just afl-build-target {{TARGET}}
|
|
|
|
|
fi
|
|
|
|
|
# ── macOS: disable crash reporter for the duration of this run ───────────
|
|
|
|
|
if [ "$(uname)" = "Darwin" ]; then
|
|
|
|
|
SL=/System/Library; PL=com.apple.ReportCrash
|
|
|
|
|
echo "macOS: disabling crash reporter (AFL++ requirement)…"
|
|
|
|
|
launchctl unload -w "${SL}/LaunchAgents/${PL}.plist" 2>/dev/null || true
|
|
|
|
|
sudo launchctl unload -w "${SL}/LaunchDaemons/${PL}.Root.plist" 2>/dev/null || true
|
|
|
|
|
trap '
|
|
|
|
|
echo "Re-enabling macOS crash reporter…"
|
|
|
|
|
SL=/System/Library; PL=com.apple.ReportCrash
|
|
|
|
|
launchctl load -w "${SL}/LaunchAgents/${PL}.plist" 2>/dev/null || true
|
|
|
|
|
sudo launchctl load -w "${SL}/LaunchDaemons/${PL}.Root.plist" 2>/dev/null || true
|
|
|
|
|
' EXIT
|
|
|
|
|
fi
|
|
|
|
|
# Main instance
|
|
|
|
|
afl-fuzz -M main -i "$CORPUS" -o "$OUTPUT" -- "$BINARY" &
|
|
|
|
|
# Secondary instances
|
|
|
|
|
for i in $(seq 1 $(( {{WORKERS}} - 1 ))); do
|
|
|
|
|
afl-fuzz -S "secondary${i}" -i "$CORPUS" -o "$OUTPUT" -- "$BINARY" &
|
|
|
|
|
done
|
|
|
|
|
sleep {{TIME}}
|
|
|
|
|
kill $(jobs -p) 2>/dev/null || true
|
|
|
|
|
wait 2>/dev/null || true
|
|
|
|
|
just afl-corpus-sync
|
|
|
|
|
|
|
|
|
|
# Copy all queue entries from every AFL++ output directory into the matching
|
|
|
|
|
# shared corpus directory (fuzz/corpus/<target>/). Run after any AFL++ session
|
|
|
|
|
# to make new interesting inputs available to cargo-fuzz and CI.
|
|
|
|
|
afl-corpus-sync:
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
if [ ! -d afl-output ]; then
|
|
|
|
|
echo "afl-output/ does not exist — nothing to sync."
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
for target_dir in afl-output/*/; do
|
|
|
|
|
TARGET=$(basename "$target_dir")
|
|
|
|
|
DEST="fuzz/corpus/${TARGET}"
|
|
|
|
|
mkdir -p "$DEST"
|
|
|
|
|
count=0
|
|
|
|
|
for instance_dir in "$target_dir"*/; do
|
|
|
|
|
QUEUE="${instance_dir}queue"
|
|
|
|
|
[ -d "$QUEUE" ] || continue
|
|
|
|
|
for f in "$QUEUE"/id:*; do
|
|
|
|
|
[ -f "$f" ] || continue
|
|
|
|
|
HASH=$(sha1sum "$f" | cut -d' ' -f1)
|
|
|
|
|
DEST_FILE="${DEST}/${HASH}"
|
|
|
|
|
if [ ! -f "$DEST_FILE" ]; then
|
|
|
|
|
cp "$f" "$DEST_FILE"
|
|
|
|
|
count=$((count + 1))
|
|
|
|
|
fi
|
|
|
|
|
done
|
|
|
|
|
done
|
|
|
|
|
echo "Synced $count new input(s) → $DEST"
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Show AFL++ campaign statistics for a target
|
|
|
|
|
# Usage: just afl-status fuzz_state_transition
|
|
|
|
|
afl-status TARGET:
|
|
|
|
|
afl-whatsup afl-output/{{TARGET}}
|
|
|
|
|
|
|
|
|
|
# Minimise a crash or hang artifact to the smallest reproducing input.
|
|
|
|
|
# Usage: just afl-tmin fuzz_state_transition afl-output/fuzz_state_transition/crashes/id:000000,...
|
|
|
|
|
afl-tmin TARGET ARTIFACT:
|
|
|
|
|
afl-tmin -i {{ARTIFACT}} -o {{ARTIFACT}}.min -- fuzz/target/release/{{TARGET}}
|
|
|
|
|
|
|
|
|
|
# Pretty-print an AFL++ artifact as a Rust byte-string literal (for copy-paste
|
|
|
|
|
# into a unit test or issue report).
|
|
|
|
|
# Usage: just afl-fmt afl-output/fuzz_state_transition/crashes/id:000000,...
|
|
|
|
|
afl-fmt ARTIFACT:
|
|
|
|
|
python3 -c "import sys; data=open(sys.argv[1],'rb').read(); print('b\"' + ''.join(f'\\\\x{b:02x}' for b in data) + '\"')" {{ARTIFACT}}
|
|
|
|
|
|
|
|
|
|
# ── Coverage ──────────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
# Generate a coverage report for a single target.
|
|
|
|
|
#
|
|
|
|
|
# Step 1 (libFuzzer): cargo fuzz coverage {{TARGET}}
|
|
|
|
|
# Step 2 (AFL++, only if afl-output/{{TARGET}}/ exists):
|
|
|
|
|
# Build with instrument-coverage, run the AFL++ queue through the binary,
|
|
|
|
|
# merge raw profiles, and generate an HTML report in coverage/afl/{{TARGET}}/.
|
|
|
|
|
#
|
|
|
|
|
# Usage: just coverage fuzz_state_transition
|
|
|
|
|
coverage TARGET:
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
# ── Step 1: libFuzzer coverage ────────────────────────────────────────────
|
|
|
|
|
echo "=== cargo fuzz coverage {{TARGET}} ==="
|
|
|
|
|
cargo fuzz coverage {{TARGET}} || true
|
|
|
|
|
|
|
|
|
|
# ── Step 2: AFL++ LLVM coverage (only if queue data exists) ──────────────
|
|
|
|
|
AFL_OUTPUT="afl-output/{{TARGET}}"
|
|
|
|
|
if [ ! -d "$AFL_OUTPUT" ]; then
|
|
|
|
|
echo "No AFL++ output for {{TARGET}} — skipping AFL++ coverage step."
|
|
|
|
|
exit 0
|
|
|
|
|
fi
|
|
|
|
|
echo "=== AFL++ LLVM coverage for {{TARGET}} ==="
|
|
|
|
|
BINARY_DIR="fuzz/target/release"
|
|
|
|
|
COV_DIR="coverage/afl/{{TARGET}}"
|
|
|
|
|
PROFRAW_DIR="${COV_DIR}/profraw"
|
|
|
|
|
mkdir -p "$PROFRAW_DIR"
|
|
|
|
|
|
|
|
|
|
# Build the target with LLVM instrumentation enabled.
|
|
|
|
|
RUSTFLAGS="-C instrument-coverage" \
|
|
|
|
|
cargo build \
|
|
|
|
|
--manifest-path fuzz/Cargo.toml \
|
|
|
|
|
--no-default-features \
|
|
|
|
|
--features fuzzer-afl \
|
|
|
|
|
--release \
|
|
|
|
|
--bin {{TARGET}}
|
|
|
|
|
|
|
|
|
|
BINARY="${BINARY_DIR}/{{TARGET}}"
|
|
|
|
|
|
|
|
|
|
# Run every queue entry through the instrumented binary.
|
|
|
|
|
idx=0
|
|
|
|
|
for instance_dir in "$AFL_OUTPUT"/*/; do
|
|
|
|
|
QUEUE="${instance_dir}queue"
|
|
|
|
|
[ -d "$QUEUE" ] || continue
|
|
|
|
|
for f in "$QUEUE"/id:*; do
|
|
|
|
|
[ -f "$f" ] || continue
|
|
|
|
|
LLVM_PROFILE_FILE="${PROFRAW_DIR}/${idx}.profraw" "$BINARY" < "$f" 2>/dev/null || true
|
|
|
|
|
idx=$((idx + 1))
|
|
|
|
|
done
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
# Merge raw profiles.
|
|
|
|
|
PROFDATA="${COV_DIR}/merged.profdata"
|
|
|
|
|
llvm-profdata merge -sparse "${PROFRAW_DIR}"/*.profraw -o "$PROFDATA"
|
|
|
|
|
|
|
|
|
|
# Generate HTML report.
|
|
|
|
|
HTML_DIR="${COV_DIR}/html"
|
|
|
|
|
mkdir -p "$HTML_DIR"
|
|
|
|
|
llvm-cov show \
|
|
|
|
|
"$BINARY" \
|
|
|
|
|
--instr-profile="$PROFDATA" \
|
|
|
|
|
--format=html \
|
|
|
|
|
--output-dir="$HTML_DIR" \
|
|
|
|
|
--ignore-filename-regex='\.cargo|rustc'
|
|
|
|
|
echo "AFL++ HTML coverage report: ${HTML_DIR}/index.html"
|
|
|
|
|
|
|
|
|
|
# Generate coverage for ALL registered fuzz targets (libFuzzer + AFL++).
|
|
|
|
|
coverage-all:
|
|
|
|
|
#!/bin/bash
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
for target in $(cargo fuzz list 2>/dev/null); do
|
|
|
|
|
echo "=== coverage $target ==="
|
|
|
|
|
just coverage "$target"
|
|
|
|
|
done
|
2026-04-15 15:47:01 +08:00
|
|
|
|
2026-04-13 16:03:20 +08:00
|
|
|
# ── Housekeeping ──────────────────────────────────────────────────────────────
|
|
|
|
|
|
|
|
|
|
# Remove all Cargo build artefacts (workspace + fuzz sub-crate)
|
|
|
|
|
clean:
|
|
|
|
|
cargo clean
|
|
|
|
|
cargo clean --manifest-path fuzz/Cargo.toml
|
|
|
|
|
|
|
|
|
|
# Remove libFuzzer crash/timeout artifacts for all targets (corpus is kept)
|
|
|
|
|
clean-artifacts:
|
|
|
|
|
rm -rf fuzz/artifacts/
|
|
|
|
|
|
2026-05-21 17:40:00 +08:00
|
|
|
# Remove coverage reports generated by `cargo fuzz coverage` and `just coverage`
|
2026-04-13 16:03:20 +08:00
|
|
|
clean-coverage:
|
2026-05-21 17:40:00 +08:00
|
|
|
rm -rf fuzz/coverage/ coverage/
|
|
|
|
|
|
|
|
|
|
# Remove AFL++ output directories (crash/hang/queue findings)
|
|
|
|
|
clean-afl:
|
|
|
|
|
rm -rf afl-output/
|
2026-04-13 16:03:20 +08:00
|
|
|
|
2026-05-21 17:40:00 +08:00
|
|
|
# Remove everything: builds, artifacts, coverage, and AFL++ output
|
|
|
|
|
clean-all: clean clean-artifacts clean-coverage clean-afl
|