Fuzzing skill for automated input-driven bug finding in C/C++. Use when setting up libFuzzer or AFL++ fuzz targets, defining fuzz entry points around parsing or I/O boundaries, integrating fuzzing into CI, managing corpora, or combining fuzzing with sanitizers. Activates on queries about libFuzzer, AFL, afl-fuzz, fuzz targets, corpus management, coverage-guided fuzzing, or OSS-Fuzz integration.
Fuzzing skill for automated input-driven bug finding in C/C++. Use when setting up libFuzzer or AFL++ fuzz targets, defining fuzz entry points around parsing or I/O boundaries, integrating fuzzing into CI, managing corpora, or combining fuzzing with sanitizers. Activates on queries about libFuzzer, AFL, afl-fuzz, fuzz targets, corpus management, coverage-guided fuzzing, or OSS-Fuzz integration.
Fuzzing
Purpose
Guide agents through setting up and running coverage-guided fuzz testing: libFuzzer (in-process) and AFL++ (fork-based), with sanitizer integration and CI pipeline setup.
Triggers
"How do I fuzz-test my parser/deserializer?"
"What is a fuzz target / how do I write one?"
"How do I set up libFuzzer?"
"How do I use AFL++ on my program?"
"How do I run fuzzing in CI?"
"Fuzzer found a crash — how do I reproduce it?"
Workflow
1. Write a fuzz target (libFuzzer)
A fuzz target is a function that accepts arbitrary bytes and exercises the code under test.
// fuzz_parser.c#include<stdint.h>#include<stddef.h>#include"myparser.h"// Entry point called by libFuzzer with random dataintLLVMFuzzerTestOneInput(constuint8_t *data, size_t size) {
// Must not abort/exit on invalid input (that's expected)
MyParser *p = parser_create();
(p) {
parser_feed(p, ( *)data, size);
parser_destroy(p);
}
;
}
// Must not read outside [data, data+size)
if
const
char
return
0
// Always return 0 (non-zero means discard input)
Key rules:
Never call abort(), exit(), or use global state that persists across calls
Handle all inputs gracefully (crash = bug found)
Keep the target fast: the fuzzer calls it millions of times
2. Build with libFuzzer
# Clang (libFuzzer is built into Clang)
clang -fsanitize=fuzzer,address -g -O1 \
fuzz_parser.c myparser.c -o fuzz_parser
# With UBSan too
clang -fsanitize=fuzzer,address,undefined -g -O1 \
fuzz_parser.c myparser.c -o fuzz_parser
-fsanitize=fuzzer links libFuzzer and provides main(). Do not provide your own main() in the fuzz target.
3. Run libFuzzer
# Create corpus directorymkdir -p corpus
# Seed with known-good inputs (greatly accelerates coverage)cp tests/inputs/* corpus/
# Run the fuzzer
./fuzz_parser corpus/ -max_len=65536 -timeout=10
# Run for a time limit
./fuzz_parser corpus/ -max_total_time=3600
# Run with specific number of jobs (parallel)
./fuzz_parser corpus/ -jobs=4 -workers=4
# Minimise a corpus (remove redundant inputs)
./fuzz_parser -merge=1 corpus_min/ corpus/
Common flags:
Flag
Default
Effect
-max_len=N
4096
Max input size in bytes
-timeout=N
1200
Kill if single run takes > N seconds
-max_total_time=N
0 (forever)
Total fuzzing time
-runs=N
-1 (infinite)
Total number of runs
-dict=file
none
Dictionary of interesting tokens
-jobs=N
1
Parallel jobs (each writes its own log)
-merge=1
off
Merge mode: minimise corpus
4. Reproduce a crash
libFuzzer writes crash inputs to files named crash-<hash>, oom-<hash>, timeout-<hash>.
# Reproduce
./fuzz_parser crash-abc123
# Debug with GDB
gdb ./fuzz_parser
(gdb) run crash-abc123
5. AFL++ setup
AFL++ is a fork-based fuzzer that works on arbitrary programs (not just those with a fuzz entry point).
# Install
apt install afl++ # or build from source# Instrument the target
CC=afl-clang-fast CXX=afl-clang-fast++ \
cmake -S . -B build-afl -DCMAKE_BUILD_TYPE=Debug
cmake --build build-afl
# Or compile directly
afl-clang-fast -g -O1 -o prog_afl main.c myparser.c
# Create input corpusmkdir -p afl-input afl-output
echo"hello" > afl-input/seed1
# Run
afl-fuzz -i afl-input -o afl-output -- ./prog_afl @@
# @@ is replaced with the input file path# For stdin-based programs: remove @@
afl-fuzz -i afl-input -o afl-output -- ./prog_afl
6. AFL++ with persistent mode (faster)
Persistent mode avoids fork() per input — much faster for library fuzzing:
// In your harness:#include"myparser.h"intmain(int argc, char **argv) {
while (__AFL_LOOP(1000)) {
// Read inputunsignedchar *buf = NULL;
ssize_t len = read(0, &buf, MAX_SIZE); // or use afl_custom_mutator
parser_feed((char*)buf, len);
free(buf);
}
return0;
}