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.
75
94%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
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.
-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:
references/sanitiser-catalog.md.
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 bug-report-from-failure
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-jvm-fuzzing.
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 bug-report-from-failure.
| 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=...).corpus-management-reference.libfuzzer-cpp,
afl-plus-plus,
cargo-fuzz-rust,
atheris-python-fuzzing,
jazzer-jvm-fuzzing,
ossfuzz-integration.