C++ Runtime Sanitizers
ASan, UBSan, TSan, MSan, and LSan — what each catches, runtime overhead, and how to layer them for maximum coverage.
The golden rule of sanitizers
Enable -fsanitize=address,undefined on every debug and CI build. These two together catch the vast majority of C++ bugs at ~2-3× overhead — a cheap price for correctness.
Note: ASan and TSan are mutually exclusive — run in separate builds. MSan requires rebuilding all dependencies.
Comparison matrix
| Property | ASan | UBSan | TSan | MSan | LSan |
|---|---|---|---|---|---|
| Compiler flag | address | undefined | thread | memory | leak |
| CPU overhead | ~2× | ~1.2× | ~5-15× | ~3× | ~1× |
| Memory overhead | ~2× | ~1× | ~5-10× | ~3× | ~1.1× |
| GCC | |||||
| Clang | |||||
| MSVC | Partial (/RTC1) |
-fsanitize=addressThe most important sanitizer. Catches heap/stack/global buffer overflows, use-after-free, and use-after-return.
What it catches
- Heap buffer overflow / underflow
- Stack buffer overflow
- Global buffer overflow
- Use-after-free (dangling pointer reads/writes)
- Use-after-return
- Use-after-scope
- Double free / invalid free
Does NOT catch
- Uninitialized reads (use MSan)
- Data races (use TSan)
- Integer overflow (use UBSan)
-fsanitize=undefinedCatches C++ undefined behavior at runtime: signed overflow, null deref, bad casts, misaligned access.
What it catches
- Signed integer overflow
- Null pointer dereference
- Invalid downcast (dynamic_cast without check)
- Misaligned pointer access
- Shift-count overflow
- Division by zero
- Returning from non-void function
- VLA bounds violation
Does NOT catch
- Buffer overflows (use ASan)
- Data races (use TSan)
- Uninitialized reads (use MSan)
-fsanitize=threadDetects data races at runtime. The only practical tool for finding concurrent memory bugs without heavy analysis.
What it catches
- Data races (concurrent read/write without synchronization)
- Lock order inversions (potential deadlock)
- Misuse of std::mutex / std::atomic
- Use of uninitialized mutex
Does NOT catch
- Buffer overflows (use ASan)
- UB (use UBSan)
- All logical race conditions
-fsanitize=memoryCatches reads of uninitialized memory — the silent source of non-deterministic bugs. Clang-only.
What it catches
- Read of uninitialized stack/heap memory
- Conditional branches on uninitialized values
- Passing uninitialized data to system calls
Does NOT catch
- Buffer overflows (use ASan)
- Data races (use TSan)
- UB (use UBSan)
-fsanitize=leakMemory leak detector — bundled with ASan (enabled by default). Run standalone for near-zero overhead.
What it catches
- Heap memory leaks at program exit
- Indirect leaks (leaked objects that own other leaks)
Does NOT catch
- Stack leaks (there are none)
- Buffer overflows (use ASan)
- Data races (use TSan)
CMake integration
option(ENABLE_ASAN "Enable AddressSanitizer + UBSan" OFF)
option(ENABLE_TSAN "Enable ThreadSanitizer" OFF)
if(ENABLE_ASAN)
target_compile_options(myapp PRIVATE
-fsanitize=address,undefined -fno-omit-frame-pointer -g)
target_link_options(myapp PRIVATE
-fsanitize=address,undefined)
endif()
if(ENABLE_TSAN)
target_compile_options(myapp PRIVATE -fsanitize=thread -g)
target_link_options(myapp PRIVATE -fsanitize=thread)
endif()Build with: cmake -DENABLE_ASAN=ON ..
Deep-dive guides
Deep dive into AddressSanitizer — use-after-free, heap buffer overflow, stack overflow, global overflow, use-after-return, leak sanitizer, and reading reports.
ASan, UBSan, TSan, MSan — compile-time instrumentation that catches memory errors, undefined behavior, and data races at runtime.