libfuzzer-cpp
Author and run LLVM libFuzzer for C/C++ - in-process coverage-guided fuzzing. Covers harness authoring (LLVMFuzzerTestOneInput entry point), build with -fsanitize=fuzzer,address,undefined, runtime flags (-max_total_time, -runs, -dict, -fork, -workers), corpus + crash-artefact handling, and CI integration. Use for libraries / parsers / decoders in C/C++ where in-process fuzzing of a function is the right scope. Compose with ASan + UBSan from sanitiser-integration-reference and corpus discipline from corpus-management-reference.
Install with skills.sh (any agent)
npx skills add testland/qa --skill libfuzzer-cpplibfuzzer-cpp
Overview
This skill wraps LLVM's libFuzzer (per llvm.org/docs/LibFuzzer.html (opens in new window)) for C/C++ targets. Composes with:
When to use
Authoring
The fuzz target
Define the entry point LLVMFuzzerTestOneInput:
#include <cstddef>
#include <cstdint>
#include "your_library.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
your_parser(Data, Size);
return 0;
}Per LLVM docs, the function signature is fixed: takes a const byte buffer + size, returns int (must return 0 for normal execution; non-zero values are reserved).
The fuzzer calls this function repeatedly with mutated Data. The target's job is to drive the library code under test and let sanitisers + asserts catch bugs.
Initialisation
Optional one-time setup:
extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) {
your_library_init();
return 0;
}Build
Standard build flag:
clang -g -O1 \
-fsanitize=fuzzer,address,undefined \
-fno-sanitize-recover=all \
-fno-omit-frame-pointer \
fuzz_target.cc your_library.cc -o fuzz_targetPer sanitiser-integration-reference: ASan + UBSan is the default pair; add MSan in a separate binary if needed.
Tips for an effective target
| Tip | Why |
|---|---|
| Keep the target small | Faster iteration; clearer coverage |
| Avoid global state between runs | Cross-input contamination defeats coverage guidance |
| Use the full input | Don't if (Size < 100) return 0; unless the lib requires |
| Avoid expensive I/O / network in the target | Slows iterations |
Use FuzzedDataProvider for structured inputs | Splits Data into typed sub-values |
FuzzedDataProvider (from LLVM's compiler-rt/include/fuzzer/FuzzedDataProvider.h):
#include <fuzzer/FuzzedDataProvider.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
FuzzedDataProvider fdp(Data, Size);
int port = fdp.ConsumeIntegralInRange(1, 65535);
std::string host = fdp.ConsumeRandomLengthString(64);
std::vector<uint8_t> body = fdp.ConsumeRemainingBytes<uint8_t>();
your_request_handler(host, port, body);
return 0;
}Running
Basic run
mkdir corpus/ seeds/
# Populate seeds/ with hand-curated inputs
./fuzz_target -max_total_time=3600 corpus/ seeds/The first directory is writable (evolved corpus); subsequent are read-only seeds (per corpus-management-reference).
Common flags
Most-used: -max_total_time=N, -runs=N (-1 = infinite), -dict=path, -fork=N, -workers=N, -merge=1 (corpus minimisation), -rss_limit_mb=N (default 2048), -timeout=N (per-input, default 1200). Full table (per llvm.org/docs/LibFuzzer.html (opens in new window)): references/flags-and-ci.md.
Parallel fuzzing
./fuzz_target -fork=8 -max_total_time=3600 corpus/ seeds/-fork=N spawns N processes, each with its own corpus subset. Combine corpora periodically with -merge=1.
Reproducing a crash
./fuzz_target crash-<sha1>
# Sanitiser report prints to stderr; same as the original crashVerify: confirm the replay prints the same sanitiser bug class and top stack frame as the original finding before minimising. If it does not reproduce, the artefact is stale against the current build (target or library rebuilt) - rebuild the target and re-run before proceeding.
Minimise the crash input:
./fuzz_target -minimize_crash=1 -runs=10000 crash-<sha1>
# Writes minimized-from-crash-<sha1> with the smallest reproducerDictionary file
For structured formats:
# fuzz.dict
"{"
"}"
"["
"]"
"true"
"false"
"null"
"\":\""Invoke: ./fuzz_target -dict=fuzz.dict corpus/.
Parsing results
libFuzzer crash artefacts are saved as:
Each file's contents are the input bytes that triggered the crash. Pair with the sanitiser report (stderr) for stack + allocation site.
For automated parsing (e.g., file as a bug), feed the sanitiser-report output to bug-report-from-failure (in the qa-defect-management plugin):
./fuzz_target crash-<sha1> 2> sanitiser-report.txt
python scripts/file-bug-from-asan.py sanitiser-report.txt crash-<sha1>CI integration
Short smoke fuzz (5 min) on every PR: build with -fsanitize=fuzzer,address,undefined, cache fuzz/corpus, run ./fuzz_target -max_total_time=300 fuzz/corpus fuzz/seeds, and upload crash-* / leak-* / timeout-* / oom-* artifacts. Full workflow: references/flags-and-ci.md.
For long-running campaigns, ossfuzz-integration is the canonical infrastructure.
Anti-patterns
| Anti-pattern | Why it fails | Fix |
|---|---|---|
LLVMFuzzerTestOneInput with global state mutation | Cross-input contamination breaks coverage signal | Reset state per call or use LLVMFuzzerInitialize |
| Fuzz target without ASan + UBSan | Catches only crashes; 80%+ of bugs missed | Always compose with sanitisers |
| No corpus minimisation ever | Corpus grows unbounded; cycle time degrades | Weekly -merge=1 |
| Crash committed without minimisation | Large bug-report attachments | Always -minimize_crash=1 |
| Single huge fuzz target | Slow iterations; coverage attribution opaque | Split into multiple targets per function |
Ignoring -rss_limit_mb OOMs | False crash class | Set limit explicit; or disable allocator-related target paths |
| No dictionary for structured formats | Fuzzer slowly rediscovers grammar | Always supply -dict= for JSON / XML / SQL / proto |
Limitations
References
libFuzzer: runtime flags and CI
View source (opens in new window)libFuzzer: runtime flags and CI
Deep reference for libfuzzer-cpp. The core harness / build / run / reproduce workflow lives in the skill spine; this file holds the full runtime-flag table and the CI job.
Common flags
Per llvm.org/docs/LibFuzzer.html (opens in new window):
| Flag | Effect |
|---|---|
-max_total_time=N | Stop after N seconds |
-runs=N | Stop after N executions (-1 = infinite) |
-dict=path | Use dictionary file |
-seed=N | Random seed |
-fork=N | Run N parallel fork-mode workers |
-workers=N | Number of parallel worker processes |
-jobs=N | Total number of jobs to run across workers |
-merge=1 | Corpus minimisation mode |
-print_final_stats=1 | Print stats summary on exit |
-rss_limit_mb=N | RSS memory limit (default 2048) |
-timeout=N | Per-input timeout in seconds (default 1200) |
-only_ascii=1 | Restrict to ASCII bytes |
CI integration
Short smoke fuzz on every PR:
jobs:
fuzz:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- name: Install clang
run: sudo apt-get install -y clang lld
- name: Build fuzz target
run: |
clang++ -g -O1 \
-fsanitize=fuzzer,address,undefined \
-fno-sanitize-recover=all \
-fno-omit-frame-pointer \
fuzz/fuzz_target.cc lib/parser.cc -o fuzz_target
- uses: actions/cache@v4
with:
path: fuzz/corpus
key: fuzz-corpus-${{ github.sha }}
restore-keys: fuzz-corpus-
- name: Smoke fuzz (5 min)
run: ./fuzz_target -max_total_time=300 fuzz/corpus fuzz/seeds
- name: Upload crashes
if: always()
uses: actions/upload-artifact@v4
with:
name: crashes
path: |
crash-*
leak-*
timeout-*
oom-*Related skills
afl-plus-plus
Author and run AFL++ - out-of-process coverage-guided fuzzer (a community fork of Google's original AFL with improved mutations and instrumentation). Covers afl-cc / afl-clang-fast instrumented build, afl-fuzz invocation, parallel master/slave (-M / -S), dictionary support (-x), QEMU mode (-Q) for binaries without source, output structure (queue / crashes / hangs), crash minimisation (afl-tmin), corpus minimisation (afl-cmin), crash filename triage, and CI integration. Use for fuzzing standalone binaries (file processors, command-line tools) where libFuzzer's in-process model doesn't fit; for cross-fuzzer corpus strategy see corpus-management-reference.
atheris-python-fuzzing
Author and run Atheris - Google's Python coverage-guided fuzzer built on libFuzzer. Covers pip installation, atheris.Setup + atheris.Fuzz invocation, TestOneInput(data: bytes) target signature, FuzzedDataProvider for structured input, instrument_imports() / instrument_func decorators for coverage instrumentation, and libFuzzer-passthrough flags (-atheris_runs, -max_total_time, -dict). Use for fuzzing Python libraries - also supports CPython native-extension fuzzing.
cargo-fuzz-rust
Author and run cargo-fuzz - Rust fuzzing via libFuzzer with cargo integration. Covers `cargo install cargo-fuzz`, `cargo fuzz init` + `cargo fuzz add {target}` for harness scaffolding, the `fuzz_target!` macro for entry-point declaration, the `Arbitrary` trait for structured input mutation, and `cargo fuzz run` invocation. Requires Rust nightly. Use for fuzz testing Rust libraries - cargo-fuzz wraps libFuzzer with native Rust ergonomics.
corpus-management-reference
Pure-reference catalog of fuzz-corpus management practices. Defines what a corpus is (seed corpus + evolved corpus saved by the fuzzer), corpus directory layout per libFuzzer / AFL++ / Go native / cargo-fuzz / OSS-Fuzz, the canonical crash-artefact naming (crash-{sha1} / leak-{sha1} / timeout-{sha1}), seed corpus construction strategies (sample-from-prod, sample-from-test-fixtures, from-spec-keywords), corpus minimisation, dictionary files, and the OSS-Fuzz integration corpus sync. Use as the corpus-discipline reference when building a fuzz target or maintaining a long-running fuzz campaign.
crash-triage-reference
Pure-reference catalog for manually triaging individual fuzzer crash artifacts - reading ASan, UBSan, and MSan output; classifying findings as LIKELY-EXPLOITABLE, MEDIUM, or BENIGN; deduplicating by stack-hash; and minimizing reproducers with -minimize_crash. Use when you need to understand what a specific crash means, build exploitability intuition, or manually work a small set of findings. For automated bulk triage across a full artifact directory, run automated findings triage instead.
fuzz-tool-selector
Routes a fuzz-target authoring task to the right fuzzer for the detected language and build type. Decision tree: C/C++ → libfuzzer-cpp + afl-plus-plus; Rust → cargo-fuzz-rust (or libfuzzer-cpp via FFI); Go → go-native-fuzzing; Python → atheris-python-fuzzing; JVM → jazzer-jvm-fuzzing; closed-source binary → afl-plus-plus in QEMU mode; mature open-source project → ossfuzz-integration. Use when a project needs coverage-guided fuzzing and no fuzzer has been chosen for its language or toolchain yet.
go-native-fuzzing
Author and run Go's native fuzzing (Go 1.18+) - coverage-guided fuzzing built into the standard testing package via FuzzXxx functions. Covers f.Add seed-corpus declaration, f.Fuzz callback signature with typed parameters, testdata/fuzz/{FuzzXxx}/ directory layout for seeds + regression cases, the -fuzz flag for `go test`, and CI integration via short smoke runs. Use for fuzz testing Go libraries - Go's native approach integrates seamlessly with standard `go test` rather than requiring a separate toolchain like AFL++.
jazzer-jvm-fuzzing
Author and run Jazzer - Code Intelligence's JVM coverage-guided fuzzer built on libFuzzer. Covers Maven / Gradle / standalone JAR installation, the @FuzzTest annotation (JUnit 5 integration), typed parameter mutation (String, primitives, byte[]), built-in JVM sanitisers (SSRF / path traversal / OS command injection / deserialization gadget / ReDoS), and the JAZZER_FUZZ=1 env var to switch between regression and fuzzing modes. Use for fuzz testing Java / Kotlin libraries - particularly effective against parsing, deserialization, and HTTP-handling code.
ossfuzz-integration
Author and submit a project to Google OSS-Fuzz - the open-source continuous fuzzing service that runs libFuzzer / AFL++ / Honggfuzz campaigns on Google infrastructure 24x7. Covers the project.yaml + Dockerfile + build.sh contract, the $OUT/$WORK conventions, supported languages + sanitisers, seed-corpus + dictionary submission, the OSS-Fuzz Build Status dashboard, and the disclosure SLA (issues filed in Monorail with 90-day deadline). Use to offload long-running fuzz campaigns to dedicated infrastructure rather than self-hosting.
sanitiser-integration-reference
Pure-reference catalog of compiler sanitisers used with fuzz testing - AddressSanitizer (ASan), UndefinedBehaviorSanitizer (UBSan), MemorySanitizer (MSan), ThreadSanitizer (TSan), and LeakSanitizer (LSan). Explains what each detects, compatibility (can ASan + UBSan combine? - yes; ASan + MSan? - no), build flags, runtime options (ASAN_OPTIONS / UBSAN_OPTIONS env vars), and the typical ~2x slowdown per ASan. Use to pick the right sanitiser per fuzz target, configure the build, and interpret crash reports.