Use when a project needs fuzz testing, a fuzz campaign must be written or maintained, or fuzzer crashes need triage. Picks libFuzzer, AFL++, cargo-fuzz, Go fuzzing, Atheris or Jazzer by language, then covers fuzz targets, seed corpora, dictionaries, sanitizers, crash triage and CI smoke fuzzing.
91%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Passed
No findings from the security scan
Shared reference for coverage-guided-fuzzing; the per-engine references
and the umbrella workflow live alongside this file.
Pure-reference catalog of the five clang sanitisers (ASan, UBSan, MSan, TSan, LSan) used with coverage-guided fuzz targets - what each detects, build flags, runtime options, compatibility matrix, performance overhead. Consumed by the per-engine references and fuzz-target authoring. For corpus discipline see corpus-management.md.
-fsanitize=fuzzer,<sanitisers> plus -fno-sanitize-recover=all and -fno-omit-frame-pointer -g.ASAN_OPTIONS, UBSAN_OPTIONS) so the fuzzer aborts on first error.| Sanitiser | Detects (summary) | Build flag | Slowdown |
|---|---|---|---|
| ASan | heap / stack / global OOB, use-after-free, double-free | -fsanitize=address -fno-omit-frame-pointer -g | ~2x |
| UBSan | signed overflow, div-by-zero, null deref, misaligned access | -fsanitize=undefined -fno-sanitize-recover=all | ~10% |
| MSan | uninitialised memory reads | -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins | 3x |
| TSan | data races, deadlocks, thread-safety violations | -fsanitize=thread -O1 -g | 5 - 15x |
| LSan | memory leaks at program exit | -fsanitize=leak (or embedded in ASan) | small |
Full per-sanitiser detail - complete detect lists, the ASAN_OPTIONS /
UBSAN_OPTIONS runtime-option tables, the MSan whole-program requirement, and
LSan's embedded vs standalone modes: see "Per-sanitiser catalog" below.
Can multiple sanitisers run in the same binary?
| Sanitiser | ASan | UBSan | MSan | TSan |
|---|---|---|---|---|
| ASan | - | ✓ | ✗ | ✗ |
| UBSan | ✓ | - | ✓ | ✓ |
| MSan | ✗ | ✓ | - | ✗ |
| TSan | ✗ | ✓ | ✗ | - |
The standard fuzzing pair is ASan + UBSan (catches most memory + UB issues, manageable slowdown):
clang -g -O1 -fsanitize=fuzzer,address,undefined \
-fno-sanitize-recover=all \
-fno-omit-frame-pointer fuzz_target.cc -o fuzz_targetFor MSan-required projects (e.g., crypto libraries), build a separate MSan-only binary and run it as a second fuzzing campaign.
The -fsanitize=fuzzer,address,undefined flag composes the
libFuzzer engine with ASan + UBSan in one binary. Each sanitiser
contributes its instrumentation.
Per llvm.org/docs/LibFuzzer.html:
# Build
clang -g -O1 \
-fsanitize=fuzzer,address,undefined \
-fno-sanitize-recover=all \
fuzz_target.cc -o fuzz_target
# Run
ASAN_OPTIONS=abort_on_error=1:halt_on_error=1 \
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
./fuzz_target -max_total_time=3600 corpus/ASan output structure:
==1234==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x7f...
READ of size 4 at 0x7f... thread T0
#0 0x4015a3 in process_input src/parser.c:42:5
#1 0x4012f0 in LLVMFuzzerTestOneInput fuzz_target.cc:10:3
...
0x7f... is located 0 bytes to the right of 16-byte region 0x7f..., 0x7f...)
allocated by thread T0 here:
#0 0x40e7c0 in __interceptor_malloc
#1 0x4015a3 in process_input src/parser.c:39:9Key fields:
heap-buffer-overflow, stack-use-after-return,
use-after-free, double-free, memory-leakParse this for the from-CI-failure workflow in bug-report-template
(qa-bug-repro plugin) to extract the failure assertion.
| Language | ASan | UBSan | MSan | TSan | LSan |
|---|---|---|---|---|---|
| C / C++ (clang / GCC) | ✓ | ✓ | ✓ (clang) | ✓ | ✓ |
| Rust (nightly) | ✓ | ✓ | ✓ | ✓ | ✓ |
| Go | partial (race detector for TSan-equivalent) | - | - | ✓ | - |
| Swift | ✓ | ✓ | - | ✓ | ✓ |
| Objective-C | ✓ | ✓ | - | ✓ | ✓ |
Java / Kotlin (Jazzer) uses JVM-level sanitisers (sanitisers for unsafe-API misuse, deserialisation gadgets, ReDoS) rather than clang's; see jazzer.md.
Python (Atheris) uses per-module instrumentation + the host process's libFuzzer; you can attach ASan to the Python interpreter itself.
A team fuzzes a C++ PNG parser. They choose ASan + UBSan (the standard pair) and build with:
clang -g -O1 -fsanitize=fuzzer,address,undefined \
-fno-sanitize-recover=all -fno-omit-frame-pointer \
png_fuzzer.cc -o png_fuzzerRunning under ASAN_OPTIONS=abort_on_error=1:halt_on_error=1, the fuzzer trips
within minutes. The report opens with heap-buffer-overflow ... READ of size 4,
top frame process_input src/parser.c:42, allocated at src/parser.c:39 (a
16-byte region). The bug class plus the allocation site pin it to an off-by-one in
the chunk-length handling. The parser has no MSan dependency requirement, so they
skip the separate MSan binary and hand the report to the from-CI-failure workflow in bug-report-template.
| Anti-pattern | Why it fails | Fix |
|---|---|---|
| Fuzzing without sanitisers | Catches only crashes; misses 80%+ of memory bugs | Always build with ASan + UBSan minimum |
-fsanitize=address,memory together | MSan + ASan incompatible | Pick one; run separate binaries |
| MSan with non-MSan dependencies | False positives flood the report | Build all dependencies with MSan or skip MSan |
UBSan without -fno-sanitize-recover=all | UBSan logs but doesn't abort; fuzzer never sees the bug | Always add -fno-sanitize-recover=all |
ASan without -fno-omit-frame-pointer | Stack traces are useless | Always add -fno-omit-frame-pointer -g |
detect_leaks=0 in fuzz CI | Leak bugs go unnoticed | Default ASan settings (Linux LSan-enabled) |
| TSan + a non-thread-safe target | Slow + noisy; data races are everywhere | Pick targets where thread-safety claims are made |
-DCMAKE_C_FLAGS=-fsanitize=...).What it detects (per clang.llvm.org/docs/AddressSanitizer.html):
Build flag: -fsanitize=address -fno-omit-frame-pointer -g
Performance: "Typical slowdown introduced by AddressSanitizer is 2x" per the docs.
Runtime options (ASAN_OPTIONS=key=value:...):
| Option | Effect |
|---|---|
detect_leaks=1 | Enable leak detection (default on Linux) |
detect_stack_use_after_return=0 | Disable use-after-return checks (faster) |
detect_container_overflow=0 | Disable container-overflow detection |
symbolize=0 | Disable online symbolization (use post-mortem) |
check_initialization_order=1 | Init-order checking |
halt_on_error=1 | Stop on first error |
abort_on_error=1 | SIGABRT on error (for fuzzers) |
What it detects: signed integer overflow, division by zero, null pointer deref, misaligned access, float-int conversion overflow, invalid enum / bool, vptr corruption, function-pointer type mismatch, etc.
Build flag: -fsanitize=undefined -fno-sanitize-recover=all
The -fno-sanitize-recover=all is important for fuzzing - without
it, UBSan logs but doesn't abort, so the fuzzer doesn't see the
bug.
Performance: ~10% slowdown - much lighter than ASan.
Runtime options (UBSAN_OPTIONS):
print_stacktrace=1 - include stack trace in reportshalt_on_error=1 - abort on first errorWhat it detects: Uninitialised memory reads.
Build flag: -fsanitize=memory -fno-omit-frame-pointer -fsanitize-memory-track-origins
Performance: 3x slowdown.
Critical: MSan requires the entire program (and all
dependencies) to be built with -fsanitize=memory. Linking
against non-MSan-instrumented libraries produces false positives.
Compatibility: MSan is incompatible with ASan; cannot combine.
What it detects: Data races, deadlocks, thread-safety violations.
Build flag: -fsanitize=thread -O1 -g
Performance: 5 - 15x slowdown + 5 - 10x memory.
Compatibility: TSan is incompatible with ASan and MSan.
What it detects: Memory leaks at program exit.
Modes:
-fsanitize=address enables LSan by default
on Linux. Toggle via detect_leaks=1.-fsanitize=leak - leak detection only, no other
checks. Smaller overhead.