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.

A campaign is the top-level unit of work in Agentic-AFL. It starts AFL++, co-launches the AgentLoop as an async daemon, monitors edge coverage, and — when a stall is detected — triggers the full Extract → Profile → Retrieve → Generate → Solve → Inject pipeline. Campaigns are driven by the agentic-afl fuzz subcommand and produce a JSON result file that captures every metric from baseline edges to bypass evidence.

Campaign Lifecycle

Every campaign passes through three phases:
1

Warmup

AFL++ runs alone for the first few minutes with no agent intervention. This establishes the baseline edge count and lets AFL++ saturate the easy, shallow coverage that standard mutation handles well. The baseline is sampled in the first 10 seconds of the campaign.
2

Stall Detection

The AgentLoop’s StallDetector polls fuzzer_stats on a configurable interval. When the edge count stops growing for --stall-minutes consecutive minutes (default 5), the stall is queued for agent processing. Stalls are prioritized by severity — CRITICAL stalls are processed before MEDIUM ones.
3

Agent Intervention

The agent runs the full neuro-symbolic pipeline: Ghidra P-Code extraction → constraint profiling → CARM template retrieval → LLM-driven Z3 generation → sandbox execution → payload injection into AFL++‘s sync directory. If all ReAct turns are exhausted without a SAT result, the stall is deferred back to AFL++ for probabilistic mutation.

Basic Campaign Command

agentic-afl fuzz ./harness -i ./seeds --duration 1h
This starts a one-hour campaign against ./harness with seeds from ./seeds. Progress is printed to the terminal every 25 seconds in a columnar format showing elapsed time, edge count, total executions, stall count, and injection count.

Using the TUI Dashboard

Pass --tui to launch the Rich terminal UI, which gives a live view of both AFL++ and the agent simultaneously:
agentic-afl fuzz ./harness -i ./seeds --duration 6h --tui
The --tui flag requires the rich package (pip install rich). The campaign runs identically with or without it — TUI is a pure display layer with no effect on fuzzing behavior.
The TUI is divided into four panels:

Fuzzer State

Live metrics read directly from AFL++‘s fuzzer_stats: edge count, executions per second, corpus size, cycles done, and pending favorites. The baseline edge count is shown alongside the current count so you can see the net gain at a glance.

Agent State

The agent pipeline displayed as a stage ladder: detect → ghidra → profile → carm → probe → llm → z3 → inject → diverse → mutator. Each stage shows active (currently running) or done (completed this cycle), mapped in real time from the agent’s log output.

Coverage Chart

A braille-dot sparkline of the edge-count history, updated every 30 seconds. A rising line during the warmup phase that flattens and then jumps after agent injection is the ideal campaign shape.

Event Log

A scrolling log of agent events — stall detections, Z3 verdicts, payload injections, and bypass confirmations — with color-coded severity indicators.

Key Campaign Flags

FlagDefaultDescription
--duration1hCampaign duration. Accepts 6h, 30m, 90s, or integer seconds.
--stall-minutes5Minutes of edge plateau before the agent triggers. Lower values make the agent more aggressive; higher values give AFL++ more time to self-recover.
--accept-markerACCEPTThe stdout/stderr string Agentic-AFL watches for to confirm a math wall bypass. Must match what your harness prints on success.
--custom-mutator(none)Path to a Python AFL++ custom mutator deployed after bypass is detected. See Custom Mutator.
--log-dir(none)Directory where the JSON result file is written. The filename is <target>_<timestamp>.json.
--nameharness stemCampaign name used in result filenames and the TUI header. Defaults to the harness binary’s filename without extension.
--tuioffEnable the Rich terminal dashboard.
--debugoffEnable debug logging. Saves every raw LLM completion and Z3 script to /tmp/agentic_afl_debug/ for post-mortem analysis.
When --tui is not used, Agentic-AFL prints a columnar dashboard to stdout every 25 seconds with these columns:
  Time    Edges       Execs  Stalls  Inject  Status
  ────────────────────────────────────────────────────────────
  1m05s     312    1048576       0       0  fuzzing
  2m30s     489    2097152       1       0  stall×1
  4m15s    1847    4194304       1      38  injected×38
The Status column shows fuzzing at baseline, stall×N when stalls have been detected, and injected×N after payloads have been written to the AFL++ sync directory.

Campaign Result JSON

When --log-dir is specified, Agentic-AFL writes a JSON result file at campaign end. The file is named <target_name>_<timestamp>.json and is a serialization of the CampaignResult dataclass:
{
  "target_name": "harness",
  "baseline_edges": 312,
  "final_edges": 1847,
  "edge_gain": 1535,
  "edge_gain_pct": 492.0,
  "bypass_detected": true,
  "bypass_time_seconds": 1423,
  "bypass_evidence": "agentic_inject_20250101_120000.bin",
  "payloads_injected": 38,
  "stalls_detected": 3,
  "llm_calls": 9,
  "react_turns": 14,
  "elapsed_seconds": 3601,
  "duration_seconds": 3600,
  "timeline": [
    { "time_seconds": 60, "edges": 315, "stalls_detected": 0, "payloads_injected": 0 },
    { "time_seconds": 120, "edges": 318, "stalls_detected": 1, "payloads_injected": 0 }
  ],
  "mutator_deployed": true
}
FieldDescription
target_nameCampaign name (harness stem or --name value).
baseline_edgesEdge count sampled in the first 10 seconds.
final_edgesEdge count at campaign end.
edge_gainfinal_edges - baseline_edges.
edge_gain_pctPercentage gain relative to baseline.
bypass_detectedtrue if the accept marker was observed in a queue entry.
bypass_time_secondsSeconds elapsed when bypass was first confirmed. null if no bypass.
bypass_evidenceFilename of the queue entry that triggered the accept marker.
payloads_injectedTotal payloads written to the AFL++ sync directory (includes diversity variants).
stalls_detectedNumber of stall events queued for agent processing.
llm_callsNumber of LLM generation requests made.
react_turnsTotal ReAct turns consumed across all stall processing cycles.
elapsed_secondsActual wall-clock campaign duration.
duration_secondsRequested duration from --duration.
timeline60-second snapshots of edges and agent activity for plotting.
mutator_deployedtrue if a custom mutator was deployed after bypass detection.

Visualizing Coverage

Generate a coverage-over-time PNG from any result file with the plot subcommand:
agentic-afl plot ./logs/harness_20250101_120000.json -o coverage.png
The output image shows edge count on the Y-axis and elapsed time on the X-axis. Stall and injection events are annotated directly on the plot. If --output is omitted, the image is saved alongside the JSON with a .png extension.

Interpreting Results

edge_gain_pct

The primary health metric. Values above 50% indicate meaningful new coverage unlocked by the agent. Gains above 200% typically mean the agent bypassed a dispatch-table gatekeeper that gates multiple handlers.

bypass_detected

Confirms the accept marker was printed by a queue entry. This is ground truth — a true here means at least one payload in the AFL++ queue makes the target’s validation logic accept the input.

payloads_injected

Includes both the primary solved payload and any diversity variants generated by the DiversityGenerator after a successful solve. A high count relative to stalls_detected means the diversity generator ran, which is a good sign.
The agent runs asynchronously — it never pauses AFL++. AFL++ continues fuzzing at full speed while the agent’s pipeline (Ghidra, LLM, Z3) runs in the background. In the TUI you can observe both running simultaneously: the exec/s counter keeps climbing while agent pipeline stages advance.

Build docs developers (and LLMs) love