Mutation Overview
Mutation is the process of modifying existing inputs to create new test cases. AFL++ uses a sophisticated combination of deterministic and random mutation strategies, guided by coverage feedback, to efficiently explore program behavior. Unlike purely random fuzzing, AFL++‘s mutations are:- Feedback-driven: Mutations that discover new paths are prioritized
- Balanced: Combines proven fuzzing strategies in optimal proportions
- Adaptive: Adjusts mutation intensity based on input characteristics
- Research-backed: Incorporates techniques from academic fuzzing research
AFL++‘s mutation strategies are the result of extensive research and testing. The “balanced and well-researched variety” of mutations is key to its effectiveness.
Mutation Stages
When AFL++ processes a test case from the queue, it goes through several mutation stages:Stage Progression
- Calibration - Analyze execution path and establish baseline
- Trimming - Minimize input size while preserving behavior
- Deterministic stages - Systematic bit/byte modifications (if enabled)
- Havoc - Stacked random mutations
- Splicing - Combine two inputs (after first full queue cycle)
Deterministic Stages
Deterministic mutations systematically walk through the input, applying predictable transformations. These stages are disabled by default in modern AFL++ because they’re not as efficient as random mutations for most targets.Deterministic fuzzing is disabled by default. Enable with
-D flag if needed, but expect slower overall progress in most cases.Bitflip Operations
Flip bits in the input with varying lengths and step sizes:- bitflip 1/1: Flip each bit individually (walk with 1-bit increment)
- bitflip 2/1: Flip 2 consecutive bits at a time
- bitflip 4/1: Flip 4 consecutive bits at a time
- bitflip 8/8: Flip entire bytes (walk with 8-bit increment)
- bitflip 16/8: Flip 2-byte values (walk with 8-bit increment)
- bitflip 32/8: Flip 4-byte values (walk with 8-bit increment)
Arithmetic Operations
Add or subtract small integers to 8-bit, 16-bit, and 32-bit values:- arith 8/8: Modify bytes by adding/subtracting 1-35
- arith 16/8: Modify 16-bit values (both endianness)
- arith 32/8: Modify 32-bit values (both endianness)
Arithmetic mutations help bypass length checks, loop counters, and simple range validations.
Interesting Values
Overwrite values with known “interesting” constants that commonly trigger edge cases:- interest 8/8: Replace bytes with: 0, 1, 16, 32, 64, 100, 127, -1, -128, etc.
- interest 16/8: Replace 16-bit values with: 0, 128, 255, 256, 512, 1000, 1024, 4096, 32767, -32768, -129, -1, etc.
- interest 32/8: Replace 32-bit values with: 0, 1, 32768, 65535, 65536, 100000, 2147483647, -2147483648, etc.
Dictionary Operations
If a dictionary is provided (via-x or auto-generated), AFL++ tries injecting dictionary terms:
- extras (over): Overwrite portions of input with dictionary entries
- extras (insert): Insert dictionary entries, shifting remaining data
- User-supplied files (
-x dictionary.txt) - Auto-generated during compilation (LTO mode AUTODICTIONARY)
- Extracted at runtime (
AFL_LLVM_DICT2FILE)
Random Mutation Stages
Havoc Stage
The havoc stage is where AFL++ shines. It applies stacked random mutations in a single pass, creating highly diverse inputs: Havoc operations include:- Bit flips at random positions
- Byte flips at random positions
- Setting bytes/words/dwords to random or interesting values
- Adding/subtracting random values from bytes/words/dwords
- Deleting blocks of bytes
- Duplicating blocks (memcpy to another location)
- Overwriting blocks with random bytes
- Inserting blocks of random bytes
- Dictionary injection (if dictionary available)
Havoc is the most productive stage for most targets. It produces the majority of new paths discovered during fuzzing.
Splicing Stage
After the first complete queue cycle, AFL++ activates the splice stage, which:- Selects two random inputs from the queue
- Picks a random split point in each
- Combines the first half of one with the second half of the other
- Applies havoc mutations to the spliced result
- Combine successful strategies from different inputs
- Create novel input structures
- Escape local optima in the search space
Splicing activates only after completing at least one full queue cycle. Early in fuzzing, only havoc is used.
Advanced Mutation Modes
MOpt Mutator
The MOpt (mutation optimizer) uses a particle swarm optimization algorithm to learn which mutation operators are most effective for the current target:- Tracks success rates of different mutation operators
- Dynamically adjusts operator probabilities
- Optimizes mutation strategy during fuzzing
- Shows “py/custom/rq” stats when active
CMPLOG / Redqueen
CMPLOG is a specialized mutation technique that instruments comparison operations and uses the observed values to guide mutations:- Target is instrumented to log comparison operands
- AFL++ captures values used in
memcmp(),strcmp(), integer comparisons, etc. - These values are spliced into the input at various positions
- Helps bypass magic number checks, checksums, and complex comparisons
-l):
-l 2: Standard transformations-l 3: Aggressive transformations-l 2AT: Standard + ASCII/Unicode transformations
CMPLOG is extremely effective for targets with magic numbers or complex input validation. It’s often better than LAF-intel/COMPCOV for solving difficult comparisons.
Custom Mutators
AFL++ supports custom mutators - user-written libraries that implement format-specific mutation logic:- Implement format-aware mutations (XML, JSON, protobuf, etc.)
- Use grammar-based generation
- Apply domain-specific mutation rules
- Integrate external mutation libraries (Radamsa, honggfuzz)
Power Schedules
Power schedules determine how much fuzzing time each queue entry receives. Different schedules optimize for different goals.Available Schedules
Set with-p <schedule>:
explore (default):
- Balanced approach for general fuzzing
- Good starting point for most targets
- Prefers smaller inputs
- Maximizes execution throughput
- Good for slow targets
- Focuses on recent discoveries
- Deprioritizes old queue entries
- Good for CI fuzzing
- Equal time for all queue entries
- Fair distribution
- Heavily favors recent finds
- Aggressive exploration
- Focuses fuzzing on promising areas
- Less exploration, more depth
- Good when you have good coverage
- Prioritizes inputs that hit rare edges
- Helps discover deep paths
- Useful for complex targets
- AFLfast power schedules
- Research-backed algorithms
- Proven effective in academic studies
Favored Queue Entries
AFL++ marks certain queue entries as “favored” based on a minimization algorithm:- Favored entries hit unique edges with smallest file size
- Get more fuzzing attention (higher priority)
- Marked with ”*” in status screen when processing non-favored
- Total paths: All queue entries
- Favored paths: Subset that provides best coverage/size ratio
Trimming Strategy
Before mutation, AFL++ tries to trim (minimize) each input: Trimming process:- Remove blocks of bytes from the input
- Execute and check if the same path is taken
- If path unchanged, keep the shorter version
- Repeat with different block sizes and positions
- 20.31%: Percentage of bytes removed
- 9201: Execs used for trimming
- 17.05%: Bytes deemed to have no effect (excluded from expensive deterministic stages)
Trimming makes fuzzing faster by reducing input size. Smaller inputs = faster execution = more iterations per second.
Trim Control
You can disable trimming for some instances:Mutation Strategy Performance
The AFL++ status screen tracks success rates for each strategy:new_paths_found / total_execs
This shows:
- Havoc/splice generates most new paths (1,903 from 20M execs)
- Deterministic stages contribute but are less efficient
- Custom mutators (py/custom/rq) can be very effective
When deterministic mode is disabled (default), the first five lines show “disabled (default, enable with -D)”.
Mutation Best Practices
For Parallel Fuzzing
Diversify mutation strategies across instances:Recommended Distribution
- 10%: MOpt mutator (
-L 0) - 10%: Old queue cycling (
-Z) - 50-70%: Trimming disabled (
AFL_DISABLE_TRIM=1) - 1-2 instances: CMPLOG enabled with
-l 2AT - 1-2 instances: LAF-intel/COMPCOV compiled targets
- Varied power schedules: Mix explore, fast, coe, exploit, rare
Dictionary Usage
Provide dictionaries when possible:Understanding Mutation Yield
Monitor which strategies are effective:- High yield from havoc/splice: Normal, expected behavior
- Good yield from deterministic stages: Target benefits from systematic mutations
- High yield from CMPLOG (rq): Target has many comparison-based checks
- Low overall yield: May need better seeds or target is saturated
Improving Mutation Effectiveness
If finding few new paths:- Try CMPLOG mode (
-cwith CMPLOG binary) - Add format-specific dictionary (
-x) - Enable LAF-intel/COMPCOV at compile time
- Use custom mutator for the specific format
- Check that input seeds are diverse
- Good! You’re finding bugs efficiently
- Consider adding sanitizers to find more bug types
- Continue fuzzing - quality over quantity
Next Steps
- Learn about power schedules in detail
- Explore custom mutators
- Read about parallel fuzzing strategies
- Understand coverage tracking that guides mutations

