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.

This guide walks you through installing Agentic-AFL, wiring up your environment, and launching a complete fuzzing campaign against an AFL++-instrumented harness. By the end you will have a running campaign with live coverage metrics and a structured JSON result file you can plot and compare across runs.
Prerequisites — confirm these before continuing:
  • afl-fuzz and afl-cc are on your PATH (AFL++ 4.x)
  • Ghidra 11.x is installed, with the path available as GHIDRA_INSTALL_DIR (default: /opt/ghidra)
  • Python 3.11 or newer is active in your shell
  • A PostgreSQL instance is reachable (Step 3 provides a Docker one-liner)
  • An OpenAI or Google Gemini API key is available
1

Clone and install

Clone the repository and install the package in editable mode. The [dev] extra pulls in the test dependencies, the rich TUI library, and python-dotenv for .env loading.
git clone https://github.com/AdithyaaSivamal/Agentic-AFL.git
cd Agentic-AFL
pip install -e "agentic-afl/[dev]"
After installation, verify the CLI is available:
agentic-afl --help
2

Configure environment

Create a .env file at the project root. Agentic-AFL’s config module automatically loads this file via python-dotenv on startup. Explicit shell exports always override .env values.
LLM_API_PROVIDER=openai
LLM_API_KEY=sk-...
LLM_MODEL_NAME=gpt-4.1-2025-04-14
GHIDRA_INSTALL_DIR=/opt/ghidra
GHIDRA_PROJECT_DIR=/tmp/ghidra_projects
POSTGRES_DSN=postgresql://agentic_afl:agentic_afl@localhost:5432/agentic_afl
To use Google Gemini instead of OpenAI, set:
LLM_API_PROVIDER=gemini
GEMINI_API_KEY=AIza...
LLM_MODEL_NAME=gemini-2.5-pro
3

Start PostgreSQL

Agentic-AFL uses PostgreSQL to store constraint profiles and Z3 templates for CARM retrieval. The quickest way to get a compatible instance running is with Docker:
docker run -d --name agentic-afl-db \
  -e POSTGRES_USER=agentic_afl \
  -e POSTGRES_PASSWORD=agentic_afl \
  -e POSTGRES_DB=agentic_afl \
  -p 5432:5432 postgres:16
The schema is initialized automatically on the first agentic-afl fuzz run. If you already have a PostgreSQL instance, just ensure the POSTGRES_DSN in your .env points to a database that the configured user can create tables in.
4

Compile your harness

Your target must be instrumented with AFL++‘s compiler wrapper so that AFL++ can track edge coverage. Compile exactly as you would with gcc or clang, but substitute afl-cc:
afl-cc -o ./harness ./target.c
For C++ targets use afl-c++. For targets that require a custom build system, set CC=afl-cc and CXX=afl-c++ in your environment before running the build.
Agentic-AFL does not compile targets for you. The harness argument to agentic-afl fuzz must already be an AFL++-instrumented binary. If your harness is not instrumented, AFL++ will run but edge coverage will always read zero, and the stall detector will trigger immediately.
5

Run your first campaign

Launch a one-hour campaign with the rich TUI dashboard enabled. Point -i at a directory containing at least one seed file that reaches the first bytes of your target’s parsing logic.
agentic-afl fuzz ./harness -i ./seeds --duration 1h --tui
The full set of fuzz subcommand options:
FlagDefaultDescription
--duration1hCampaign wall-clock duration. Accepts 6h, 30m, 90s, or raw seconds.
--stall-minutes5Minutes of edge plateau before the agent triggers.
--accept-markerACCEPTStdout string that signals a confirmed math wall bypass.
--custom-mutatorPath to a Python AFL++ custom mutator deployed after bypass.
--log-dirDirectory for JSON result files (one file per campaign).
--nameharness stemCampaign name used in result filenames and the summary header.
--tuioffEnable the rich TUI dashboard (requires the rich package).
--debugoffEnable debug logging; also saves raw LLM completions and Z3 scripts to /tmp/agentic_afl_debug/.

