Harness Design Fuzzing
provos/ironcurtain
Reference vocabulary for designing instrumented harnesses that drive vulnerability discovery — design classes (trigger-driven vs coverage-driven), tiered scope (T1 isolated function / T2…
Sets up and runs libFuzzer, the coverage-guided fuzzer built into LLVM, on C/C++ code that compiles with Clang.
$ npx skills add trailofbits/skills --skill libfuzzer -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install trailofbits/skills libfuzzer --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/trailofbits/skills.git skills-src && mkdir -p .claude/skills && cp -r skills-src/plugins/testing-handbook-skills/skills/libfuzzer .claude/skills/libfuzzer && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "libfuzzer" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/libfuzzer into .claude/skills/libfuzzer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "libfuzzer", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/libfuzzerType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add trailofbits/skills --skill libfuzzer -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install trailofbits/skills libfuzzer --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/trailofbits/skills.git skills-src && mkdir -p .agents/skills && cp -r skills-src/plugins/testing-handbook-skills/skills/libfuzzer .agents/skills/libfuzzer && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "libfuzzer" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/libfuzzer into .agents/skills/libfuzzer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "libfuzzer", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add trailofbits/skills --skill libfuzzer -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install trailofbits/skills libfuzzer --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/trailofbits/skills.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/plugins/testing-handbook-skills/skills/libfuzzer .cursor/skills/libfuzzer && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "libfuzzer" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/libfuzzer into .cursor/skills/libfuzzer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "libfuzzer", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/trailofbits/skills.git --path plugins/testing-handbook-skills/skills/libfuzzer--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add trailofbits/skills --skill libfuzzer -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install trailofbits/skills libfuzzer --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/trailofbits/skills.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/plugins/testing-handbook-skills/skills/libfuzzer .gemini/skills/libfuzzer && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "libfuzzer" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/libfuzzer into .gemini/skills/libfuzzer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "libfuzzer", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install trailofbits/skills libfuzzerInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add trailofbits/skills --skill libfuzzer -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/trailofbits/skills.git skills-src && mkdir -p .github/skills && cp -r skills-src/plugins/testing-handbook-skills/skills/libfuzzer .github/skills/libfuzzer && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "libfuzzer" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/libfuzzer into .github/skills/libfuzzer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "libfuzzer", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add trailofbits/skills --skill libfuzzer -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install trailofbits/skills libfuzzer --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/trailofbits/skills.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/plugins/testing-handbook-skills/skills/libfuzzer .opencode/skills/libfuzzer && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "libfuzzer" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/libfuzzer into .opencode/skills/libfuzzer/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "libfuzzer", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
libfuzzerSets up and runs libFuzzer, the coverage-guided fuzzer built into LLVM, on C/C++ code that compiles with Clang.
Libfuzzer is an agent skill from trailofbits/skills, published by the product's own GitHub organization. Sets up and runs libFuzzer, the coverage-guided fuzzer built into LLVM, on C/C++ code that compiles with Clang. Covers harness structure, -fsanitize=fuzzer builds, corpus and dictionary management, sanitizer integration, and campaign triage. Use when writing or debugging an LLVMFuzzerTestOneInput harness, starting fuzzing on a C/C++ library, choosing between libFuzzer and AFL++, or working out why a libFuzzer run finds nothing.
Its SKILL.md is about 6.1k tokens, which your agent loads only when the skill is triggered. The skill folder holds 4 other files, including assets (for example `agents/openai.yaml`).
It sits in Security, covering Fuzzing. It works with C++. The repository describes itself as: Trail of Bits Claude Code skills for security research, vulnerability detection, and audit workflows. The licence is CC-BY-SA-4.0.
2 steps, taken from the first numbered list in SKILL.md.
Read from SKILL.md and the folder at commit 82fe822. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Shell commands in SKILL.md call:
curlaptmakecmakebrewFrom the folder's file list and the shell code blocks in SKILL.md.
Hosts in commands or code, which the agent is likely to contact:
raw.githubusercontent.comdownloads.sourceforge.netAlso links to:
github.comlearn.microsoft.comllvm.orgclang.llvm.orgFrom URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Libfuzzer loads about 6.1k tokens when it runs. Until then it costs about 110 tokens; SKILL.md has 1,701 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from trailofbits/skills at commit 82fe822, republished under its CC-BY-SA-4.0 licence (© trailofbits). 1,701 words, ~6,059 tokens.
.claude/skills/libfuzzer/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.libFuzzer is an in-process, coverage-guided fuzzer that is part of the LLVM project. It's the recommended starting point for fuzzing C/C++ projects due to its simplicity and integration with the LLVM toolchain. While libFuzzer has been in maintenance-only mode since late 2022, it is easier to install and use than its alternatives, has wide support, and will be maintained for the foreseeable future.
| Fuzzer | Best For | Complexity |
|---|---|---|
| libFuzzer | Quick setup, single-project fuzzing | Low |
| AFL++ | Multi-core fuzzing, diverse mutations | Medium |
| LibAFL | Custom fuzzers, research projects | High |
| Honggfuzz | Hardware-based coverage | Medium |
Choose libFuzzer when:
Note: Fuzzing harnesses written for libFuzzer are compatible with AFL++, making it easy to transition if you need more advanced features like better multi-core support.
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Validate input if needed
if (size < 1) return 0;
// Call your target function with fuzzer-provided data
my_target_function(data, size);
return 0;
}Compile and run:
clang++ -fsanitize=fuzzer,address -g -O2 harness.cc target.cc -o fuzz
mkdir corpus/
./fuzz corpus/apt install clang llvmFor the latest LLVM version:
# Add LLVM repository from apt.llvm.org
# Then install specific version, e.g.:
apt install clang-18 llvm-18# Using Homebrew
brew install llvm
# Or using Nix
nix-env -i clangInstall Clang through Visual Studio. Refer to Microsoft's documentation for setup instructions.
Recommendation: If possible, fuzz on a local x86_64 VM or rent one on DigitalOcean, AWS, or Hetzner. Linux provides the best support for libFuzzer.
clang++ --version
# Should show LLVM version informationThe harness is the entry point for the fuzzer. libFuzzer calls the LLVMFuzzerTestOneInput function repeatedly with different inputs.
#include <stdint.h>
#include <stddef.h>
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// 1. Optional: Validate input size
if (size < MIN_REQUIRED_SIZE) {
return 0; // Reject inputs that are too small
}
// 2. Optional: Convert raw bytes to structured data
// Example: Parse two integers from byte array
if (size >= 2 * sizeof(uint32_t)) {
uint32_t a = *(uint32_t*)(data);
uint32_t b = *(uint32_t*)(data + sizeof(uint32_t));
my_function(a, b);
}
// 3. Call target function
target_function(data, size);
// 4. Always return 0 (non-zero reserved for future use)
return 0;
}| Do | Don't |
|---|---|
| Handle all input types (empty, huge, malformed) | Call exit() - stops fuzzing process |
| Join all threads before returning | Leave threads running |
| Keep harness fast and simple | Add excessive logging or complexity |
| Maintain determinism | Use random number generators or read /dev/random |
| Reset global state between runs | Rely on state from previous executions |
| Use narrow, focused targets | Mix unrelated data formats (PNG + TCP) in one harness |
Rationale:
For complex inputs (strings, multiple parameters), use the FuzzedDataProvider helper:
#include <stdint.h>
#include <stddef.h>
#include "FuzzedDataProvider.h" // From LLVM project
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fuzzed_data(data, size);
// Extract structured data
size_t allocation_size = fuzzed_data.ConsumeIntegral<size_t>();
std::vector<char> str1 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
std::vector<char> str2 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
// Call target with extracted data
char* result = concat(&str1[0], str1.size(), &str2[0], str2.size(), allocation_size);
if (result != NULL) {
free(result);
}
return 0;
}Download FuzzedDataProvider.h from the LLVM repository.
Use a single harness to test multiple related functions:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 1 + 2 * sizeof(int32_t)) {
return 0;
}
uint8_t mode = data[0];
int32_t numbers[2];
memcpy(numbers, data + 1, 2 * sizeof(int32_t));
// Select function based on first byte
switch (mode % 4) {
case 0: add(numbers[0], numbers[1]); break;
case 1: subtract(numbers[0], numbers[1]); break;
case 2: multiply(numbers[0], numbers[1]); break;
case 3: divide(numbers[0], numbers[1]); break;
}
return 0;
}See Also: For detailed harness writing techniques, patterns for handling complex inputs, structure-aware fuzzing, and protobuf-based fuzzing, see the fuzz-harness-writing technique skill.
The key flag is -fsanitize=fuzzer, which:
main function)memcmpclang++ -fsanitize=fuzzer -g -O2 harness.cc target.cc -o fuzzFlags explained:
-fsanitize=fuzzer: Enable libFuzzer-g: Add debug symbols (helpful for crash analysis)-O2: Production-level optimizations (recommended for fuzzing)-DNO_MAIN: Define macro if your code has a main functionAddressSanitizer (recommended):
clang++ -fsanitize=fuzzer,address -g -O2 -U_FORTIFY_SOURCE harness.cc target.cc -o fuzzMultiple sanitizers:
clang++ -fsanitize=fuzzer,address,undefined -g -O2 harness.cc target.cc -o fuzzSee Also: For detailed sanitizer configuration, common issues, ASAN_OPTIONS flags, and advanced sanitizer usage, see the address-sanitizer and undefined-behavior-sanitizer technique skills.
| Flag | Purpose |
|---|---|
-fsanitize=fuzzer | Enable libFuzzer runtime and instrumentation |
-fsanitize=address | Enable AddressSanitizer (memory error detection) |
-fsanitize=undefined | Enable UndefinedBehaviorSanitizer |
-fsanitize=fuzzer-no-link | Instrument without linking fuzzer (for libraries) |
-g | Include debug symbols |
-O2 | Production optimization level |
-U_FORTIFY_SOURCE | Disable fortification (can interfere with ASan) |
For projects that produce static libraries:
export CC=clang CFLAGS="-fsanitize=fuzzer-no-link -fsanitize=address"
export CXX=clang++ CXXFLAGS="$CFLAGS"
./configure --enable-shared=no
makeclang++ -fsanitize=fuzzer -fsanitize=address harness.cc libmylib.a -o fuzzproject(FuzzTarget)
cmake_minimum_required(VERSION 3.0)
add_executable(fuzz main.cc harness.cc)
target_compile_definitions(fuzz PRIVATE NO_MAIN=1)
target_compile_options(fuzz PRIVATE -g -O2 -fsanitize=fuzzer -fsanitize=address)
target_link_libraries(fuzz -fsanitize=fuzzer -fsanitize=address)Build with:
cmake -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ .
cmake --build .Create a directory for the corpus (can start empty):
mkdir corpus/Optional but recommended: Provide seed inputs (valid example files):
# For a PNG parser:
cp examples/*.png corpus/
# For a protocol parser:
cp test_packets/*.bin corpus/Benefits of seed inputs:
The corpus directory contains:
a9993e364706816aba3e25717850c26c9cd0d89d)libFuzzer automatically minimizes corpus entries during fuzzing. To explicitly minimize:
mkdir minimized_corpus/
./fuzz -merge=1 minimized_corpus/ corpus/This creates a deduplicated, minimized corpus in minimized_corpus/.
See Also: For corpus creation strategies, seed selection, format-specific corpus building, and corpus maintenance, see the fuzzing-corpus technique skill.
./fuzz corpus/This runs until a crash is found or you stop it (Ctrl+C).
./fuzz -fork=1 -ignore_crashes=1 corpus/The -fork and -ignore_crashes flags (experimental but widely used) allow fuzzing to continue after finding crashes.
Control input size:
./fuzz -max_len=4000 corpus/Rule of thumb: 2x the size of minimal realistic input.
Set timeout:
./fuzz -timeout=2 corpus/Abort test cases that run longer than 2 seconds.
Use a dictionary:
./fuzz -dict=./format.dict corpus/Close stdout/stderr (speed up fuzzing):
./fuzz -close_fd_mask=3 corpus/See all options:
./fuzz -help=1Option 1: Jobs and workers (recommended):
./fuzz -jobs=4 -workers=4 -fork=1 -ignore_crashes=1 corpus/-jobs=4: Run 4 sequential campaigns-workers=4: Process jobs in parallel with 4 processesOption 2: Fork mode:
./fuzz -fork=4 -ignore_crashes=1 corpus/Note: For serious multi-core fuzzing, consider switching to AFL++, Honggfuzz, or LibAFL.
Re-run a single crash:
./fuzz ./crash-a9993e364706816aba3e25717850c26c9cd0d89dTest all inputs in a directory without fuzzing:
./fuzz -runs=0 corpus/When fuzzing runs, you'll see statistics like:
INFO: Seed: 3517090860
INFO: Loaded 1 modules (9 inline 8-bit counters)
#2 INITED cov: 3 ft: 4 corp: 1/1b exec/s: 0 rss: 26Mb
#57 NEW cov: 4 ft: 5 corp: 2/4b lim: 4 exec/s: 0 rss: 26Mb| Output | Meaning |
|---|---|
INITED | Fuzzing initialized |
NEW | New coverage found, added to corpus |
REDUCE | Input minimized while keeping coverage |
cov: N | Number of coverage edges hit |
corp: X/Yb | Corpus size: X entries, Y total bytes |
exec/s: N | Executions per second |
rss: NMb | Resident memory usage |
On crash:
==11672== ERROR: libFuzzer: deadly signal
artifact_prefix='./'; Test unit written to ./crash-a9993e364706816aba3e25717850c26c9cd0d89d
0x61,0x62,0x63,
abc
Base64: YWJjThe crash is saved to ./crash-<hash> with the input shown in hex, UTF-8, and Base64.
Reproducibility: Use -seed=<value> to reproduce a fuzzing campaign (single-core only).
Dictionaries help the fuzzer discover interesting inputs faster by providing hints about the input format.
Create a text file with quoted strings (one per line):
# Lines starting with '#' are comments
# Magic bytes
magic="\x89PNG"
magic2="IEND"
# Keywords
"GET"
"POST"
"Content-Type"
# Hex sequences
delimiter="\xFF\xD8\xFF"./fuzz -dict=./format.dict corpus/From header files:
grep -o '".*"' header.h > header.dictFrom man pages:
man curl | grep -oP '^\s*(--|-)\K\S+' | sed 's/[,.]$//' | sed 's/^/"&/; s/$/&"/' | sort -u > man.dictFrom binary strings:
strings ./binary | sed 's/^/"&/; s/$/&"/' > strings.dictUsing LLMs: Ask ChatGPT or similar to generate a dictionary for your format (e.g., "Generate a libFuzzer dictionary for a JSON parser").
See Also: For advanced dictionary generation, format-specific dictionaries, and dictionary optimization strategies, see the fuzzing-dictionaries technique skill.
While libFuzzer shows basic coverage stats (cov: N), detailed coverage analysis requires additional tools.
1. Recompile with coverage instrumentation:
clang++ -fsanitize=fuzzer -fprofile-instr-generate -fcoverage-mapping harness.cc target.cc -o fuzz2. Run fuzzer to collect coverage:
LLVM_PROFILE_FILE="coverage-%p.profraw" ./fuzz -runs=10000 corpus/3. Merge coverage data:
llvm-profdata merge -sparse coverage-*.profraw -o coverage.profdata4. Generate coverage report:
llvm-cov show ./fuzz -instr-profile=coverage.profdata5. Generate HTML report:
llvm-cov show ./fuzz -instr-profile=coverage.profdata -format=html > coverage.htmlTips:
See Also: For detailed coverage analysis techniques, identifying coverage gaps, systematic coverage improvement, and comparing coverage across fuzzers, see the coverage-analysis technique skill.
ASan detects memory errors like buffer overflows and use-after-free bugs. Highly recommended for fuzzing.
Enable ASan:
clang++ -fsanitize=fuzzer,address -g -O2 -U_FORTIFY_SOURCE harness.cc target.cc -o fuzzExample ASan output:
==1276163==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x6020000c4ab1
WRITE of size 1 at 0x6020000c4ab1 thread T0
#0 0x55555568631a in check_buf(char*, unsigned long) main.cc:13:25
#1 0x5555556860bf in LLVMFuzzerTestOneInput harness.cc:7:3Configure ASan with environment variables:
ASAN_OPTIONS=verbosity=1:abort_on_error=1 ./fuzz corpus/Important flags:
verbosity=1: Show ASan is activedetect_leaks=0: Disable leak detection (leaks reported at end)abort_on_error=1: Call abort() instead of _exit() on errorsDrawbacks:
-rss_limit_mb=0)See Also: For comprehensive ASan configuration, common pitfalls, symbolization, and combining with other sanitizers, see the address-sanitizer technique skill.
UBSan detects undefined behavior like integer overflow, null pointer dereference, etc.
Enable UBSan:
clang++ -fsanitize=fuzzer,undefined -g -O2 harness.cc target.cc -o fuzzCombine with ASan:
clang++ -fsanitize=fuzzer,address,undefined -g -O2 harness.cc target.cc -o fuzzMSan detects uninitialized memory reads. More complex to use (requires rebuilding all dependencies).
clang++ -fsanitize=fuzzer,memory -g -O2 harness.cc target.cc -o fuzz| Issue | Solution |
|---|---|
| ASan slows fuzzing too much | Use -fsanitize-recover=address for non-fatal errors |
| Out of memory | Set ASAN_OPTIONS=rss_limit_mb=0 or -rss_limit_mb=0 |
| Stack exhaustion | Increase stack size: ASAN_OPTIONS=stack_size=8388608 |
False positives with _FORTIFY_SOURCE | Use -U_FORTIFY_SOURCE flag |
| MSan reports in dependencies | Rebuild all dependencies with -fsanitize=memory |
libpng is a widely-used library for reading/writing PNG images. Bugs can lead to security issues.
1. Get source code:
curl -L -O https://downloads.sourceforge.net/project/libpng/libpng16/1.6.37/libpng-1.6.37.tar.xz
tar xf libpng-1.6.37.tar.xz
cd libpng-1.6.37/2. Install dependencies:
apt install zlib1g-dev3. Compile with fuzzing instrumentation:
export CC=clang CFLAGS="-fsanitize=fuzzer-no-link -fsanitize=address"
export CXX=clang++ CXXFLAGS="$CFLAGS"
./configure --enable-shared=no
make4. Get a harness (or write your own):
curl -O https://raw.githubusercontent.com/glennrp/libpng/f8e5fa92b0e37ab597616f554bee254157998227/contrib/oss-fuzz/libpng_read_fuzzer.cc5. Prepare corpus and dictionary:
mkdir corpus/
curl -o corpus/input.png https://raw.githubusercontent.com/glennrp/libpng/acfd50ae0ba3198ad734e5d4dec2b05341e50924/contrib/pngsuite/iftp1n3p08.png
curl -O https://raw.githubusercontent.com/glennrp/libpng/2fff013a6935967960a5ae626fc21432807933dd/contrib/oss-fuzz/png.dict6. Link and compile fuzzer:
clang++ -fsanitize=fuzzer -fsanitize=address libpng_read_fuzzer.cc .libs/libpng16.a -lz -o fuzz7. Run fuzzing campaign:
./fuzz -close_fd_mask=3 -dict=./png.dict corpus/Harness that finds a division-by-zero bug:
#include <stdint.h>
#include <stddef.h>
double divide(uint32_t numerator, uint32_t denominator) {
// Bug: No check if denominator is zero
return numerator / denominator;
}
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if(size != 2 * sizeof(uint32_t)) {
return 0;
}
uint32_t numerator = *(uint32_t*)(data);
uint32_t denominator = *(uint32_t*)(data + sizeof(uint32_t));
divide(numerator, denominator);
return 0;
}Compile and fuzz:
clang++ -fsanitize=fuzzer harness.cc -o fuzz
./fuzzThe fuzzer will quickly find inputs causing a crash.
| Tip | Why It Helps |
|---|---|
| Start with single-core, switch to AFL++ for multi-core | libFuzzer harnesses work with AFL++ |
| Use dictionaries for structured formats | 10-100x faster bug discovery |
Close file descriptors with -close_fd_mask=3 | Speed boost if SUT writes output |
Set reasonable -max_len | Prevents wasted time on huge inputs |
| Run for days/weeks, not minutes | Coverage plateaus take time to break |
| Use seed corpus from test suites | Starts fuzzing from valid inputs |
For highly structured inputs (e.g., complex protocols, file formats), use libprotobuf-mutator:
See structure-aware fuzzing documentation for details.
libFuzzer allows custom mutators for specialized fuzzing:
extern "C" size_t LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size,
size_t MaxSize, unsigned int Seed) {
// Custom mutation logic
return new_size;
}
extern "C" size_t LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
const uint8_t *Data2, size_t Size2,
uint8_t *Out, size_t MaxOutSize,
unsigned int Seed) {
// Custom crossover logic
return new_size;
}| Setting | Impact |
|---|---|
-close_fd_mask=3 | Closes stdout/stderr, speeds up fuzzing |
-max_len=<reasonable_size> | Avoids wasting time on huge inputs |
-timeout=<seconds> | Detects hangs, prevents stuck executions |
| Disable ASan for baseline | 2-4x speed boost (but misses memory bugs) |
Use -jobs and -workers | Limited multi-core support |
| Run on Linux | Best platform support and performance |
| Problem | Cause | Solution |
|---|---|---|
| No crashes found after hours | Poor corpus, low coverage | Add seed inputs, use dictionary, check harness |
| Very slow executions/sec (<100) | Target too complex, excessive logging | Optimize target, use -close_fd_mask=3, reduce logging |
| Out of memory | ASan's 20TB virtual memory | Set -rss_limit_mb=0 to disable RSS limit |
| Fuzzer stops after first crash | Default behavior | Use -fork=1 -ignore_crashes=1 to continue |
| Can't reproduce crash | Non-determinism in harness/target | Remove random number generation, global state |
Linking errors with -fsanitize=fuzzer | Missing libFuzzer runtime | Ensure using Clang, check LLVM installation |
| GCC project won't compile with Clang | GCC-specific code | Switch to AFL++ with gcc_plugin instead |
| Coverage not improving | Corpus plateau | Run longer, add dictionary, improve seeds, check coverage report |
| Crashes but ASan doesn't trigger | Memory error not detected without ASan | Recompile with -fsanitize=address |
| Skill | Use Case |
|---|---|
| fuzz-harness-writing | Detailed guidance on writing effective harnesses, structure-aware fuzzing, and FuzzedDataProvider usage |
| address-sanitizer | Memory error detection configuration, ASAN_OPTIONS, and troubleshooting |
| undefined-behavior-sanitizer | Detecting undefined behavior during fuzzing |
| coverage-analysis | Measuring fuzzing effectiveness and identifying untested code paths |
| fuzzing-corpus | Building and managing seed corpora, corpus minimization strategies |
| fuzzing-dictionaries | Creating format-specific dictionaries for faster bug discovery |
| Skill | When to Consider |
|---|---|
| aflpp | When you need serious multi-core fuzzing, or when libFuzzer coverage plateaus |
| honggfuzz | When you want hardware-based coverage feedback on Linux |
| libafl | When building custom fuzzers or conducting fuzzing research |
© trailofbits, CC-BY-SA-4.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 2 other files (assets) in plugins/testing-handbook-skills/skills/libfuzzer of trailofbits/skills.
Open the folder on GitHubat commit 82fe822
Libfuzzer next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Libfuzzer this skilltrailofbits/skills | 7.4k | — | ~6.1k | Automated safety check: Pass | CC-BY-SA-4.0 | |
| Harness Design Fuzzingprovos/ironcurtain | 613 | — | ~5.7k | Automated safety check: Pass | Apache-2.0 | |
| ClusterfuzzliteInternationalColorConsortium/iccDEV | 183 | — | ~1.5k | Automated safety check: Pass | BSD-3-Clause | |
| Audit Native Memory Safetycyberful/cyberful | 134 | — | ~829 | Automated safety check: Pass | AGPL-3.0 | |
| Fuzzingmohitmishra786/low-level-dev-skills | 253 | — | ~2.1k | Automated safety check: Pass | MIT | |
| Fizzpashov/skills | 1.2k | 2 repos | ~11k | Automated safety check: Pass | MIT |
provos/ironcurtain
Reference vocabulary for designing instrumented harnesses that drive vulnerability discovery — design classes (trigger-driven vs coverage-driven), tiered scope (T1 isolated function / T2…
InternationalColorConsortium/iccDEV
Build, test, or update the iccDEV ClusterFuzzLite libFuzzer integration across ASan, UBSan, and MSan.
cyberful/cyberful
Audit C, C++, unsafe Rust, native extensions, parsers, codecs, FFI boundaries, and systems code for memory corruption and low-level exploitation risk.
mohitmishra786/low-level-dev-skills
Fuzzing skill for automated input-driven bug finding in C/C++.
pashov/skills
Generate Echidna/Medusa-compatible Solidity fuzz suites from Foundry or Hardhat projects.
microsoft/onnxruntime
Finds and fixes out-of-range output writes in ONNX Runtime operator shape-inference functions where a getNumOutputs guard admits too few outputs.
trailofbits/skills
Scans a codebase for vulnerabilities with CodeQL's data flow and taint tracking in run-all or important-only modes, including data extensions for project-specific sources and sinks.
trailofbits/skills
Generates Mermaid diagrams from Trailmark code graphs, including call graphs, class hierarchies, module dependency maps, complexity heatmaps and attack surface data flows.
trailofbits/skills
Compares Trailmark code graphs at two snapshots, such as commits, tags or directories, to surface attack paths, blast radius and taint changes that text diffs miss.
trailofbits/skills
Draws a 12 Houses tarot spread to break ties when a request is vague or casually delegated, then reads the cards to pick the next step.
trailofbits/skills
Detects languages, proposes rulesets for approval, then runs the approved Semgrep scan across a codebase and merges the output into one SARIF file.
trailofbits/skills
Searches and extracts data from Burp Suite project files on the command line: regex searches over responses, audit findings, proxy history and site map data.
Works with
Categories
Sets up and runs libFuzzer, the coverage-guided fuzzer built into LLVM, on C/C++ code that compiles with Clang. Libfuzzer is an agent skill from trailofbits/skills, published by the product's own GitHub organization. Sets up and runs libFuzzer, the coverage-guided fuzzer built into LLVM, on C/C++ code that compiles with Clang.
Libfuzzer fits situations like: debugging an LLVMFuzzerTestOneInput harness; starting fuzzing on a C/C++ library; choosing between libFuzzer and AFL++; working out why a libFuzzer run finds nothing.
Run `npx skills add trailofbits/skills --skill libfuzzer -a claude-code`. Or copy the skill folder (plugins/testing-handbook-skills/skills/libfuzzer in trailofbits/skills) into .claude/skills/libfuzzer in your project. Claude Code loads it when a task matches its description.
Run `npx skills add trailofbits/skills --skill libfuzzer -a codex`. Or copy the skill folder (plugins/testing-handbook-skills/skills/libfuzzer in trailofbits/skills) into .agents/skills/libfuzzer in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add trailofbits/skills --skill libfuzzer -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/libfuzzer, .gemini/skills/libfuzzer, .github/skills/libfuzzer and .opencode/skills/libfuzzer in your project.
Going by SKILL.md and its folder, Libfuzzer needs the command-line tools its instructions call (curl, apt, make, cmake and brew).
SKILL.md names 6 domains. In commands or code: raw.githubusercontent.com and downloads.sourceforge.net; the agent is likely to contact these when it follows the instructions. As links in the text: github.com, learn.microsoft.com, llvm.org and clang.llvm.org. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Libfuzzer is published under the CC-BY-SA-4.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.1k tokens (SKILL.md is roughly 24k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Libfuzzer: Harness Design Fuzzing (provos/ironcurtain, 613 stars), Clusterfuzzlite (InternationalColorConsortium/iccDEV, 183 stars), Audit Native Memory Safety (cyberful/cyberful, 134 stars) and Fuzzing (mohitmishra786/low-level-dev-skills, 253 stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
trailofbits (a GitHub organization, an official publisher) maintains it in trailofbits/skills, which has 7,400 GitHub stars. The repository holds 79 skills in this directory. The repository was last updated on October 2, 2026.
Source: trailofbits/skills on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.