AFL++ (American Fuzzy Lop plus plus) is a coverage-guided greybox fuzzer designed to automatically discover security vulnerabilities and bugs in software. This document provides a high-level introduction to AFL++'s architecture, core components, and fuzzing workflow. It serves as an entry point for understanding how AFL++ instruments target programs, executes them, tracks code coverage, and mutates inputs to maximize code exploration.
For detailed information on specific subsystems, see:
AFL++ is a superior fork of Google's AFL (American Fuzzy Lop), originally written by Michal Zalewski. It provides:
The current development version is 4.35a, with stable release 4.34c. The project is maintained by Marc Heuse, Dominik Maier, Andrea Fioraldi, and Heiko Eissfeldt.
Sources: README.md1-70 docs/Changelog.md1-30
The following diagram shows AFL++'s major subsystems and how they interact:
This architecture separates concerns into distinct layers:
Sources: src/afl-fuzz.c1-30 src/afl-cc.c1-30 instrumentation/afl-compiler-rt.o.c1-100
The afl-fuzz binary is the primary fuzzing engine. Its responsibilities include:
Key data structures in afl_state_t include/afl-fuzz.h503-869:
queue_buf[]: Array of test case entries (struct queue_entry)virgin_bits: Tracks unexplored edgesfsrv: Forkserver state (afl_forkserver_t)shm: Shared memory for coverage mapMain entry point: src/afl-fuzz.c555-1700
Sources: src/afl-fuzz.c1-100 include/afl-fuzz.h503-869
The afl-cc compiler wrapper intercepts compilation and injects instrumentation. It supports multiple modes:
| Mode | Description | Implementation |
|---|---|---|
| LLVM | Default LLVM-based instrumentation | instrumentation/afl-llvm-pass.so.cc |
| LTO | Link-time optimization mode | instrumentation/SanitizerCoverageLTO.so.cc |
| GCC_PLUGIN | GCC plugin instrumentation | instrumentation/afl-gcc-pass.cc |
| QEMU | Binary-only via QEMU | qemu_mode/ |
| FRIDA | Binary-only via Frida | frida_mode/ |
Mode selection logic: src/afl-cc.c653-763
The wrapper adds compiler flags, links runtime libraries, and configures instrumentation passes based on environment variables and command-line options.
Sources: src/afl-cc.c1-200 src/afl-cc.c653-763
The runtime library instrumentation/afl-compiler-rt.o.c provides:
__afl_start_forkserver() sets up IPC with fuzzer__afl_area_ptr buffer for edge hit counts__afl_map_shm() attaches to fuzzer's shared memoryKey globals instrumentation/afl-compiler-rt.o.c156-192:
The forkserver protocol allows AFL++ to fork the target process repeatedly without re-executing initialization code, dramatically improving performance.
Sources: instrumentation/afl-compiler-rt.o.c82-460
The forkserver listens on file descriptors FORKSRV_FD (198) and FORKSRV_FD+1 (199). This design eliminates the overhead of repeatedly calling execve().
Sources: instrumentation/afl-compiler-rt.o.c1200-1500 src/afl-fuzz-run.c45-178
-i directory src/afl-fuzz-init.c498-800Sources: src/afl-fuzz-one.c1-300 src/afl-fuzz-queue.c46-496 src/afl-fuzz-bitmap.c220-400
AFL++ uses an edge-based coverage model. Each basic block transition (edge) is assigned a unique ID during instrumentation:
The XOR creates a unique value for each edge pair. The coverage bitmap (__afl_area_ptr) is a byte array where:
Hit count classification src/afl-fuzz-bitmap.c32-80 distinguishes between "edge taken once" vs "edge taken many times", enabling path explosion detection.
The virgin_bits array include/afl-fuzz.h620-622 tracks which edges have never been seen, guiding input prioritization.
Sources: instrumentation/afl-compiler-rt.o.c322-346 src/afl-fuzz-bitmap.c1-150
| Term | Definition | Code Reference |
|---|---|---|
| Corpus | Collection of test cases (queue) | afl->queue_buf[] include/afl-fuzz.h709 |
| Edge | Basic block transition | Coverage map index |
| Bitmap | Coverage map tracking edge hits | __afl_area_ptr instrumentation/afl-compiler-rt.o.c168 |
| Calibration | Running seed multiple times to measure stability | src/afl-fuzz-run.c800-1100 |
| Deterministic stage | Predictable mutations (bitflips, arithmetic) | src/afl-fuzz-one.c2500-4000 |
| Havoc | Random stacked mutations | src/afl-fuzz-one.c4000-5000 |
| Favored | High-priority queue entries | q->favored include/afl-fuzz.h223 |
| Virgin bits | Edges not yet covered | afl->virgin_bits include/afl-fuzz.h620 |
| Power schedule | Algorithm for compute time allocation | afl->schedule include/afl-fuzz.h586 |
| Persistent mode | In-process fuzzing without fork | AFL_PERSISTENT_ADDR |
Sources: include/afl-fuzz.h209-262 include/afl-fuzz.h503-869
AFL++ is highly configurable via environment variables. Key categories:
Instrumentation control docs/env_variables.md13-200:
AFL_CC_COMPILER: Select instrumentation mode (LLVM/LTO/GCC_PLUGIN)AFL_LLVM_INSTRUMENT: Fine-tune instrumentation (PCGUARD/CLASSIC/CFG)AFL_MAP_SIZE: Override coverage map sizeFuzzing behavior docs/env_variables.md200-400:
AFL_CUSTOM_MUTATOR_LIBRARY: Load custom mutator pluginAFL_CMPLOG_ONLY_NEW: Run CmpLog only on new seedsAFL_DISABLE_TRIM: Skip input minimizationAFL_FAST_CAL: Faster calibration (fewer runs)Performance docs/env_variables.md400-600:
AFL_TMPDIR: Use RAM disk for temporary filesAFL_TESTCACHE_SIZE: Cache testcases in memoryAFL_NO_AFFINITY: Disable CPU pinningComplete reference: Environment Variables Reference
Sources: docs/env_variables.md1-100 include/envs.h18-150
Compile target with afl-cc:
Prepare seeds: Create directory with sample valid inputs
Run fuzzer:
Monitor progress: Check output/default/fuzzer_stats and UI
Investigate findings: Crashes in output/default/crashes/
For detailed instructions, see Fuzzing in Depth
Entry point: src/afl-fuzz.c555 - main() function
Sources: README.md70-128 src/afl-fuzz.c555-1700
AFL++ supports multiple instrumentation approaches, each with trade-offs:
For compilation details, see Compilation and Instrumentation For binary-only modes, see Binary-only Fuzzing
Sources: src/afl-cc.c66-103 Diagram 2 from provided materials
IJON_MAX() macrosFor detailed coverage, see Advanced Features
Sources: src/afl-fuzz-redqueen.c1-100 src/afl-fuzz-ijon.c1-50 src/afl-fuzz-mutators.c1-50
| Component | Purpose | Key Files | Key Data Structures |
|---|---|---|---|
| afl-fuzz | Main fuzzing engine | src/afl-fuzz.c | afl_state_t include/afl-fuzz.h503 |
| afl-cc | Compiler wrapper | src/afl-cc.c | aflcc_state_t src/afl-cc.c163 |
| compiler-rt | Runtime library | instrumentation/afl-compiler-rt.o.c | __afl_area_ptr, __afl_fuzz_ptr |
| Forkserver | Process management | src/afl-forkserver.c | afl_forkserver_t include/forkserver.h |
| Queue | Input corpus | src/afl-fuzz-queue.c | struct queue_entry include/afl-fuzz.h209 |
| Mutations | Input transformations | src/afl-fuzz-one.c | Mutation stage enums include/afl-fuzz.h282 |
| Bitmap | Coverage tracking | src/afl-fuzz-bitmap.c | virgin_bits, count_class_lookup |
| CmpLog | Comparison solving | src/afl-fuzz-redqueen.c | struct cmp_map include/cmplog.h |
| QEMU Mode | Binary fuzzing | qemu_mode/ | Modified QEMU translator |
| Frida Mode | Binary fuzzing | frida_mode/ | Stalker instrumentation |
Sources: src/afl-fuzz.c1-30 src/afl-cc.c1-30 instrumentation/afl-compiler-rt.o.c1-100
AFLplusplus/
├── src/ # Main fuzzer source code
│ ├── afl-fuzz.c # Main fuzzing engine
│ ├── afl-fuzz-one.c # Mutation logic
│ ├── afl-fuzz-run.c # Execution and forkserver
│ ├── afl-fuzz-queue.c # Corpus management
│ ├── afl-fuzz-bitmap.c # Coverage analysis
│ ├── afl-fuzz-redqueen.c # CmpLog solving
│ └── afl-cc.c # Compiler wrapper
├── instrumentation/ # Instrumentation implementations
│ ├── afl-compiler-rt.o.c # Runtime library
│ ├── afl-llvm-pass.so.cc # LLVM instrumentation
│ └── afl-gcc-pass.cc # GCC plugin
├── qemu_mode/ # QEMU binary-only mode
├── frida_mode/ # Frida binary-only mode
├── unicorn_mode/ # Unicorn emulation mode
├── include/ # Header files
│ ├── afl-fuzz.h # Main fuzzer definitions
│ ├── config.h # Configuration constants
│ └── types.h # Type definitions
├── docs/ # Documentation
└── test/ # Test suite
Sources: Directory structure from repository context
Refresh this wiki