Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/AdithyaaSivamal/Agentic-AFL/llms.txt

Use this file to discover all available pages before exploring further.

Once Agentic-AFL detects a math wall bypass — the harness prints the accept marker and a queue entry is confirmed — it can automatically restart AFL++ with a custom Python mutator that has been tailored to the now-unlocked code region. This is called Level 3 deployment. The idea is that standard AFL++ mutation strategies (bit flips, byte substitutions, havoc) are efficient for exploring shallow code, but after a protocol validation wall is bypassed, the post-validation state machine benefits from domain-aware mutation that understands the protocol’s structure — field boundaries, enum values, length fields, and checksum positions.

How Custom Mutator Deployment Works

Level 3 deployment is triggered automatically by CampaignRunner the moment bypass_detected transitions to True:
1

Bypass confirmed

CampaignRunner re-runs queue entries against the harness. When a queue entry’s combined stdout and stderr contains the accept marker, bypass_detected is set to True and bypass_evidence is set to the queue entry filename.
2

AFL++ terminated gracefully

The running AFL++ process is sent SIGTERM and given 5 seconds to exit. If it does not exit within that window, it is sent SIGKILL. The AFL++ output directory and sync directory are preserved — the queue and corpus remain intact.
3

AFL++ restarted with the custom mutator

AFL++ is relaunched with - as the input directory (resume mode, reading from the existing output directory). AFL_PYTHON_MODULE is set to the mutator file’s stem (e.g., dnp3_mutator for ./mutators/dnp3_mutator.py) and PYTHONPATH is set to the mutator’s parent directory (e.g., ./mutators). All other AFL++ environment variables are preserved from the original launch.
4

Campaign continues

The campaign continues for the remaining --duration with the custom mutator active. The mutator_deployed field in the result JSON is set to true.

Specifying a Custom Mutator

Pass the path to your mutator Python file with --custom-mutator:
agentic-afl fuzz ./harness -i ./seeds \
  --duration 6h \
  --custom-mutator ./mutators/dnp3_mutator.py
The mutator file must exist at campaign start. If the path does not exist, CampaignRunner will log a warning and skip Level 3 deployment when a bypass occurs. Verify the path before starting a long campaign.

Writing a Custom Mutator

AFL++ custom mutators in Python must export an init function and a fuzz function. The describe function is optional but recommended — AFL++ uses it to label the mutator in its status output:
dnp3_mutator.py
import random

def init(seed):
    random.seed(seed)

def fuzz(buf, add_buf, max_size):
    """Mutate buf. Return the mutated bytes."""
    buf = bytearray(buf)
    # Example: flip the DNP3 function code byte
    if len(buf) > 3:
        buf[3] = random.randint(0, 0xFF)
    return bytes(buf)

def describe(max_description_len):
    return "dnp3_mutator"
AFL++ calls fuzz with three arguments:
ArgumentTypeDescription
bufbytesThe current test case bytes to mutate.
add_bufbytesAn additional splicing input from the corpus (may be empty).
max_sizeintMaximum allowed output size in bytes.
The function must return bytes. If it returns None or raises an exception, AFL++ discards the mutation for that cycle.
Design your mutator around the protocol’s post-validation structure. Focus on fields that are only reachable after the math wall is bypassed — for example, command codes, register addresses, payload lengths, and timestamp fields. Mutating the header bytes that carry the checksum is less valuable here because the bypass has already been confirmed and AFL++ is now exploring the downstream handlers.

Custom Mutator Environment

Agentic-AFL sets up the Python environment automatically at Level 3 deployment. You do not need to install anything or set environment variables manually:
  • AFL_PYTHON_MODULE is set to the file stem of your mutator (e.g., dnp3_mutator for ./mutators/dnp3_mutator.py).
  • PYTHONPATH is set to the directory containing the mutator file (e.g., ./mutators).
  • All other AFL++ environment variables (AFL_NO_UI, AFL_SKIP_CPUFREQ, etc.) are preserved from the original AFL++ launch.
If your mutator imports third-party libraries, those libraries must be installed in the Python environment that AFL++ can find via the above PYTHONPATH. A virtualenv in the mutator’s directory works:
cd ./mutators
python -m venv .venv
source .venv/bin/activate
pip install crcmod
Then set PYTHONPATH=./mutators/.venv/lib/python3.x/site-packages:./mutators before starting the campaign, or include the activation in the mutator itself via sys.path.
The custom mutator is only deployed after bypass_detected becomes True — that is, after the accept marker is observed in an AFL++ queue entry. If no bypass occurs during the campaign, the mutator file is never used and AFL++ runs with its built-in mutation strategies for the entire duration.

Build docs developers (and LLMs) love