Safeguard
Developer Tools•

UBSan in production: minimal runtime and trap mode explained

UndefinedBehaviorSanitizer has a full runtime for testing and two lighter options for production builds. What each one does, and what you give up.

Safeguard Security Team
3 min read

UndefinedBehaviorSanitizer instruments C and C++ code to catch undefined behavior such as bad shifts, null dereferences and out-of-bounds array use. Clang says the full runtime is meant for testing. For security sensitive production builds, it points to a minimal runtime or trap mode.

What UBSan checks

The Clang documentation groups checks by theme. A few that matter for memory bugs:

  • bounds, which covers array and local bounds
  • null and alignment for pointer problems
  • object-size and pointer-overflow
  • signed-integer-overflow, shift and integer-divide-by-zero for arithmetic
  • vptr for bad dynamic type use in C++

Not all of these are memory safety checks, but integer errors often feed length calculations that lead to buffer overruns.

Three ways to run it

Full runtime, for tests. Pass -fsanitize=undefined or a narrower list. You get readable diagnostics with file and line. The documentation describes it as the testing configuration.

Minimal runtime. Add -fsanitize-minimal-runtime. It has a small attack surface and gives basic issue logging and deduplication. It does not support vptr checking.

Trap mode. Use -fsanitize-trap=all or a list of checks. No sanitizer runtime library is linked. A violation ends the program, typically with an illegal instruction or trap signal. It also does not support vptr.

Picking one

  1. Test builds: full runtime, as many checks as the code base tolerates.
  2. Beta or staging builds where you want reports: minimal runtime.
  3. Production builds of exposed components: trap mode for a short list of cheap checks, such as bounds, after measuring the cost.

Plan for the crash

Trap mode turns a silent corruption into a crash. That is a good trade for a parser exposed to the network, but only if your supervisor restarts the process cleanly and you collect the crash. Decide in advance what happens to in flight requests.

Watch the cost

Every check adds instructions. The documentation does not publish a single overhead number for these options, so measure on your own workload and on your own hardware before turning a check on for everything. Start with the checks that guard memory indexes.

Which checks matter most for memory bugs

If you can only afford a few checks, think about what feeds memory accesses. The bounds check covers array indexing where the compiler knows the size. The null and alignment checks catch pointer misuse. The integer checks matter because a wrapped length or a bad shift often becomes the size of a later copy.

Signed integer overflow is undefined behavior in C and C++, so the compiler may assume it never happens and optimize code that depends on it. A sanitizer turns that silent assumption into a visible failure during tests, which is where you want to learn about it.

Rollout steps

  1. Run the full runtime over your tests and count the distinct findings.
  2. Fix or consciously suppress each one, with a ticket for every suppression.
  3. Pick the three or four cheapest checks that guard memory indexes.
  4. Build a canary release with trap mode for those checks and watch crash rates.
  5. Expand only if the crash data is clean and the cost is acceptable.

Quick check

Does trap mode print a readable diagnostic?

No. There is no runtime library to print one. The program terminates, so use a debug build to find out what failed.

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.