Sanitizers: ASan, UBSan, TSan, MSan
This chapter covers the four major sanitizers: AddressSanitizer, Undefined Behavior Sanitizer, ThreadSanitizer, and MemorySanitizer. They instrument your code to catch bugs at run time that would otherwise be invisible.
Why This Matters
Sanitizers turn silent memory corruption and undefined behavior into clear, actionable reports. They are the single most effective way to find the bugs that "only happen sometimes" or "only in release."
Prerequisites
c.debug.1— debuggers.c.build.8— sanitizer flags.
Core Concept
A sanitizer instruments the program (via compiler flags) to add runtime checks or tracking. When a violation is detected, it prints a report and aborts.
| Sanitizer | Detects | Flag |
|---|---|---|
| ASan | buffer overflow, use-after-free, double free, leaks (with -fsanitize=address) | -fsanitize=address |
| UBSan | signed overflow, invalid shift, misaligned access, etc. | -fsanitize=undefined |
| TSan | data races | -fsanitize=thread |
| MSan | uninitialized memory reads | -fsanitize=memory (Clang) |
Examples
ASan
cc -g -fsanitize=address main.c -o app
./app
A heap overflow produces a report with the offending access, the allocation site, and a stack trace.
UBSan
cc -g -fsanitize=undefined main.c -o app
./app
Signed overflow produces: runtime error: signed integer overflow: ....
TSan
cc -g -fsanitize=thread -pthread main.c -o app
./app
A data race produces a report showing the two conflicting accesses.
How It Works
ASan uses shadow memory: it maps every 8 bytes of application memory to a shadow byte describing its state (addressable/poisoned). Instrumented loads/ stores check the shadow. UBSan inserts checks at operations that can be UB. TSan tracks happens-before relations and detects conflicting accesses. MSan tracks which memory is initialized.
Variations
Combining sanitizers
ASan and UBSan combine well (-fsanitize=address,undefined). TSan is generally not combined with ASan (different runtimes).
LeakSanitizer
ASan includes leak detection (or use -fsanitize=leak).
Common Mistakes
- Not using sanitizers because of the performance cost during development.
- Ignoring sanitizer reports as "false positives" (they are almost always real
bugs).
- Combining incompatible sanitizers.
Undefined Behavior
- Sanitizers *detect* UB; they do not change that the code is UB. A sanitized
build still aborts on the first violation.
Portability
- ASan/UBSan are available on GCC and Clang (Linux/macOS). TSan and MSan are
primarily Clang (TSan also on GCC Linux). MSVC has /fsanitize=address.
Under the Hood
ASan's shadow-memory model is the classic design. UBSan emits a branch check before each risky operation. TSan maintains a shadow of memory access metadata and a happens-before graph. MSan tracks definedness bit-by-bit.
Practical Usage
- Run all tests under ASan + UBSan.
- Run concurrency tests under TSan.
- Use MSan (Clang) to find uninitialized reads.
- Investigate every report, not just crashes.
Exercises
1. Write a program with a heap buffer overflow and run it under ASan. 2. Write a program with signed overflow and run it under UBSan. 3. Write a program with a data race and run it under TSan. 4. (Clang) Write a program reading uninitialized memory and run it under MSan.
Deep Challenge
Explain how ASan's shadow memory detects a use-after-free: what happens to the shadow bytes at free, and why the subsequent access traps. Then discuss the overhead and limitations.
Related Concepts
c.ub.catalog— the UB being detected.c.debug.4— Valgrind and static analysis.c.build.8— sanitizer flags.
References
- AddressSanitizer paper/docs, UBSan docs, TSan docs, MSan docs.
Verification
- Sanitizer flags and behaviors are documented tool features.
VERIFIED - No example was executed during generation unless noted. `Execution not
verified.`
Progress
- [ ] Read
- [ ] Understand
- [ ] Complete examples
- [ ] Complete exercises
- [ ] Complete deep challenge
Concept checkboxes
- [ ] AddressSanitizer
- [ ] UndefinedBehaviorSanitizer
- [ ] ThreadSanitizer
- [ ] MemorySanitizer
Mastery levels
| Concept | Current level (0–8) | Target level |
|---|---|---|
| c.debug.asan | 0 | 6 |
| c.debug.ubsan | 0 | 6 |
| c.debug.tsan | 0 | 5 |
| c.debug.msan | 0 | 5 |