Author and run AFL++ - out-of-process coverage-guided fuzzer (a community fork of Google's original AFL with improved mutations and instrumentation). Covers afl-cc / afl-clang-fast instrumented build, afl-fuzz invocation, parallel master/slave (-M / -S), dictionary support (-x), QEMU mode (-Q) for binaries without source, output structure (queue / crashes / hangs), crash minimisation (afl-tmin), corpus minimisation (afl-cmin), crash filename triage, and CI integration. Use for fuzzing standalone binaries (file processors, command-line tools) where libFuzzer's in-process model doesn't fit; for cross-fuzzer corpus strategy see corpus-management-reference.
80
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
Distinct from libFuzzer (in-process) - AFL++ (per github.com/AFLplusplus/AFLplusplus) fits binaries you can't compile-in (closed-source, multi-language) and tools that process inputs end-to-end (parsers reading files, decoders reading stdin).
For sanitiser pairing see
sanitiser-integration-reference;
for corpus discipline see
corpus-management-reference.
Per AFL++ README:
# Docker
docker pull aflplusplus/aflplusplus
docker run -ti -v /location/of/your/target:/src aflplusplus/aflplusplus
# Or apt (Debian/Ubuntu)
apt-get install -y afl++For local build see docs/INSTALL.md in the AFL++ repo.
CC=afl-cc CXX=afl-c++ ./configure --disable-shared
make clean allOr LLVM-based (recommended):
CC=afl-clang-fast CXX=afl-clang-fast++ makeafl-clang-fast produces ~3x faster instrumented binaries than
the legacy afl-cc.
Add sanitisers in the standard way:
AFL_USE_ASAN=1 AFL_USE_UBSAN=1 CC=afl-clang-fast \
CFLAGS="-fno-sanitize-recover=all -fno-omit-frame-pointer -g" \
makeAFL_USE_ASAN + AFL_USE_UBSAN env vars pass the sanitiser flags
through AFL's compiler driver.
./afl-fuzz -i seeds/ -o output/ -- ./target @@Per AFL++ README:
-i seeds/ - input seed corpus-o output/ - output directory (created if absent)-- ./target @@ - target binary; @@ is replaced by AFL with
the input filename@@: -- ./target (AFL pipes
the input)| Flag | Effect |
|---|---|
-M name | Master process in parallel campaign |
-S name | Secondary process (slave) |
-x dict.txt | Use dictionary |
-t N | Per-input timeout in ms (default 1000) |
-m N | Memory limit per child (MB) |
-Q | QEMU mode for non-instrumented binaries |
-c path | CMPLog binary (companion mode for collisions) |
-d | "Quick" mode - deterministic mutations skipped |
# Terminal 1 - master
afl-fuzz -i seeds/ -o output/ -M main -- ./target @@
# Terminals 2..N - slaves
afl-fuzz -i seeds/ -o output/ -S slave1 -- ./target @@
afl-fuzz -i seeds/ -o output/ -S slave2 -- ./target @@The master runs deterministic mutations; slaves run random
mutations. Output corpus is shared via the output/ directory.
For closed-source binaries:
afl-fuzz -Q -i seeds/ -o output/ -- ./closed_source_target @@QEMU instruments via dynamic binary translation. ~5x slower than native instrumentation but works on any binary.
Output directory structure:
output/
fuzzer_stats # current run metrics
fuzzer_setup # configuration
plot_data # gnuplot-friendly time series
queue/
id:000000,orig:seed1.bin
id:000001,src:000000,op:... # mutated from id 0 with op
...
crashes/
id:000000,sig:11,src:000123,op:havoc,rep:8
README.txt
hangs/
id:000000,...Crash filename encoding (per AFL++ docs):
id:N - sequence numbersig:S - signal (11=SEGV, 6=SIGABRT, 9=SIGKILL, etc.)src:M - derived from queue entry Mop:X - mutation operator that produced itrep:R - repetition countReproduce a crash:
./target output/default/crashes/id:000000,sig:11,...
# Or for stdin-driven:
./target < output/default/crashes/id:000000,sig:11,...For automated bug filing, parse the sanitiser output (if compiled
with AFL_USE_ASAN=1) via bug-report-from-failure
(in the qa-defect-management plugin).
afl-tmin minimises a single crash input:
afl-tmin -i output/default/crashes/id:000000,sig:11,... \
-o minimised.bin \
-- ./target @@afl-cmin minimises the entire queue (corpus minimisation):
afl-cmin -i output/default/queue/ -o minimised_queue/ -- ./target @@- name: Install AFL++
run: docker pull aflplusplus/aflplusplus
- name: Build instrumented target
run: |
docker run -v $PWD:/src aflplusplus/aflplusplus bash -c \
"cd /src && CC=afl-clang-fast AFL_USE_ASAN=1 make"
- name: Smoke fuzz (5 min)
run: |
timeout 300 docker run -v $PWD:/src aflplusplus/aflplusplus \
afl-fuzz -i /src/seeds -o /src/output -- /src/target @@ || true
- name: Upload crashes
uses: actions/upload-artifact@v4
with:
name: afl-crashes
path: output/default/crashes/| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Target compiled without AFL instrumentation | No coverage signal; fuzzer is blind | Use afl-clang-fast or -Q (QEMU) |
-i - (continuation) without checking output state | Hard to resume; lost queue progress | Use canonical -i seeds/ on fresh runs |
| Single AFL slave on a multi-core machine | 80% of cores idle | Run N-1 slaves in parallel |
No AFL_USE_ASAN=1 | Only catches crashes, not memory bugs | Always set sanitiser env vars |
Treating hangs/ as bugs | Often false positives (slow targets, infinite loops in test data) | Investigate; raise -t for slow targets |
Long-running campaign without afl-cmin cycle | Queue bloats; cycle time degrades | Run afl-cmin weekly |
| Mixing AFL++ + libFuzzer corpora | Format incompatibility | Convert via afl-fuzz -i corpus -E ... round-trip |
LLVMFuzzerTestOneInput-like
persistent harnesses approach libFuzzer speed but require
source instrumentation.hangs/ reports many false positives. Slow but valid
inputs sit there until manually triaged.sanitiser-integration-reference,
corpus-management-reference.libfuzzer-cpp,
cargo-fuzz-rust,
atheris-python-fuzzing,
jazzer-jvm-fuzzing,
go-native-fuzzing,
ossfuzz-integration.fuzz-tool-selector.