Reading the Output

When --tui is not set, Agentic-AFL prints a columnar dashboard to stdout that updates every 25 seconds:
══════════════════════════════════════════════════════════════════════
  AGENTIC-AFL
  Target:   harness
  Seeds:    ./seeds (3 files)
  Duration: 1h00m00s
  Stall:    5m threshold
══════════════════════════════════════════════════════════════════════

      Time   Edges       Execs  Stalls  Inject  Status
  ────────────────────────────────────────────────────────────
       30s     412       98421       0       0  fuzzing
     1m05s     431      201034       0       0  fuzzing
     5m30s     431      612890       1       0  stall×1
     6m12s     589      641203       1       1  injected×1
ColumnMeaning
TimeElapsed campaign time formatted as HhMMmSSs.
EdgesCurrent edges_found from AFL++‘s fuzzer_stats.
ExecsCumulative execs_done from AFL++‘s fuzzer_stats.
StallsNumber of coverage stalls detected so far.
InjectNumber of solved payloads injected into the AFL++ sync directory.
Statusfuzzing during normal operation; stall×N when stalls are queued; injected×N after successful payload injection.
When --tui is used, the same data is presented in a live Rich panel layout with a pipeline stage indicator (Detect → Ghidra → Profile → CARM → LLM → Z3 → Inject) and a scrolling event log.

Campaign Results

At the end of every campaign (whether the duration expires or you press Ctrl+C), Agentic-AFL prints a structured summary:
══════════════════════════════════════════════════════════════════════
  CAMPAIGN RESULTS — harness
══════════════════════════════════════════════════════════════════════
  Duration:          1h00m00s
  Baseline edges:    412
  Final edges:       1087
  Edge gain:         +675 (+163.8%)
  Stalls detected:   2
  Payloads injected: 2
  LLM calls:         6
  Math wall bypass:  ✅ YES
  Evidence:          agentic_a3f2b1c0_0x08001a44_20250601_143022.bin
══════════════════════════════════════════════════════════════════════
FieldMeaning
Baseline edgesedges_found from AFL++‘s fuzzer_stats at ~10 seconds into the campaign — represents what AFL++ achieved on its own with the seed corpus.
Final edgesedges_found at campaign end — total coverage achieved with Agentic-AFL’s help.
Edge gainAbsolute and percentage increase in edges. A large gain after payload injection is the primary indicator that a math wall was bypassed.
Stalls detectedHow many times the stall detector fired (edge plateau for --stall-minutes).
Payloads injectedHow many solved Z3 payloads were successfully written to the AFL++ sync directory.
LLM callsTotal number of LLM API requests made (each call generates K scripts; multiple ReAct turns may occur per stall).
Math wall bypass✅ YES if any AFL++ queue entry produced the --accept-marker string when executed against the harness binary.
EvidenceThe filename of the queue entry that triggered the accept marker — a concrete byte payload that bypasses the math constraint.
If --log-dir is set, the full result is also written as a timestamped JSON file (e.g., harness_20250601_143022.json) containing the complete timeline of edge counts, stall events, and payload injection timestamps.

Visualizing Coverage

Generate a coverage-over-time plot from any campaign JSON result file:
agentic-afl plot ./results/campaign.json -o coverage.png
The plot command reads the timeline array from the JSON file (recorded every ~60 seconds during the campaign) and renders an edge count over time chart. The -o flag is optional — if omitted, the output image is saved with the same name as the JSON file but with a .png extension.
Use --stall-minutes 2 for quick experiments and CI-style validation runs — it triggers the agent much sooner than the default 5-minute threshold, so you can verify the full Extract → Translate → Solve → Inject pipeline completes within a short window. For production fuzzing campaigns on real targets, the default --stall-minutes 5 reduces false-positive stall detection.

Build docs developers (and LLMs) love