Safeguard
Developer Tools•

ASan, UBSan, MSan and TSan: which sanitizer finds which bug

Four Clang sanitizers, four bug families: memory errors, undefined behavior, uninitialized reads, and data races. What each costs and how to combine them.

Safeguard Security Team
3 min read

Each Clang sanitizer watches for one family of bug. AddressSanitizer finds memory errors, UndefinedBehaviorSanitizer finds undefined behavior such as signed overflow, MemorySanitizer finds uninitialized reads, and ThreadSanitizer finds data races. You usually cannot stack them all in one build, so you run several builds.

The four at a glance

  • AddressSanitizer (-fsanitize=address): out-of-bounds access, use-after-free, double free. About 2x slowdown.
  • UBSan (-fsanitize=undefined): undefined behavior such as overflow, bad shifts and null use. Small runtime cost.
  • MemorySanitizer (-fsanitize=memory): reads of uninitialized memory. About 3x slowdown.
  • ThreadSanitizer (-fsanitize=thread): data races. About 5x to 15x slowdown.

The figures come from the Clang documentation for each tool.

UBSan: the cheap one to add

UBSan instruments code at compile time with a small runtime cost. Its checks include signed integer overflow, shifts, null pointer use, misaligned access, out-of-bounds array indexing and more. One detail trips people up: the undefined group does not include every check. The docs say it leaves out float-divide-by-zero, unsigned-integer-overflow, vptr and the nullability checks, so enable those by name if you want them.

By default most checks print a report and keep going. For fuzzing, add -fno-sanitize-recover so the first failure stops the run and gets saved as a crash.

MSan: powerful but demanding

MemorySanitizer detects uninitialized values used in branches, pointer accesses, or passed to and returned from functions. Its big requirement, in the docs' words: all program code must be instrumented, including libraries. A single uninstrumented dependency can cause false reports. That is why OSS-Fuzz lists the memory sanitizer as recommended but opt-in, requiring every dependency to be built with it.

Origin tracking, enabled with -fsanitize-memory-track-origins, shows where an uninitialized value came from, at an added cost of roughly 1.5x to 2x on top of the base slowdown.

TSan: for concurrent code

ThreadSanitizer finds data races, with documented overhead of about 5x to 15x time and 5x to 10x memory. It is marked beta in the docs and needs all code built with it for reliable results. Use it on tests that exercise threads, not on a parser fuzz target that runs single threaded.

How to combine them

  • ASan and UBSan go together well, and many fuzzing setups use them as the default pair. OSS-Fuzz also defaults to address and undefined.
  • MSan and TSan are separate builds. Do not try to link them with ASan.
  • Each build is its own CI job with its own corpus runs.

None of them is for production

The Clang pages for ASan, MSan and TSan all say the runtimes are not meant for production executables. UBSan's docs say the full runtime is for testing, and for security-sensitive production builds suggest the minimal runtime or trap mode.

Quick check

Which sanitizer should I add first?

AddressSanitizer plus UBSan. They cover the most common C and C++ bugs at the lowest cost.

Why does MSan report bugs that are not real?

Usually because a library it calls was not built with MSan. Instrument every dependency, or the tool cannot tell initialized memory from uninitialized.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.