Audit Native Memory Safety
cyberful/cyberful
Audit C, C++, unsafe Rust, native extensions, parsers, codecs, FFI boundaries, and systems code for memory corruption and low-level exploitation risk.
Guides writing and improving fuzzing harnesses for C, C++ and Rust so random byte input gets translated into structured, reproducible test cases for the target code.
$ npx skills add trailofbits/skills --skill harness-writing -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install trailofbits/skills harness-writing --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/harness-writing .claude/skills/harness-writing && 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 "harness-writing" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/harness-writing into .claude/skills/harness-writing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "harness-writing", 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/harness-writingType 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 harness-writing -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install trailofbits/skills harness-writing --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/harness-writing .agents/skills/harness-writing && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "harness-writing" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/harness-writing into .agents/skills/harness-writing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "harness-writing", 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 harness-writing -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install trailofbits/skills harness-writing --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/harness-writing .cursor/skills/harness-writing && 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 "harness-writing" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/harness-writing into .cursor/skills/harness-writing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "harness-writing", 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/harness-writing--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 harness-writing -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install trailofbits/skills harness-writing --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/harness-writing .gemini/skills/harness-writing && 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 "harness-writing" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/harness-writing into .gemini/skills/harness-writing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "harness-writing", 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 harness-writingInstalls 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 harness-writing -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/harness-writing .github/skills/harness-writing && 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 "harness-writing" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/harness-writing into .github/skills/harness-writing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "harness-writing", 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 harness-writing -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 harness-writing --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/harness-writing .opencode/skills/harness-writing && 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 "harness-writing" agent skill from https://github.com/trailofbits/skills/tree/main/plugins/testing-handbook-skills/skills/harness-writing into .opencode/skills/harness-writing/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "harness-writing", 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.
harness-writingGuides writing and improving fuzzing harnesses for C, C++ and Rust so random byte input gets translated into structured, reproducible test cases for the target code.
This skill treats the harness, the entry point that receives fuzzer bytes and calls the system under test, as the single biggest factor in whether a fuzzing campaign finds real bugs. It defines key terms such as the system under test, the fuzzer's required entry-point signature like LLVMFuzzerTestOneInput or fuzz_target!, FuzzedDataProvider for turning raw bytes into typed values, determinism, and interleaved fuzzing where one harness exercises several operations based on its input.
It applies when a fuzz target is being written for the first time, when a running campaign has low coverage or finds nothing, when crashes do not reproduce, or when the target's API needs structured rather than raw input; it is skipped when an existing well-tested harness or automatic harness generation already covers the target. A quick-reference table gives the minimal C++ and Rust harness signatures, a size-validation guard, and FuzzedDataProvider calls for consuming typed values and strings.
The step-by-step guidance begins by identifying entry points in the target API before structuring how bytes map onto arguments, and the skill ships as a plugin asset bundle with a Trail of Bits mark.
5 steps, taken from the step headings 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:
cargoFrom the folder's file list and the shell code blocks in SKILL.md.
Links to these hosts (documentation or services it may open):
github.comllvm.orgrust-fuzz.github.ioyoutube.comFrom 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.
Fuzzing Harness Design loads about 5.3k tokens when it runs. Until then it costs about 110 tokens; SKILL.md has 1,693 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,693 words, ~5,306 tokens.
.claude/skills/harness-writing/SKILL.md (or your agent's skills folder). This skill also uses 2 other files; get the full folder from GitHub.A fuzzing harness is the entrypoint function that receives random data from the fuzzer and routes it to your system under test (SUT). The quality of your harness directly determines which code paths get exercised and whether critical bugs are found. A poorly written harness can miss entire subsystems or produce non-reproducible crashes.
The harness is the bridge between the fuzzer's random byte generation and your application's API. It must parse raw bytes into meaningful inputs, call target functions, and handle edge cases gracefully. The most important part of any fuzzing setup is the harness—if written poorly, critical parts of your application may not be covered.
| Concept | Description |
|---|---|
| Harness | Function that receives fuzzer input and calls target code under test |
| SUT | System Under Test—the code being fuzzed |
| Entry point | Function signature required by the fuzzer (e.g., LLVMFuzzerTestOneInput) |
| FuzzedDataProvider | Helper class for structured extraction of typed data from raw bytes |
| Determinism | Property that ensures same input always produces same behavior |
| Interleaved fuzzing | Single harness that exercises multiple operations based on input |
Apply this technique when:
Skip this technique when:
| Task | Pattern |
|---|---|
| Minimal C++ harness | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) |
| Minimal Rust harness | `fuzz_target!( |
| Size validation | if (size < MIN_SIZE) return 0; |
| Cast to integers | uint32_t val = *(uint32_t*)(data); |
| Use FuzzedDataProvider | FuzzedDataProvider fuzzed_data(data, size); |
| Extract typed data (C++) | auto val = fuzzed_data.ConsumeIntegral<uint32_t>(); |
| Extract string (C++) | auto str = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF); |
Find functions in your codebase that:
Good targets are typically:
Start with the simplest possible harness that calls your target function:
C/C++:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
target_function(data, size);
return 0;
}Rust:
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
target_function(data);
});Reject inputs that are too small or too large to be meaningful:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Ensure minimum size for meaningful input
if (size < MIN_INPUT_SIZE || size > MAX_INPUT_SIZE) {
return 0;
}
target_function(data, size);
return 0;
}Rationale: The fuzzer generates random inputs of all sizes. Your harness must handle empty, tiny, huge, or malformed inputs without causing unexpected issues in the harness itself (crashes in the SUT are fine—that's what we're looking for).
For APIs that require typed data (integers, strings, etc.), use casting or helpers like FuzzedDataProvider:
Simple casting:
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;
}Using FuzzedDataProvider:
#include "FuzzedDataProvider.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fuzzed_data(data, size);
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);
concat(&str1[0], str1.size(), &str2[0], str2.size(), allocation_size);
return 0;
}Run the fuzzer and monitor:
Iterate on the harness to improve these metrics.
Use Case: When target expects primitive types like integers or floats
Implementation:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Ensure exactly 2 4-byte numbers
if (size != 2 * sizeof(uint32_t)) {
return 0;
}
// Split input into two integers
uint32_t numerator = *(uint32_t*)(data);
uint32_t denominator = *(uint32_t*)(data + sizeof(uint32_t));
divide(numerator, denominator);
return 0;
}Rust equivalent:
fuzz_target!(|data: &[u8]| {
if data.len() != 2 * std::mem::size_of::<i32>() {
return;
}
let numerator = i32::from_ne_bytes([data[0], data[1], data[2], data[3]]);
let denominator = i32::from_ne_bytes([data[4], data[5], data[6], data[7]]);
divide(numerator, denominator);
});Why it works: Any 8-byte input is valid. The fuzzer learns that inputs must be exactly 8 bytes, and every bit flip produces a new, potentially interesting input.
Use Case: When target requires multiple strings, integers, or variable-length data
Implementation:
#include "FuzzedDataProvider.h"
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
FuzzedDataProvider fuzzed_data(data, size);
// Extract different types of data
size_t allocation_size = fuzzed_data.ConsumeIntegral<size_t>();
// Consume variable-length strings with terminator
std::vector<char> str1 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
std::vector<char> str2 = fuzzed_data.ConsumeBytesWithTerminator<char>(32, 0xFF);
char* result = concat(&str1[0], str1.size(), &str2[0], str2.size(), allocation_size);
if (result != NULL) {
free(result);
}
return 0;
}Why it helps: FuzzedDataProvider handles the complexity of extracting structured data from a byte stream. It's particularly useful for APIs that need multiple parameters of different types.
Use Case: When multiple related operations should be tested in a single harness
Implementation:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
if (size < 1 + 2 * sizeof(int32_t)) {
return 0;
}
// First byte selects operation
uint8_t mode = data[0];
// Next bytes are operands
int32_t numbers[2];
memcpy(numbers, data + 1, 2 * sizeof(int32_t));
int32_t result = 0;
switch (mode % 4) {
case 0:
result = add(numbers[0], numbers[1]);
break;
case 1:
result = subtract(numbers[0], numbers[1]);
break;
case 2:
result = multiply(numbers[0], numbers[1]);
break;
case 3:
result = divide(numbers[0], numbers[1]);
break;
}
// Prevent compiler from optimizing away the calls
printf("%d", result);
return 0;
}Advantages:
When to use:
Use Case: When fuzzing Rust code that uses custom structs
Implementation:
use arbitrary::Arbitrary;
#[derive(Debug, Arbitrary)]
pub struct Name {
data: String
}
impl Name {
pub fn check_buf(&self) {
let data = self.data.as_bytes();
if data.len() > 0 && data[0] == b'a' {
if data.len() > 1 && data[1] == b'b' {
if data.len() > 2 && data[2] == b'c' {
process::abort();
}
}
}
}
}Harness with arbitrary:
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: your_project::Name| {
data.check_buf();
});Add to Cargo.toml:
[dependencies]
arbitrary = { version = "1", features = ["derive"] }Why it helps: The arbitrary crate automatically handles deserialization of raw bytes into your Rust structs, reducing boilerplate and ensuring valid struct construction.
Limitation: The arbitrary crate doesn't offer reverse serialization, so you can't manually construct byte arrays that map to specific structs. This works best when starting from an empty corpus (fine for libFuzzer, problematic for AFL++).
| Tip | Why It Helps |
|---|---|
| Start with parsers | High bug density, clear entry points, easy to harness |
| Mock I/O operations | Prevents hangs from blocking I/O, enables determinism |
| Use FuzzedDataProvider | Simplifies extraction of structured data from raw bytes |
| Reset global state | Ensures each iteration is independent and reproducible |
| Free resources in harness | Prevents memory exhaustion during long campaigns |
| Avoid logging in harness | Logging is slow—fuzzing needs 100s-1000s exec/sec |
| Test harness manually first | Run harness with known inputs before starting campaign |
| Check coverage early | Ensure harness reaches expected code paths |
For highly structured input formats, consider using Protocol Buffers as an intermediate format with custom mutators:
// Define your input format in .proto file
// Use libprotobuf-mutator to generate valid mutations
// This ensures fuzzer mutates message contents, not the protobuf encoding itselfThis approach is more setup but prevents the fuzzer from wasting time on unparseable inputs. See structure-aware fuzzing documentation for details.
Problem: Random values or timing dependencies cause non-reproducible crashes.
Solutions:
rand() with deterministic PRNG seeded from fuzzer input:uint32_t seed = fuzzed_data.ConsumeIntegral<uint32_t>();
srand(seed);/dev/random or /dev/urandomIf your SUT uses global state (singletons, static variables), reset it between iterations:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Reset global state before each iteration
global_reset();
target_function(data, size);
// Clean up resources
global_cleanup();
return 0;
}Rationale: Global state can cause crashes after N iterations rather than on a specific input, making bugs non-reproducible.
Follow these rules to ensure effective fuzzing harnesses:
| Rule | Rationale |
|---|---|
| Handle all input sizes | Fuzzer generates empty, tiny, huge inputs—harness must handle gracefully |
Never call exit() | Calling exit() stops the fuzzer process. Use abort() in SUT if needed |
| Join all threads | Each iteration must run to completion before next iteration starts |
| Be fast | Aim for 100s-1000s executions/sec. Avoid logging, high complexity, excess memory |
| Maintain determinism | Same input must always produce same behavior for reproducibility |
| Avoid global state | Global state reduces reproducibility—reset between iterations if unavoidable |
| Use narrow targets | Don't fuzz PNG and TCP in same harness—different formats need separate targets |
| Free resources | Prevent memory leaks that cause resource exhaustion during long campaigns |
Note: These guidelines apply not just to harness code, but to the entire SUT. If the SUT violates these rules, consider patching it (see the fuzzing obstacles technique).
| Anti-Pattern | Problem | Correct Approach |
|---|---|---|
| Global state without reset | Non-deterministic crashes | Reset all globals at start of harness |
| Blocking I/O or network calls | Hangs fuzzer, wastes time | Mock I/O, use in-memory buffers |
| Memory leaks in harness | Resource exhaustion kills campaign | Free all allocations before returning |
Calling exit() in SUT | Stops entire fuzzing process | Use abort() or return error codes |
| Heavy logging in harness | Reduces exec/sec by orders of magnitude | Disable logging during fuzzing |
| Too many operations per iteration | Slows down fuzzer | Keep iterations fast and focused |
| Mixing unrelated input formats | Corpus entries not useful across formats | Separate harnesses for different formats |
| Not validating input size | Harness crashes on edge cases | Check size before accessing data |
Harness signature:
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
// Your code here
return 0; // Non-zero return is reserved for future use
}Compilation:
clang++ -fsanitize=fuzzer,address -g harness.cc -o fuzz_targetIntegration tips:
FuzzedDataProvider.h for structured input extraction-fsanitize=fuzzer to link the fuzzing runtime-fsanitize=address,undefined) to detect more bugs-g for better stack traces when crashes occurRunning:
./fuzz_target corpus_dir/Resources:
AFL++ supports multiple harness styles. For best performance, use persistent mode:
Persistent mode harness:
#include <unistd.h>
int main(int argc, char **argv) {
#ifdef __AFL_HAVE_MANUAL_CONTROL
__AFL_INIT();
#endif
unsigned char buf[MAX_SIZE];
while (__AFL_LOOP(10000)) {
// Read input from stdin
ssize_t len = read(0, buf, sizeof(buf));
if (len <= 0) break;
// Call target function
target_function(buf, len);
}
return 0;
}Compilation:
afl-clang-fast++ -g harness.cc -o fuzz_targetIntegration tips:
__AFL_LOOP) for 10-100x speedup__AFL_INIT()) to skip setup overheadAFL_USE_ASAN=1 or AFL_USE_UBSAN=1 for sanitizer buildsRunning:
afl-fuzz -i seeds/ -o findings/ -- ./fuzz_targetHarness signature:
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
// Your code here
});With structured input (arbitrary crate):
#![no_main]
use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: YourStruct| {
data.check();
});Creating harness:
cargo fuzz init
cargo fuzz add my_targetIntegration tips:
arbitrary crate for automatic struct deserializationfuzz/fuzz_targets/ directoryRunning:
cargo +nightly fuzz run my_targetResources:
Harness signature:
// +build gofuzz
package mypackage
func Fuzz(data []byte) int {
// Call target function
target(data)
// Return codes:
// -1 if input is invalid
// 0 if input is valid but not interesting
// 1 if input is interesting (e.g., added new coverage)
return 0
}Building:
go-fuzz-buildIntegration tips:
Running:
go-fuzz -bin=./mypackage-fuzz.zip -workdir=fuzz| Issue | Cause | Solution |
|---|---|---|
| Low executions/sec | Harness is too slow (logging, I/O, complexity) | Profile harness, remove bottlenecks, mock I/O |
| No crashes found | Coverage not reaching buggy code | Check coverage, improve harness to reach more paths |
| Non-reproducible crashes | Non-determinism or global state | Remove randomness, reset globals between iterations |
| Fuzzer exits immediately | Harness calls exit() | Replace exit() with abort() or return error |
| Out of memory errors | Memory leaks in harness or SUT | Free allocations, use leak sanitizer to find leaks |
| Crashes on empty input | Harness doesn't validate size | Add if (size < MIN_SIZE) return 0; |
| Corpus not growing | Inputs too constrained or format too strict | Use FuzzedDataProvider or structure-aware fuzzing |
| Skill | How It Applies |
|---|---|
| libfuzzer | Uses LLVMFuzzerTestOneInput harness signature with FuzzedDataProvider |
| aflpp | Supports persistent mode harnesses with __AFL_LOOP for performance |
| cargo-fuzz | Uses Rust-specific fuzz_target! macro with arbitrary crate integration |
| atheris | Python harness takes bytes, calls Python functions |
| ossfuzz | Requires harnesses in specific directory structure for cloud fuzzing |
| Skill | Relationship |
|---|---|
| coverage-analysis | Measure harness effectiveness—are you reaching target code? |
| address-sanitizer | Detects bugs found by harness (buffer overflows, use-after-free) |
| fuzzing-dictionary | Provide tokens to help fuzzer pass format checks in harness |
| fuzzing-obstacles | Patch SUT when it violates harness rules (exit, non-determinism) |
Split Inputs in libFuzzer - Google Fuzzing Docs Explains techniques for handling multiple input parameters in a single fuzzing harness, including use of magic separators and FuzzedDataProvider.
Structure-Aware Fuzzing with Protocol Buffers Advanced technique using protobuf as intermediate format with custom mutators to ensure fuzzer mutates message contents rather than format encoding.
libFuzzer Documentation Official LLVM documentation covering harness requirements, best practices, and advanced features.
cargo-fuzz Book Comprehensive guide to writing Rust fuzzing harnesses with cargo-fuzz and the arbitrary crate.
© 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/harness-writing of trailofbits/skills.
Open the folder on GitHubat commit 82fe822
We found 1 copy of this SKILL.md (exact, near-identical or edited) in other folders, from 1 other GitHub owner. This page covers the copy in trailofbits/skills, which our catalogue first saw on October 7, 2026.
Fuzzing Harness Design 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 |
|---|---|---|---|---|---|---|
| Fuzzing Harness Design this skilltrailofbits/skills | 7.4k | 1 repos | ~5.3k | Automated safety check: Pass | CC-BY-SA-4.0 | |
| Audit Native Memory Safetycyberful/cyberful | 135 | — | ~829 | Automated safety check: Pass | AGPL-3.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 | |
| Fuzzingnwjs/chromium.src | 160 | — | ~1.1k | Automated safety check: Pass | BSD-3-Clause | |
| Fuzzingmohitmishra786/low-level-dev-skills | 253 | — | ~2.1k | Automated safety check: Pass | MIT |
cyberful/cyberful
Audit C, C++, unsafe Rust, native extensions, parsers, codecs, FFI boundaries, and systems code for memory corruption and low-level exploitation risk.
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.
nwjs/chromium.src
Implements, registers, and verifies fuzz tests in Chromium. An agent skill from nwjs/chromium.src.
mohitmishra786/low-level-dev-skills
Fuzzing skill for automated input-driven bug finding in C/C++.
mohitmishra786/low-level-dev-skills
Rust security skill for supply chain safety and memory-safe development.
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
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
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
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.
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.
Categories
Guides writing and improving fuzzing harnesses for C, C++ and Rust so random byte input gets translated into structured, reproducible test cases for the target code. This skill treats the harness, the entry point that receives fuzzer bytes and calls the system under test, as the single biggest factor in whether a fuzzing campaign finds real bugs., FuzzedDataProvider for turning raw bytes into typed values, determinism, and interleaved fuzzing where one harness exercises several operations based on its input.
Fuzzing Harness Design fits situations like: writing a first fuzz harness for a C, C++ or Rust library; fixing a fuzzing campaign that finds no bugs or has low coverage; debugging a fuzzer crash that will not reproduce; deciding whether to fuzz several related functions together with one harness.
Run `npx skills add trailofbits/skills --skill harness-writing -a claude-code`. Or copy the skill folder (plugins/testing-handbook-skills/skills/harness-writing in trailofbits/skills) into .claude/skills/harness-writing in your project. Claude Code loads it when a task matches its description.
Run `npx skills add trailofbits/skills --skill harness-writing -a codex`. Or copy the skill folder (plugins/testing-handbook-skills/skills/harness-writing in trailofbits/skills) into .agents/skills/harness-writing 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 harness-writing -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/harness-writing, .gemini/skills/harness-writing, .github/skills/harness-writing and .opencode/skills/harness-writing in your project.
Going by SKILL.md and its folder, Fuzzing Harness Design needs the command-line tools its instructions call (cargo). Our summary lists: A C, C++ or Rust codebase with a fuzzing toolchain such as libFuzzer or cargo-fuzz.
SKILL.md names 4 domains. As links in the text: github.com, llvm.org, rust-fuzz.github.io and youtube.com. 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.
Fuzzing Harness Design 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 5.3k tokens (SKILL.md is roughly 21k 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 Fuzzing Harness Design: Audit Native Memory Safety (cyberful/cyberful, 135 stars), Harness Design Fuzzing (provos/ironcurtain, 613 stars), Clusterfuzzlite (InternationalColorConsortium/iccDEV, 183 stars) and Fuzzing (nwjs/chromium.src, 160 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,420 GitHub stars. The repository holds 79 skills in this directory. The repository was last updated on October 7, 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.