Home C / C++ Sanitizers Deep-Dive: ASan, UBSan, TSan, MSan in C++
Advanced 3 min · July 13, 2026

Sanitizers Deep-Dive: ASan, UBSan, TSan, MSan in C++

Master AddressSanitizer, UBSan, TSan, and MSan in C++.

N
Naren Founder & Principal Engineer

20+ years shipping performance-critical C and C++ systems. Lessons pulled from things that broke in production.

Follow
Production
production tested
July 13, 2026
last updated
2,073
articles · all by Naren
Before you start⏱ 15-20 min read
  • Basic knowledge of C++ syntax and compilation
  • Understanding of pointers and dynamic memory allocation
  • Familiarity with multithreading concepts (for TSan)
  • A compiler supporting sanitizers (GCC 4.8+ or Clang 3.1+)
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • AddressSanitizer (ASan) detects buffer overflows, use-after-free, and memory leaks.
  • UndefinedBehaviorSanitizer (UBSan) catches integer overflow, null pointer dereference, and other UB.
  • ThreadSanitizer (TSan) detects data races in multithreaded code.
  • MemorySanitizer (MSan) identifies reads of uninitialized memory.
  • Enable with compiler flags like -fsanitize=address and link with -fsanitize=address.
✦ Definition~90s read
What is AddressSanitizer, UBSan, TSan, and MSan Deep-Dive?

Sanitizers are compile-time instrumentation tools that detect memory errors, undefined behavior, data races, and uninitialized memory reads in C++ programs.

Think of sanitizers as a security camera system for your code.
Plain-English First

Think of sanitizers as a security camera system for your code. ASan watches for memory misuse like a guard checking IDs at a door. UBSan is a rulebook enforcer that flags when you break the rules. TSan monitors conversations between threads to prevent miscommunication. MSan checks if you're using data before it's properly set, like reading a blank page.

⚙ Browser compatibility
Latest versions — ✓ supported
ChromeFirefoxSafariEdge

Memory bugs, undefined behavior, data races, and uninitialized memory are the silent killers of C++ applications. They lurk in production, causing crashes, security vulnerabilities, and unpredictable behavior. Traditional debugging tools often fail to catch these issues until they manifest in catastrophic ways. Enter the sanitizers: AddressSanitizer (ASan), UndefinedBehaviorSanitizer (UBSan), ThreadSanitizer (TSan), and MemorySanitizer (MSan). These compiler-integrated tools instrument your code at compile time to detect a wide range of bugs during runtime. They are fast, precise, and have become essential in modern C++ development. In this deep-dive, you'll learn how to use each sanitizer, understand their output, and integrate them into your build and CI pipelines. We'll cover real-world scenarios, common pitfalls, and best practices. By the end, you'll be equipped to write safer, more reliable C++ code.

What Are Sanitizers?

Sanitizers are runtime tools integrated into compilers like GCC and Clang that instrument code to detect specific classes of bugs. They work by adding checks at compile time that verify memory accesses, arithmetic operations, thread synchronization, and memory initialization. Each sanitizer focuses on a different category: AddressSanitizer (ASan) for memory errors, UndefinedBehaviorSanitizer (UBSan) for undefined behavior, ThreadSanitizer (TSan) for data races, and MemorySanitizer (MSan) for uninitialized memory reads. They are designed to be fast (typically 2x slowdown) and provide detailed error reports including stack traces. Sanitizers are not a replacement for unit tests but complement them by catching bugs that are hard to test for. They are especially valuable in C++ where manual memory management and low-level operations are common.

sanitizer_intro.cppCPP
1
2
3
4
5
6
7
8
// Example: ASan detecting a heap buffer overflow
#include <iostream>
int main() {
    int* arr = new int[10];
    arr[10] = 42; // overflow
    delete[] arr;
    return 0;
}
Output
==1==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x...
WRITE of size 4 at 0x... thread T0
#0 0x... in main ...
#1 0x... in _start ...
0x... is located 0 bytes after 40-byte region [0x...,0x...)
freed by thread T0 here:
#0 0x... in operator delete[]
#1 0x... in main
previously allocated by thread T0 here:
#0 0x... in operator new[]
#1 0x... in main
🔥Sanitizers are not for production
📊 Production Insight
In production, use sanitizers in a canary environment or during integration tests to catch bugs before release.
🎯 Key Takeaway
Sanitizers are compile-time instrumentation tools that catch specific bug categories at runtime with detailed reports.

