Documentation Index Fetch the complete documentation index at: https://mintlify.com/AFLplusplus/AFLplusplus/llms.txt
Use this file to discover all available pages before exploring further.
Parallel fuzzing multiplies your fuzzing effectiveness by running multiple AFL++ instances simultaneously. This guide shows how to maximize coverage and bug discovery using all available CPU cores.
Why Parallel Fuzzing?
Running a single afl-fuzz instance is like searching for bugs with one flashlight. Parallel fuzzing gives you dozens of flashlights, each exploring different strategies:
Increased throughput : More executions per second
Strategy diversity : Different mutation approaches find different bugs
Path exploration : Multiple instances explore different code paths
Sanitizer coverage : Run sanitized targets without slowing down the main campaign
If you’re not using parallel fuzzing, you’re leaving 90% of potential bug discoveries on the table.
Architecture
AFL++ parallel fuzzing uses a main/secondary architecture:
Main instance (-M): Coordinates the campaign, performs final sync
Secondary instances (-S): Run independently with various strategies
All instances share the same output directory and automatically sync discoveries.
output/
├── main-hostname/ # Main fuzzer
│ ├── queue/
│ ├── crashes/
│ └── fuzzer_stats
├── secondary1/ # Secondary fuzzers
├── secondary2/
├── secondary3/
└── ...
Basic Setup
Main Instance
Start one main instance per machine:
export AFL_FINAL_SYNC = 1
afl-fuzz -M main- $HOSTNAME -i seeds -o output -- ./target @@
Main fuzzer name. Use main-$HOSTNAME for multi-machine setups.
Perform final test case import when terminating. Required for main instances .
Secondary Instances
Start secondary instances with unique names:
Secondary 1
Secondary 2
Secondary 3
afl-fuzz -S secondary1 -i seeds -o output -- ./target @@
Secondary fuzzer name. Must be unique across all instances.
Optimal Core Allocation
AFL++ has diminishing returns beyond 32-64 cores per machine due to synchronization overhead.
Sweet spot : 32-64 cores per machine for most targets.
Recommended Distribution
For a 32-core machine , distribute instances as follows:
1 Main Instance
export AFL_FINAL_SYNC = 1
afl-fuzz -M main- $HOSTNAME -i seeds -o output -- ./target @@
1-2 CMPLOG Instances
# At least one with -l 2AT
afl-fuzz -S cmplog1 -i seeds -o output -c target.cmplog -l 2AT -- ./target @@
afl-fuzz -S cmplog2 -i seeds -o output -c target.cmplog -l 2 -- ./target @@
1 Sanitizer Instance
afl-fuzz -S asan1 -i seeds -o output -- ./target.asan @@
1-3 LAF-Intel/COMPCOV Instances
afl-fuzz -S laf1 -i seeds -o output -- ./target.laf @@
If running multiple LAF instances, the main (-M) should be one of them for proper syncing.
~3 MOpt Instances (10%)
afl-fuzz -S mopt1 -L 0 -i seeds -o output -- ./target @@
afl-fuzz -S mopt2 -L 0 -i seeds -o output -- ./target @@
afl-fuzz -S mopt3 -L 0 -i seeds -o output -- ./target @@
Remaining: Standard Instances
Distribute different strategies:
50-70% : with AFL_DISABLE_TRIM=1
10% : old queue cycling (-Z)
40% : -P explore
20% : -P exploit
Different power schedules : fast, coe, lin, quad, rare
30% : with -a if not using by default
export AFL_DISABLE_TRIM = 1
afl-fuzz -S secondary1 -i seeds -o output -p fast -- ./target @@
afl-fuzz -S secondary2 -i seeds -o output -p coe -- ./target @@
afl-fuzz -S secondary3 -i seeds -o output -p lin -- ./target @@
afl-fuzz -S secondary4 -i seeds -o output -p quad -- ./target @@
afl-fuzz -S secondary5 -i seeds -o output -p exploit -P exploit -- ./target @@
afl-fuzz -S secondary6 -i seeds -o output -p rare -- ./target @@
afl-fuzz -S secondary7 -Z -i seeds -o output -- ./target @@
# ... etc
Configuration Best Practices
Cache Test Cases
With sufficient RAM, cache test cases for dramatic speed improvements:
export AFL_TESTCACHE_SIZE = 200 # 200MB cache
Recommended: 50-500MB depending on corpus size and RAM availability.
Use Ramdisk
Reduce SSD wear and improve speed:
export AFL_TMPDIR = / dev / shm / afl
mkdir -p $AFL_TMPDIR
Enable Import First
Load discoveries from other instances before starting:
export AFL_IMPORT_FIRST = 1
This can slow initial startup with many instances/seeds. Skip for CI or fresh campaigns.
Large/Previous Corpus
For resuming or CI with large corpus:
export AFL_CMPLOG_ONLY_NEW = 1 # Only CMPLOG new findings
export AFL_FAST_CAL = 1 # Fast calibration
For CI with huge queues:
export AFL_NO_STARTUP_CALIBRATION = 1 # Skip calibration entirely
Only use AFL_NO_STARTUP_CALIBRATION if calibration phase would be too long for your fuzz run time.
Ignore Problematic Seeds
Skip crashing/timeout seeds on startup:
export AFL_IGNORE_SEED_PROBLEMS = 1
Strategy Distribution
Diversify strategies across instances for maximum effectiveness:
Power Schedules
Distribute all schedules:
Schedule % of Instances Purpose explore30% Default, balanced fast15% Quick iterations coe10% Recent discoveries lin10% Linear favoritism quad10% Quadratic favoritism exploit15% Deep exploitation rare10% Edge cases
afl-fuzz -S exp1 -i seeds -o output -p explore -- ./target @@
afl-fuzz -S fast1 -i seeds -o output -p fast -- ./target @@
afl-fuzz -S coe1 -i seeds -o output -p coe -- ./target @@
# ... etc
Trimming
50-70% of instances should skip trimming:
export AFL_DISABLE_TRIM = 1
afl-fuzz -S notrim1 -i seeds -o output -- ./target @@
Queue Cycling
~10% should use old queue cycling:
afl-fuzz -S oldq1 -Z -i seeds -o output -- ./target @@
If not using -a by default:
30% with -a ascii
30% with -a binary
40% without -a
If using -a:
70% with -a
30% without -a
Example: Complete 32-Core Setup
View complete script for 32 cores
#!/bin/bash
# Configuration
TARGET = "./target"
TARGET_ASAN = "./target.asan"
TARGET_CMPLOG = "./target.cmplog"
TARGET_LAF = "./target.laf"
SEEDS = "seeds"
OUTPUT = "output"
# Environment
export AFL_TESTCACHE_SIZE = 200
export AFL_TMPDIR = / dev / shm / afl
export AFL_IMPORT_FIRST = 1
export AFL_SKIP_CPUFREQ = 1
mkdir -p $AFL_TMPDIR
# Main instance
AFL_FINAL_SYNC = 1 afl-fuzz -M main- $( hostname ) -i $SEEDS -o $OUTPUT -- $TARGET @@ &
# CMPLOG instances (2)
afl-fuzz -S cmplog1 -i $SEEDS -o $OUTPUT -c $TARGET_CMPLOG -l 2AT -- $TARGET @@ &
afl-fuzz -S cmplog2 -i $SEEDS -o $OUTPUT -c $TARGET_CMPLOG -l 2 -- $TARGET @@ &
# Sanitizer instance (1)
afl-fuzz -S asan1 -i $SEEDS -o $OUTPUT -- $TARGET_ASAN @@ &
# LAF-Intel instance (1)
afl-fuzz -S laf1 -i $SEEDS -o $OUTPUT -- $TARGET_LAF @@ &
# MOpt instances (3 = ~10%)
afl-fuzz -S mopt1 -L 0 -i $SEEDS -o $OUTPUT -- $TARGET @@ &
afl-fuzz -S mopt2 -L 0 -i $SEEDS -o $OUTPUT -- $TARGET @@ &
afl-fuzz -S mopt3 -L 0 -i $SEEDS -o $OUTPUT -- $TARGET @@ &
# Standard instances with various strategies (24 remaining)
# With AFL_DISABLE_TRIM (~16 instances)
export AFL_DISABLE_TRIM = 1
afl-fuzz -S std1 -i $SEEDS -o $OUTPUT -p fast -- $TARGET @@ &
afl-fuzz -S std2 -i $SEEDS -o $OUTPUT -p coe -- $TARGET @@ &
afl-fuzz -S std3 -i $SEEDS -o $OUTPUT -p lin -- $TARGET @@ &
afl-fuzz -S std4 -i $SEEDS -o $OUTPUT -p quad -- $TARGET @@ &
afl-fuzz -S std5 -i $SEEDS -o $OUTPUT -p exploit -P exploit -- $TARGET @@ &
afl-fuzz -S std6 -i $SEEDS -o $OUTPUT -p rare -- $TARGET @@ &
afl-fuzz -S std7 -i $SEEDS -o $OUTPUT -p explore -- $TARGET @@ &
afl-fuzz -S std8 -i $SEEDS -o $OUTPUT -p fast -- $TARGET @@ &
afl-fuzz -S std9 -i $SEEDS -o $OUTPUT -p coe -P explore -- $TARGET @@ &
afl-fuzz -S std10 -i $SEEDS -o $OUTPUT -p lin -- $TARGET @@ &
afl-fuzz -S std11 -i $SEEDS -o $OUTPUT -p quad -P exploit -- $TARGET @@ &
afl-fuzz -S std12 -i $SEEDS -o $OUTPUT -p exploit -- $TARGET @@ &
afl-fuzz -S std13 -i $SEEDS -o $OUTPUT -p rare -- $TARGET @@ &
afl-fuzz -S std14 -i $SEEDS -o $OUTPUT -p explore -- $TARGET @@ &
afl-fuzz -S std15 -i $SEEDS -o $OUTPUT -p fast -P explore -- $TARGET @@ &
afl-fuzz -S std16 -i $SEEDS -o $OUTPUT -p coe -- $TARGET @@ &
unset AFL_DISABLE_TRIM
# Without AFL_DISABLE_TRIM (~5 instances)
afl-fuzz -S trim1 -i $SEEDS -o $OUTPUT -p lin -- $TARGET @@ &
afl-fuzz -S trim2 -i $SEEDS -o $OUTPUT -p quad -- $TARGET @@ &
afl-fuzz -S trim3 -i $SEEDS -o $OUTPUT -p exploit -P exploit -- $TARGET @@ &
afl-fuzz -S trim4 -i $SEEDS -o $OUTPUT -p rare -- $TARGET @@ &
afl-fuzz -S trim5 -i $SEEDS -o $OUTPUT -p explore -- $TARGET @@ &
# Old queue cycling (~3 instances)
afl-fuzz -S oldq1 -Z -i $SEEDS -o $OUTPUT -- $TARGET @@ &
afl-fuzz -S oldq2 -Z -i $SEEDS -o $OUTPUT -p fast -- $TARGET @@ &
afl-fuzz -S oldq3 -Z -i $SEEDS -o $OUTPUT -p exploit -- $TARGET @@ &
echo "All 32 instances started!"
echo "Monitor with: afl-whatsup -s $OUTPUT "
Multi-Machine Fuzzing
Scale beyond a single machine by syncing between servers.
Setup
Run fuzzing on each machine
Ensure each machine has one unique -M main instance: # Machine 1
AFL_FINAL_SYNC = 1 afl-fuzz -M main-server1 -i seeds -o output -- ./target @@
# Machine 2
AFL_FINAL_SYNC = 1 afl-fuzz -M main-server2 -i seeds -o output -- ./target @@
Sync between machines
Sync only main instances (they handle secondary sync): for FROM in $( cat servers.txt ); do
for TO in $( cat servers.txt ); do
[[ " $FROM " == " $TO " ]] && continue
rsync -rlpogtz --rsh=ssh \
$FROM :/target/output/main- $FROM \
$TO :/target/output/
done
done
Sync Strategies
Each machine explores independently. May find different bugs but slower overall coverage. # Don't run sync script at all
Regular sync (every ~4 hours)
All campaigns see the same discoveries. Like one huge server. # Cron job every 4 hours
0 * /4 * * * /path/to/sync_script.sh
Interval sync (1/10th of campaign)
Balance individuality with knowledge sharing. # For 10-day campaign: sync every 24 hours
# For 24-hour campaign: sync every 2-3 hours
Advanced Sync Script
Use utils/distributed_fuzzing for more complex setups.
Monitoring
afl-whatsup
View campaign status:
Summary only:
afl-plot
Generate performance graphs:
afl-plot output/main-hostname /var/www/html/graphs
Watch Script
Continuously monitor:
watch -n 60 'afl-whatsup -s output/'
Stopping and Resuming
Stop All Instances
Use SIGINT (Ctrl+C / kill -INT) for clean shutdown, not SIGKILL.
Resume Campaign
Use -i - or set AFL_AUTORESUME:
export AFL_AUTORESUME = 1
# Rerun the same commands as before
afl-fuzz -M main- $( hostname ) -i seeds -o output -- ./target @@
afl-fuzz -S secondary1 -i seeds -o output -- ./target @@
# ...
Add New Seeds
Run temporary instance with new seeds:
AFL_BENCH_JUST_ONE = 1 AFL_FAST_CAL = 1 afl-fuzz \
-i new_seeds -o output -S temp_import -- ./target @@
The main instance will pick them up and distribute to secondaries.
Mixing with Other Fuzzers
AFL-Compatible Fuzzers
Use the same -o directory with unique -S names:
# AFL++ instance
afl-fuzz -M main -i seeds -o output -- ./target @@
# AFLsmart instance
aflsmart -S aflsmart1 -i seeds -o output -- ./target @@
Non-AFL Fuzzers (honggfuzz, libfuzzer)
Sync using -F flag on main instance:
# honggfuzz (separate directory)
honggfuzz -n 2 -i seeds -W honggfuzz_output -- ./target ___FILE___
# AFL++ main with sync
afl-fuzz -M main -F honggfuzz_output -i seeds -o output -- ./target @@
Highly recommended : Run honggfuzz (-n 1 or -n 2) and libfuzzer (-entropic=1) in parallel!
System Configuration
Run before first fuzzing session:
For permanent boot optimizations:
sudo afl-persistent-config
These improve fuzzing performance but reduce security. Use on dedicated fuzzing machines only.
Docker Considerations
Pass CPU cores explicitly:
docker run --cpuset-cpus= "0-31" aflplusplus/aflplusplus
Or disable affinity:
CPU Binding
AFL++ automatically binds to free cores. Override:
Disable CPU binding (allows more instances than cores).
Try binding but don’t fail if unavailable.
Troubleshooting
Check:
All use same -o directory
Unique -M / -S names
AFL_NO_SYNC is not set
File permissions allow reading other instances’ queues
Slow performance with many instances
Reduce AFL_TESTCACHE_SIZE
Run fewer sanitizer instances (they use most RAM)
Set memory limits with -m
afl-fuzz -m 512 -S asan1 -i seeds -o output -- ./target.asan @@
Docker or virtualization blocking affinity:
CI Fuzzing Notes
For short CI runs, parallel fuzzing differs:
Do NOT use -M mode in CI. Run only -S secondaries.
# CI: Use only secondaries
afl-fuzz -S ci1 -i seeds -o output -- ./target @@
afl-fuzz -S ci2 -i seeds -o output -p fast -- ./target @@
CI-specific settings:
export AFL_FAST_CAL = 1
export AFL_CMPLOG_ONLY_NEW = 1
export AFL_NO_STARTUP_CALIBRATION = 1 # If queue is huge
Power Schedules Optimize fuzzing strategies
Environment Variables Fine-tune AFL++ behavior
Custom Mutators Implement domain-specific mutations
Getting Started Basic fuzzing guide