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 harness is a thin C program that bridges AFL++ and your target library or protocol implementation. AFL++ calls the harness as ./harness @@, where @@ is replaced by the path to each mutated input file — the harness receives that path as argv[1], opens the file, and passes its bytes to the target function. Agentic-AFL watches the harness’s output for a configurable accept marker that signals the math or crypto validation wall has been bypassed. Before running any campaign, you need a correctly written and compiled harness — getting this step right determines how much of your target’s state machine the fuzzer can explore.

Harness Requirements

Every Agentic-AFL harness must satisfy three constraints:
1

Accept input from argv[1]

AFL++ passes the mutated input file path as the first command-line argument (the @@ placeholder). The harness must open argv[1], read it, and pass the bytes to the target function. Do not read from stdin — Agentic-AFL does not use deferred fork-server stdin mode.
2

Compile with afl-cc

The binary must be instrumented with AFL++‘s edge-coverage instrumentation. Compile with afl-cc (which wraps clang or gcc). Without instrumentation, AFL++ cannot measure coverage and the stall detector has no edge signal to monitor.
3

Print the accept marker on bypass

When the target’s validation logic accepts the input (i.e., the math or CRC wall is passed), the harness must print the accept marker to stdout or stderr. The default marker is ACCEPT. Agentic-AFL polls the AFL++ queue to detect when an injected payload triggers this marker, confirming a successful bypass.

Example Harness Structure

Replace process_packet with your actual parser or validator. The structure below is minimal and correct for Agentic-AFL:
harness.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Stub — replace with your actual parser/validator
int process_packet(const unsigned char *buf, size_t len);

int main(int argc, char *argv[]) {
    if (argc < 2) return 1;

    FILE *f = fopen(argv[1], "rb");
    if (!f) return 1;

    unsigned char buf[4096];
    size_t n = fread(buf, 1, sizeof(buf), f);
    fclose(f);

    if (process_packet(buf, n) == 0) {
        // Math wall bypassed — print the accept marker
        printf("ACCEPT\n");
        return 0;
    }
    return 1;
}
The accept marker (ACCEPT) must be flushed to stdout before the process exits. printf followed by process exit is sufficient — fflush is not required since the C runtime flushes stdout buffers on normal exit.

Compiling with afl-cc

export AFL_CC=clang  # or gcc
afl-cc -o ./harness ./harness.c -lm
Verify that the binary is instrumented by running it under afl-showmap:
afl-showmap -o /dev/null -- ./harness ./seeds/seed1.bin
You should see a non-zero tuple count in the output. A zero count means the binary was not instrumented.
Set __AFL_FUZZ_TESTCASE_BUF and __AFL_FUZZ_TESTCASE_LEN for persistent mode if you want higher throughput. Persistent mode avoids the fork() overhead per execution and can increase exec/s by 5–10×. See the AFL++ documentation for the __AFL_LOOP macro.

Providing Seed Inputs

Seed quality directly affects how quickly AFL++ reaches the stall site. Seeds should be:

Minimal valid frames

Use the smallest complete packet or frame that passes all pre-validation checks up to (but not including) the math/crypto wall. Larger seeds slow down mutation without adding coverage.

One per frame type

Provide at least one seed per protocol frame type or message class. AFL++‘s corpus covers one code path per seed — a single seed for a multi-type protocol leaves entire dispatch branches unreachable.
Place all seeds in a flat directory (no subdirectories):
./seeds/
  seed_process_data.bin
  seed_alarm.bin
  seed_diagnostics.bin
  seed_auth.bin

Custom Accept Marker

The marker that Agentic-AFL watches for can be changed with --accept-marker. This is useful when the target already prints protocol-specific strings on success, or when you want to distinguish bypass events from unrelated stdout output:
agentic-afl fuzz ./harness -i ./seeds --accept-marker "VALID_FRAME"
Make sure the harness prints the exact marker string (case-sensitive, substring match) when the validation check passes. The marker is matched against the combined stdout and stderr of each queue entry Agentic-AFL re-runs after payload injection.

Verifying Your Harness

Test the harness manually before running a campaign. A known-bad seed should produce a non-zero exit code; a hand-crafted valid frame should print the accept marker:
./harness ./seeds/seed1.bin
echo $?  # Non-zero means the math wall rejected the seed (expected)

./harness ./known_good_frame.bin
# Should print: ACCEPT
If ./harness ./known_good_frame.bin does not print the accept marker, Agentic-AFL will never confirm a bypass even when one occurs. Verify the marker path in your target code before starting a campaign.

Build docs developers (and LLMs) love