Skip to content
Development
Skill

/fuzzing

Use when planning an end-to-end fuzzing program for a project: engine and target selection, corpus management, and CI or nightly wiring. Not for harness design: use fuzz-harness-writing. Not for engine operation or triage: use libfuzzer. Not for sanitizer flag reference: use

From plugin
odin-claude-plugin
36200 skills
Install
$ npx -y skills add OutlineDriven/odin-claude-plugin --skill fuzzing --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/fuzzing

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when planning an end-to-end fuzzing program for a project: engine and target selection, corpus management, and CI or nightly wiring. Not for harness design: use fuzz-harness-writing. Not for engine operation or triage: use libfuzzer. Not for sanitizer flag reference: use

SKILL.md

fuzzing.SKILL.md
name: fuzzing
description: 'Use when planning an end-to-end fuzzing program for a project: engine and target selection, corpus management, and CI or nightly wiring. Not for harness design: use fuzz-harness-writing. Not for engine operation or triage: use libfuzzer. Not for sanitizer flag reference: use sanitizers.'

Fuzzing

Contract

| Field | Bound contract | |---|---| | Trigger | Coverage-guided fuzz testing for a parser, deserializer, codec, or API boundary: harness writing, libFuzzer or AFL++ setup, corpus management, crash reproduction, or CI runs. | | Authority | Reversible local. Writes are limited to fuzz binaries, corpora, dictionaries, findings, and logs under the project tree; rollback is deleting those directories. No remote mutation. | | Side effect | Built fuzz targets, corpus directories, crash artifacts, and coverage measurements. | | Done | A fuzz target runs against a seeded corpus, the crash or clean verdict is reproduced on demand, and the CI or nightly plan is written down. |

Inputs

1. Target code (required): the library or entry point to fuzz, ideally a parse or I/O boundary. 2. Seeds (required when they exist): known-good inputs from tests or real traffic. 3. Toolchain (required): Clang for libFuzzer; AFL++ installed for fork-mode fuzzing. Grounded channels: Clang 23.1.0, AFL++ v5.03c (AGPL 3.0+ since v5.00c, mixed licensing in the repo). 4. Time budget (optional): seconds for CI regression runs, hours for nightly.

Procedure

1. Write the target as a function of arbitrary bytes. The harness owns every guarantee:

// fuzz_parser.c
#include <stdint.h>
#include <stddef.h>
#include "myparser.h"

int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
    MyParser *p = parser_create();
    if (p) {
        parser_feed(p, (const char *)data, size);
        parser_destroy(p);
    }
    return 0;
}

Rules: return 0 for uninteresting input instead of exiting; never call `abort` or `exit`; never read or write outside `[data, data + size)`; keep no state between calls; stay fast, since the fuzzer calls it millions of times. Done when: the harness passes a review against each rule. 2. Build with libFuzzer and the sanitizer set. `-fsanitize=fuzzer` links libFuzzer and supplies `main`, so the target must not define one:

clang -fsanitize=fuzzer,address,undefined -fno-omit-frame-pointer -g -O1 \
    fuzz_parser.c myparser.c -o fuzz_parser

Done when: the binary links without a duplicate `main` and runs `-help=1` cleanly. 3. Seed and run:

mkdir -p corpus
cp tests/inputs/* corpus/          # real seeds sharply cut time to coverage
./fuzz_parser corpus/ -max_len=65536 -timeout=10
./fuzz_parser corpus/ -max_total_time=3600
./fuzz_parser corpus/ -jobs=4 -workers=4

Defaults worth knowing: `-max_len` 4096 when unset, `-timeout` 1200 seconds, `-rss_limit_mb` 2048, `-error_exitcode` 77, `-len_control` 100. Workers default to half the core count when unset. Done when: the run starts consuming the corpus and reporting new coverage. 4. Reproduce and minimize every finding. Artifacts land as `crash-<hash>`, `oom-<hash>`, or `timeout-<hash>` in the artifact prefix directory:

./fuzz_parser crash-abc123                 # reproduce
./fuzz_parser -minimize_crash=1 -max_total_time=60 crash-abc123

Re-run the minimized case under gdb (`gdb --args ./fuzz_parser crash-min`) to name the faulting frame. Done when: one small input reproduces the bug deterministically. 5. Add AFL++ where process-level isolation or non-library targets are needed:

afl-clang-fast -g -O1 -o prog_afl main.c myparser.c
afl-fuzz -i afl-input -o afl-output -- ./prog_afl @@    # @@ = input file path
afl-fuzz -i afl-input -o afl-output -- ./prog_afl       # stdin target: drop @@

Persistent mode avoids a fork per input and raises throughput for library targets; `__AFL_LOOP(1000)` bounds each iteration batch. Scale out with `-M main` plus one `-S name` instance per spare core, and read status with `afl-whatsup afl-output`. Done when: at least one AFL++ instance reports stable cycles with new paths. 6. Manage the corpus as an asset. Merge runs to the coverage-unique subset, and keep the corpus in version control or CI cache:

./fuzz_parser -merge=1 corpus_min/ corpus/ run1/ run2/
afl-cmin -i afl-output/default/queue -o corpus_min -- ./prog_afl @@
./fuzz_parser corpus/ -runs=0 -print_coverage=1     # coverage of the corpus

Done when: one minimized corpus exists and reproduces the union of coverage. 7. Guide mutation with a dictionary when the format has tokens:

# parser.dict
kw1="<"
kw2=">"
null_byte="\x00"
magic1="\x89PNG"
./fuzz_parser corpus/ -dict=parser.dict
afl-fuzz -i corpus/ -o out/ -x parser.dict -- ./prog_afl @@

For structured formats that checksum or length-prefix, implement `LLVMFuzzerCustomMutator` and repair the invariant after `LLVMFuzzerMutate`, or the fuzzer never passes the header. Done when: deep paths are reached without human-crafted inputs. 8. Wire CI as a fixed-time regression, not an open-ended run:

- name: Build fuzz target
  run: |
    clang -fsanitize=fuzzer,address,undefined -g -O1 \
      fuzz_parser.c myparser.c -o fuzz_parser
- name: Fuzz regression (60 seconds)
  run: |
    ./fuzz_parser corpus/ -max_total_time=60 -error_exitcode=1 \
      -artifact_prefix=artifacts/
- name: Reproduce known crashes
  run: |
    for f in known_crashes/*; do ./fuzz_parser "$f" || exit 1; done

Longer campaigns go to scheduled jobs with `-max_total_time=3600` or to OSS-Fuzz infrastructure. Done when: a pull request cannot merge with a target that regresses. 9. Add Python targets with Atheris, and Zig targets through the built-in fuzzer (Zig 0.13 and later):

import atheris, sys

with atheris.instrument_imports():
    import myparser

def TestOneInput(data: bytes) -> None:
    fdp = atheris.FuzzedDataProvider(data)
    try:
        myparser.parse(fdp.ConsumeString(sys.maxsize
Read more
Ships withodin-claude-plugin

Formerly the ODIN Claude Plugin. The repository URL is unchanged. Outline-Driven Development, nicknamed ODIN, is a highly opinionated code-agent skill library: principles-first engineering, surgical editing, and workflow automation, published as installable

Get the whole plugin
Stats
36
Stars
0
Forks
Active
Maintenance
Python
Language
Apache-2.0
License
3d ago
Last commit
10mo ago
Created

Repo: OutlineDriven/odin-claude-plugin

Other skills on odin-claude-plugin.