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.
Install with skills.sh (any agent)
npx skills add testland/qa --skill sanitiser-integration-referencesanitiser-integration-reference
Overview
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-language fuzzer skills and fuzz-target authoring. For corpus discipline see corpus-management-reference.
When to use
How to use
The five sanitisers
| 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: references/sanitiser-catalog.md.
Compatibility matrix
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.
libFuzzer + sanitiser composition
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 (opens in new window):
# 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/Reading a sanitiser report
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:
Parse this for bug-report-from-failure to extract the failure assertion.
Per-language sanitiser support
| 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-jvm-fuzzing.
Python (Atheris) uses per-module instrumentation + the host process's libFuzzer; you can attach ASan to the Python interpreter itself.
Worked example
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 bug-report-from-failure.
Anti-patterns
| 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 |
Limitations
References
Sanitiser catalog
View source (opens in new window)Sanitiser catalog
Per-sanitiser detail for the five clang sanitisers used with coverage-guided fuzz targets - what each detects, build flags, runtime options, and performance overhead. Per clang.llvm.org sanitiser docs.
AddressSanitizer (ASan)
What it detects (per clang.llvm.org/docs/AddressSanitizer.html (opens in new window)):
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) |
UndefinedBehaviorSanitizer (UBSan)
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):
MemorySanitizer (MSan)
What 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.
ThreadSanitizer (TSan)
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.
LeakSanitizer (LSan)
What it detects: Memory leaks at program exit.
Modes:
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.
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.
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.