AddressSanitizer (ASan): Detecting Memory Errors

AddressSanitizer is the most widely used sanitizer. It detects heap buffer overflows, stack buffer overflows, global buffer overflows, use-after-free, use-after-return, double free, and memory leaks. ASan works by shadow memory: it maps every 8 bytes of application memory to one shadow byte that encodes whether the memory is accessible. On each memory access, the compiler inserts a check against the shadow memory. If the check fails, ASan reports an error with a stack trace. To use ASan, compile with -fsanitize=address and link with the same flag. For C++ programs, also add -fsanitize=address -fno-omit-frame-pointer for better stack traces. Example: detecting a use-after-free.

asan_use_after_free.cppCPP
1
2
3
4
5
6
7
8
#include <iostream>
int main() {
    int* p = new int(42);
    delete p;
    *p = 10; // use-after-free
    std::cout << *p << std::endl;
    return 0;
}
Output
==1==ERROR: AddressSanitizer: heap-use-after-free on address 0x...
WRITE of size 4 at 0x... thread T0
#0 0x... in main ...
#1 0x... in _start
0x... is located 0 bytes inside of 4-byte region [0x...,0x...)
freed by thread T0 here:
#0 0x... in operator delete
#1 0x... in main
previously allocated by thread T0 here:
#0 0x... in operator new
#1 0x... in main
💡Leak detection
📊 Production Insight
ASan can be combined with fuzzing (e.g., libFuzzer) to automatically discover memory bugs.
🎯 Key Takeaway
ASan catches memory errors like buffer overflows and use-after-free with precise stack traces.

UndefinedBehaviorSanitizer (UBSan): Catching UB

Undefined behavior (UB) is a notorious source of bugs in C++. The C++ standard leaves many operations undefined, meaning the compiler can assume they never happen and optimize accordingly. UBSan instruments code to detect common UB at runtime, such as integer overflow, null pointer dereference, misaligned access, invalid shifts, and more. To enable UBSan, compile with -fsanitize=undefined. You can also enable specific checks like -fsanitize=integer or -fsanitize=null. UBSan reports the exact location and type of UB. Example: integer overflow.

ubsan_integer_overflow.cppCPP
1
2
3
4
5
6
7
8
#include <iostream>
#include <climits>
int main() {
    int x = INT_MAX;
    x++; // overflow
    std::cout << x << std::endl;
    return 0;
}
Output
ubsan_integer_overflow.cpp:5:5: runtime error: signed integer overflow: 2147483647 + 1 cannot be represented in type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior ubsan_integer_overflow.cpp:5:5 in
-2147483648
⚠ UBSan can be noisy
📊 Production Insight
UBSan is lightweight (often <2x slowdown) and can be used in testing to ensure no UB escapes to production.
🎯 Key Takeaway
UBSan detects undefined behavior at runtime, helping you write more portable and predictable code.

ThreadSanitizer (TSan): Detecting Data Races

Data races occur when two threads access the same memory location without synchronization, and at least one access is a write. TSan instruments every memory access and tracks the happens-before relationship using vector clocks. When it detects a race, it reports the two conflicting accesses and the threads involved. To use TSan, compile with -fsanitize=thread and link with -lpthread. TSan requires all code to be compiled with the flag, including libraries. Example: a simple data race.

tsan_data_race.cppCPP
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
#include <thread>
int counter = 0;
void increment() {
    for (int i = 0; i < 100000; ++i)
        counter++;
}
int main() {
    std::thread t1(increment);
    std::thread t2(increment);
    t1.join();
    t2.join();
    std::cout << counter << std::endl;
    return 0;
}
Output
==================
WARNING: ThreadSanitizer: data race (pid=...)
Write of size 4 at 0x... by thread T2:
#0 increment() ...
Previous write of size 4 at 0x... by thread T1:
#0 increment() ...
Location is global 'counter' of size 4 at 0x...
Thread T2 (tid=..., running) created by main at:
#0 ...
Thread T1 (tid=..., running) created by main at:
#0 ...
SUMMARY: ThreadSanitizer: data race ...
🔥TSan and signal handlers
📊 Production Insight
TSan can be used in stress tests to uncover races that only occur under specific interleavings.
🎯 Key Takeaway
TSan detects data races in multithreaded code, helping you write correct concurrent programs.

