Files
wehub-resource-sync bd44b5aa08
Test / build-libghostty-vt (x86_64-windows-gnu) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-ios) (push) Has been cancelled
Test / build-libghostty-vt-macos (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt-macos (x86_64-macos) (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-dist (push) Has been cancelled
Test / GTK x11=true wayland=true (push) Has been cancelled
Test / Build -Dsimd=false (push) Has been cancelled
Test / Build -Dsimd=true (push) Has been cancelled
Test / Build -Dsentry=false (push) Has been cancelled
Test / Build -Dsentry=true (push) Has been cancelled
Test / zig-fmt (push) Has been cancelled
Test / GitHub Actions Pins (push) Has been cancelled
Test / prettier (push) Has been cancelled
Test / swiftlint (push) Has been cancelled
Test / alejandra (push) Has been cancelled
Test / typos (push) Has been cancelled
Nix / Required Checks: Nix (push) Has been cancelled
Nix / check-zig-cache-hash (push) Has been cancelled
Test / skip (push) Has been cancelled
Test / Required Checks: Test (push) Has been cancelled
Test / build-bench (push) Has been cancelled
Test / list-examples (push) Has been cancelled
Test / Example ${{ matrix.dir }} (Windows) (push) Has been cancelled
Test / build-cmake (push) Has been cancelled
Test / test-lib-vt-pkgconfig (push) Has been cancelled
Test / build-flatpak (push) Has been cancelled
Test / build-snap (push) Has been cancelled
Test / build-libghostty-vt (aarch64-linux) (push) Has been cancelled
Test / build-libghostty-vt (aarch64-macos) (push) Has been cancelled
Test / build-libghostty-vt (wasm32-freestanding) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-linux-musl) (push) Has been cancelled
Test / build-libghostty-vt (x86_64-macos) (push) Has been cancelled
Test / build-libghostty-vt-android (aarch64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-android (arm-linux-androideabi) (push) Has been cancelled
Test / build-libghostty-vt-android (x86_64-linux-android) (push) Has been cancelled
Test / build-libghostty-vt-windows (push) Has been cancelled
Test / build-libghostty-windows-gnu (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-linux (namespace-profile-ghostty-md-arm64) (push) Has been cancelled
Test / build-linux-libghostty (push) Has been cancelled
Test / build-nix (namespace-profile-ghostty-md) (push) Has been cancelled
Test / build-dist-lib-vt (push) Has been cancelled
Test / trigger-snap (push) Has been cancelled
Test / trigger-flatpak (push) Has been cancelled
Test / build-macos (push) Has been cancelled
Test / build-macos-freetype (push) Has been cancelled
Test / test (push) Has been cancelled
Test / test-lib-vt (push) Has been cancelled
Test / GTK x11=false wayland=false (push) Has been cancelled
Test / GTK x11=true wayland=false (push) Has been cancelled
Test / GTK x11=false wayland=true (push) Has been cancelled
Test / test-macos (push) Has been cancelled
Test / test-windows (push) Has been cancelled
Test / Build -Di18n=false (push) Has been cancelled
Test / Build -Di18n=true (push) Has been cancelled
Test / Build test/fuzz-libghostty (push) Has been cancelled
Test / shellcheck (push) Has been cancelled
Test / translations (push) Has been cancelled
Test / blueprint-compiler (push) Has been cancelled
Test / Test pkg/wuffs (push) Has been cancelled
Test / Test build on Debian 13 (push) Has been cancelled
Test / valgrind (push) Has been cancelled
Test / Example ${{ matrix.dir }} (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:02:48 +08:00

127 lines
4.6 KiB
Nu
Executable File

#!/usr/bin/env nu
# Replay AFL++ crash files against their corresponding fuzzer binaries.
#
# AFL++ stores crashing inputs as raw byte files under:
# afl-out/<fuzzer>/default/crashes/
#
# Each file's name encodes metadata about how the crash was found, e.g.:
# id:000001,sig:06,src:004129,time:690036,execs:1989720,op:havoc,rep:4
#
# The fuzzer binaries (fuzz-parser, fuzz-stream) read input from stdin,
# so each crash file is replayed by piping it into the binary:
# open --raw <crash-file> | fuzz-<name>
#
# Modes:
# (default) Replay every crash file and report pass/fail.
# --list Print crash file paths, one per line (no replay).
# --json Emit structured JSON with fuzzer name, file path,
# binary path, and a ready-to-run replay command.
# Useful for LLM agents that need to enumerate and
# selectively replay specific crashes.
# --fuzzer Restrict to a single fuzzer target (e.g. "stream").
def main [
afl_out: string = "afl-out" # Path to the AFL++ output directory
--list (-l) # List crash files without replaying
--json (-j) # Output as JSON (implies --list)
--fuzzer (-f): string # Only process this fuzzer (e.g. "stream" or "parser")
] {
# Directory where `zig build` places the instrumented fuzzer binaries.
let bin_dir = "zig-out/bin"
# All known fuzzer targets. Each one has a corresponding binary
# named fuzz-<name> and AFL++ output under afl-out/<name>/.
let all_fuzzers = [parser stream]
# If --fuzzer is given, restrict to just that target; otherwise run all.
let fuzzers = if $fuzzer != null { [$fuzzer] } else { $all_fuzzers }
# --json implies --list (we never replay in JSON mode).
let list_only = $list or $json
# Accumulator for --list/--json output records.
mut results = []
# Counter for crash files that still reproduce (non-zero exit from binary).
mut failures = 0
for fuzz in $fuzzers {
# AFL++ writes crash inputs to <out>/<fuzzer>/default/crashes/.
let crashes_dir = $"($afl_out)/($fuzz)/default/crashes"
# The replay binary for this fuzzer target.
let binary = $"($bin_dir)/fuzz-($fuzz)"
# Skip this fuzzer if no crashes directory exists (fuzzer may not
# have been run, or it found no crashes).
if not ($crashes_dir | path exists) {
continue
}
# Gather all crash files, filtering out:
# - Directories (AFL++ may create subdirs like .synced/)
# - README.txt (AFL++ places a README in the crashes dir)
let crash_files = (ls $crashes_dir
| where type == file
| where { |f| ($f.name | path basename) != "README.txt" }
| get name)
# In list-only mode, collect metadata about each crash file
# without actually replaying it. This lets an LLM enumerate
# crashes and decide which ones to investigate.
if $list_only {
for crash_file in $crash_files {
$results = ($results | append {
fuzzer: $fuzz,
file: $crash_file,
binary: $binary,
replay_cmd: $"open --raw ($crash_file) | ($binary)",
})
}
continue
}
# In replay mode, we need the binary to exist.
if not ($binary | path exists) {
print -e $"WARNING: binary ($binary) not found, skipping ($fuzz)"
continue
}
# Replay each crash file by piping its raw bytes into the fuzzer
# binary via stdin. The binary exits non-zero if the crash
# reproduces (e.g. SIGABRT / signal 6).
for crash_file in $crash_files {
print $"==> ($crash_file)"
let result = do -i { open --raw $crash_file | run-external $binary }
if $result != null {
$failures += 1
}
}
}
# Emit collected results in the requested format.
if $list_only {
if $json {
# JSON output: array of objects, each with fuzzer, file,
# binary, and a replay_cmd string an LLM can execute directly.
print ($results | to json)
} else {
# Plain text: one file path per line.
for r in $results {
print $r.file
}
}
return
}
# Summary: exit 1 if any crashes still reproduce so CI / scripts
# can detect regressions.
if $failures > 0 {
print $"FAILED: ($failures) crash\(es\) still reproduce"
exit 1
}
print "All crash files replayed."
}