Skip to main content

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.

Custom mutators allow you to implement domain-specific mutations beyond AFL++‘s built-in strategies. This enables structure-aware fuzzing for complex formats like XML, protobuf, or grammar-based inputs.

Overview

Custom mutators are passed to afl-fuzz via environment variables and must export specific functions. AFL++ supports:
  • C/C++ libraries (.so files)
  • Python modules
  • Rust (experimental support in custom_mutators/rust)
The custom mutation stage runs as the first non-deterministic stage, right before havoc.

Multiple Mutators

You can chain multiple custom mutators together:
export AFL_CUSTOM_MUTATOR_LIBRARY="/path/to/mutator_first.so;/path/to/mutator_second.so"
Functions are called in the order specified.

API Reference

C/C++ API

If you need to increase the size of an out_buf buffer, you must use afl_realloc(). Include include/alloc-inl.h or afl-fuzz will crash when freeing buffers.
void *afl_custom_init(afl_state_t *afl, unsigned int seed);

Python API

def init(seed):
    """Called when AFL++ starts up. Seed RNG and set up state."""
    pass

Function Details

init (optional in Python)

Seeds RNG and sets up buffers and state when AFL++ starts.

queue_get (optional)

Determines whether AFL++ should fuzz the current queue entry with all mutators.

fuzz_count (optional)

Overrides AFL++‘s calculation of fuzzing attempts for a queue entry. Most useful when not using AFL_CUSTOM_MUTATOR_ONLY.

splice_optout (optional)

If this function exists (never called, just needs to be present), no splicing target is passed to fuzz(). Saves time if splicing data isn’t needed.

fuzz (optional)

Performs your custom mutations. The add_buf can be used for splicing or ignored (define splice_optout if ignoring). Returning length 0 skips this mutation result.
For non-Python: the returned output buffer is under your memory management!

describe (optional)

Describes the current test case generated by the last mutation. Called to name crash files, helping reproduce crashing mutations.

havoc_mutation & havoc_mutation_probability (optional)

havoc_mutation performs a single custom mutation stacked with others in havoc. havoc_mutation_probability returns the probability (default 6%) that it’s called.

post_process (optional)

Transforms mutated data into the format expected by the target. For example, converting protobuf format to plain text.
# libprotobuf-mutator scenario:
# Mutator returns protobuf format
# Target expects plain text
# post_process converts protobuf -> text

def post_process(buf):
    # Convert protobuf to plain text
    plaintext = protobuf_to_text(buf)
    
    # Skip if too short or corrupted
    if len(plaintext) < 10:
        return b""  # Return empty to skip
    
    return plaintext
Do NOT make random changes in post_process! This is for format conversion only.For C/C++: If possible, make changes in-place (*outbuf = data) for best performance.
You can return NULL/empty buffer and zero length to prevent sending to target (e.g., if too short/corrupted).

fuzz_send (optional)

Send data to the target yourself (e.g., via IPC). Replaces some usage of utils/afl_proxy but requires starting the target with afl-fuzz. Set AFL_CUSTOM_MUTATOR_LATE_SEND to call afl_custom_fuzz_send() after the target restarts (needed for TCP services). Example: custom_mutators/examples/custom_send.c

queue_new_entry (optional)

Called after adding a new test case to the queue. Return True if file contents were changed.

introspection (optional)

Called after discovering a new queue entry, crash, or timeout (if compiled with INTROSPECTION). Return a string reporting exact mutations used.

deinit (optional in Python)

Last method called, for cleanup.

Trimming Support

Generic AFL++ trimming can destroy complex format structures. Implement custom trimming to avoid broken test cases.
1

init_trim

Receives initial buffer, returns number of iteration steps possible.
  • Remove elements one-by-one: return n
  • Binary search: return log(n)
  • Unknown steps: return 1, then return 0 in post_trim until done
2

trim

Called for each trimming operation. No arguments (state from init_trim is in data variables). Returns trimmed input buffer.
3

post_trim

Informs if trimming step was successful. Reset to last known good state on failure. Return next trim iteration index (0 to max from init_trim).
Omitting any of the three trimming methods disables custom trimming and falls back to built-in trimming.If you have a custom post_process mutator, you must call it yourself at the end of successful trimming!

Environment Variables

AFL_CUSTOM_MUTATOR_ONLY
boolean
Disable all other mutation stages. Use only the custom mutator. Best combined with custom trimming to prevent broken test cases.
AFL_CUSTOM_MUTATOR_LIBRARY
string
Path to C/C++ shared library (.so). Supports multiple mutators separated by semicolons.
export AFL_CUSTOM_MUTATOR_LIBRARY="/path/to/first.so;/path/to/second.so"
AFL_PYTHON_MODULE
string
Python module name (not path). Set PYTHONPATH to the directory containing the module.
export PYTHONPATH=/path/to/directory
export AFL_PYTHON_MODULE=example
AFL_DEBUG
boolean
With AFL_NO_UI, emits additional messages about custom trimmer performance and actions.

Helper Environment Variables

For mutators in Python, Rust, GO, etc., AFL++ sets these variables:
AFL_CUSTOM_INFO_PROGRAM
string
The target program name. Contains the actual target even in QEMU mode (-Q).
AFL_CUSTOM_INFO_PROGRAM_INPUT
string
The input file path if -f parameter is used with afl-fuzz.
AFL_CUSTOM_INFO_PROGRAM_ARGV
string
Parameters given to the target program, still contains @@ identifier.
AFL_CUSTOM_INFO_OUT
string
Output directory for this fuzzer instance. E.g., if called with -o out -S foobar, this is out/foobar.
If AFL_CUSTOM_INFO_PROGRAM_INPUT is empty and AFL_CUSTOM_INFO_PROGRAM_ARGV doesn’t contain @@, the target gets input via stdin.

Compilation & Usage

C/C++ Custom Mutator

1

Compile as shared object

gcc -shared -Wall -O3 example.c -o example.so
2

Run afl-fuzz

export AFL_CUSTOM_MUTATOR_LIBRARY="/full/path/to/example.so"
afl-fuzz -i input -o output -- /path/to/program @@

Python Custom Mutator

1

Install Python development package

sudo apt install python3-dev
2

Compile AFL++ with Python support

The Makefile auto-detects Python3 via python-config/python3-config.For non-standard setups:
PYTHON_INCLUDE=/path/to/python/include LDFLAGS=-L/path/to/python/lib make
3

Run afl-fuzz

export PYTHONPATH=`dirname /full/path/to/example.py`
export AFL_PYTHON_MODULE=example
afl-fuzz -i input -o output -- /path/to/program @@

Examples

C Example

Complete C/C++ custom mutator example

Python Example

Complete Python custom mutator example

Custom Send

Example using fuzz_send for IPC

Rust Support

Experimental Rust custom mutators

Additional Resources