MemorySanitizer (MSan): Uninitialized Memory Reads

Reading uninitialized memory leads to undefined behavior. MSan detects when a program reads memory that has not been initialized. It works by tracking the initialization state of every bit of memory using shadow memory. When an uninitialized value is used in a way that affects program behavior (e.g., branching, output), MSan reports an error. To use MSan, compile with -fsanitize=memory -fsanitize-memory-track-origins (for origin tracking). MSan requires all code to be compiled with the flag, including libraries. Example: reading an uninitialized local variable.

msan_uninitialized.cppCPP
1
2
3
4
5
6
7
#include <iostream>
int main() {
    int x;
    if (x > 0) // uninitialized read
        std::cout << "positive" << std::endl;
    return 0;
}
Output
==1==WARNING: MemorySanitizer: use-of-uninitialized-value
#0 0x... in main ...
Uninitialized value was created by an allocation of 'x' in the stack frame
#0 0x... in main ...
SUMMARY: MemorySanitizer: use-of-uninitialized-value ...
💡Origin tracking
📊 Production Insight
MSan is particularly useful for detecting bugs in complex codebases where initialization is not obvious.
🎯 Key Takeaway
MSan catches reads of uninitialized memory, preventing hard-to-debug non-deterministic behavior.

Combining Sanitizers and Best Practices

Sanitizers can be combined, but not all combinations work together. ASan and UBSan can be used together: -fsanitize=address,undefined. TSan and ASan are incompatible because both instrument memory accesses. MSan also conflicts with ASan and TSan. For maximum coverage, run separate builds with different sanitizers. Best practices: 1) Always compile with -g -fno-omit-frame-pointer for better stack traces. 2) Use sanitizers in your CI pipeline on every commit. 3) For large codebases, start with ASan and UBSan as they have lower overhead. 4) Use suppression files to ignore known false positives. 5) Combine with fuzzing for automated bug discovery. Example: combining ASan and UBSan.

combined_sanitizers.cppCPP
1
2
3
4
5
6
7
8
9
// Compile with: g++ -fsanitize=address,undefined -g -o combined combined.cpp
#include <iostream>
#include <cstring>
int main() {
    char buf[10];
    strcpy(buf, "Hello, world!"); // buffer overflow
    int x = 1 << 31; // undefined shift
    return 0;
}
Output
==1==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x...
WRITE of size 14 at 0x... thread T0
#0 0x... in __interceptor_strcpy ...
#1 0x... in main ...
...
combined.cpp:7:13: runtime error: shift exponent 31 is too large for 32-bit type 'int'
SUMMARY: UndefinedBehaviorSanitizer: undefined-behavior combined.cpp:7:13 in
⚠ Performance impact
📊 Production Insight
In production, consider using sanitizers in a canary deployment to catch bugs that only appear under real workloads.
🎯 Key Takeaway
Combine sanitizers wisely; use separate builds for incompatible ones and integrate into CI.
● Production incidentPOST-MORTEMseverity: high

The Phantom Crash: A Use-After-Free in a Web Server

Symptom
Random segmentation faults under high load, with no reproducible test case.
Assumption
The team suspected a race condition in the request handler.
Root cause
A use-after-free bug: a pointer to a request object was stored in a cache after the object was deleted.
Fix
Enabled AddressSanitizer in debug builds, which immediately pinpointed the invalid memory access. The fix was to clear the cache entry upon deletion.
Key lesson
  • Always run sanitizers in test and staging environments.
  • Use ASan to catch use-after-free and buffer overflows early.
  • Sanitizers can find bugs that are nearly impossible to reproduce manually.
  • Integrate sanitizers into CI to prevent regressions.
  • Combine sanitizers with fuzzing for maximum coverage.
Production debug guideSymptom to Action4 entries
Symptom · 01
Segmentation fault with no clear pattern
Fix
Enable AddressSanitizer and reproduce the crash.
Symptom · 02
Intermittent data corruption in multithreaded code
Fix
Run ThreadSanitizer to detect data races.
Symptom · 03
Unexpected integer overflow or division by zero
Fix
Enable UndefinedBehaviorSanitizer.
Symptom · 04
Non-deterministic behavior with uninitialized variables
Fix
Use MemorySanitizer to find uninitialized reads.
★ Quick Debug Cheat SheetQuick reference for common sanitizer scenarios.
Buffer overflow
Immediate action
Compile with -fsanitize=address
Commands
g++ -fsanitize=address -g -o test test.cpp
./test
Fix now
Check array bounds and use std::array or std::vector.
Use-after-free+
Immediate action
Compile with -fsanitize=address
Commands
g++ -fsanitize=address -g -o test test.cpp
./test
Fix now
Set pointer to nullptr after delete.
Integer overflow+
Immediate action
Compile with -fsanitize=undefined
Commands
g++ -fsanitize=undefined -g -o test test.cpp
./test
Fix now
Use checked arithmetic or saturating operations.
Data race+
Immediate action
Compile with -fsanitize=thread
Commands
g++ -fsanitize=thread -g -o test test.cpp -lpthread
./test
Fix now
Add mutex or use atomic variables.
Uninitialized memory read+
Immediate action
Compile with -fsanitize=memory
Commands
g++ -fsanitize=memory -g -o test test.cpp
./test
Fix now
Initialize variables at declaration.
SanitizerDetectsCompile FlagOverheadCompatibility
ASanBuffer overflows, use-after-free, leaks-fsanitize=address~2x slowdownIncompatible with TSan, MSan
UBSanInteger overflow, null deref, invalid shift-fsanitize=undefined~1.5x slowdownCompatible with ASan
TSanData races-fsanitize=thread~5x slowdownIncompatible with ASan, MSan
MSanUninitialized memory reads-fsanitize=memory~3x slowdownIncompatible with ASan, TSan
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
sanitizer_intro.cppint main() {What Are Sanitizers?
asan_use_after_free.cppint main() {AddressSanitizer (ASan)
ubsan_integer_overflow.cppint main() {UndefinedBehaviorSanitizer (UBSan)
tsan_data_race.cppint counter = 0;ThreadSanitizer (TSan)
msan_uninitialized.cppint main() {MemorySanitizer (MSan)
combined_sanitizers.cppint main() {Combining Sanitizers and Best Practices

Key takeaways

1
Sanitizers (ASan, UBSan, TSan, MSan) are powerful tools to detect memory errors, undefined behavior, data races, and uninitialized memory reads.
2
Enable sanitizers with compiler flags like -fsanitize=address and always use -g for debug symbols.
3
Sanitizers are not for production; use them in testing and CI to catch bugs early.
4
Combine sanitizers wisely
ASan+UBSan works together; TSan and MSan require separate builds.
5
Integrate sanitizers into your development workflow to improve code reliability and security.

Common mistakes to avoid

5 patterns
×

Forgetting to link with -fsanitize=address when compiling with ASan

×

Using TSan without compiling all code (including libraries) with -fsanitize=thread

×

Ignoring UBSan warnings about signed integer overflow

×

Running sanitizers in release mode without debug symbols

×

Assuming sanitizers catch all bugs

INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
What is AddressSanitizer and how does it detect heap buffer overflows?
Q02SENIOR
Explain the difference between ASan and MSan.
Q03SENIOR
How would you debug a data race in a multithreaded C++ application?
Q04SENIOR
What are the limitations of UBSan?
Q05SENIOR
Can you combine TSan and ASan? Why or why not?
Q01 of 05SENIOR

What is AddressSanitizer and how does it detect heap buffer overflows?

ANSWER
AddressSanitizer (ASan) is a runtime memory error detector. It uses shadow memory to track the validity of each byte. On every memory access, it checks the shadow byte; if the access is invalid, it reports an error with a stack trace.
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
Can I use sanitizers in production?
02
How do I suppress false positives in UBSan?
03
Why does TSan report a race on std::cout?
04
Can I use ASan with dynamic libraries?
05
How do I detect memory leaks with ASan?
N
Naren Founder & Principal Engineer

20+ years shipping performance-critical C and C++ systems. Lessons pulled from things that broke in production.

Follow
Verified
production tested
July 13, 2026
last updated
2,073
articles · all by Naren
🔥

That's C++ Advanced. Mark it forged?

3 min read · try the examples if you haven't

Previous
C/C++ Interop and Foreign Function Interface
34 / 41 · C++ Advanced
Next
GoogleTest, Catch2, and doctest in C++