Safeguard
Developer Tools•

libc++ hardening modes: fast, extensive or debug, which to pick

libc++ can check preconditions such as valid element access at run time. The four hardening modes, what each adds, and which one fits production.

Safeguard Security Team
3 min read

libc++ has four hardening modes: none, fast, extensive and debug. The LLVM documentation intends fast mode for production, with low constant time overhead, extensive for cases you have benchmarked, and debug for testing only. Fast mode turns out-of-bounds element access in standard containers into an immediate trap.

Choosing the mode

You set it at compile time:

clang++ -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST app.cpp

The other values are _LIBCPP_HARDENING_MODE_NONE, _LIBCPP_HARDENING_MODE_EXTENSIVE and _LIBCPP_HARDENING_MODE_DEBUG. Pass only one. Library vendors choose the default, and the vendor default is none, meaning no checks.

What each mode adds

  • None. Adds nothing and has no overhead.
  • Fast. Adds valid element access and valid input range checks. Intended for production, with low constant time cost.
  • Extensive. Adds to fast checks such as non-null pointers, non-overlapping ranges, valid deallocation and argument domain. Benchmark before production.
  • Debug. Adds to extensive the semantic requirement and internal checks. For testing and development only.

Each mode is currently a superset of the one before it, though the library may change which assertions each mode enables.

What failure looks like

Fast and extensive modes trap at once with no message. Debug mode terminates and prints the error message. That difference matters for support: a production trap needs a crash report pipeline, and a debug build is where you find out which assertion fired.

Compatibility notes

The documentation says choosing a hardening mode does not affect the ABI. Some checks, such as bounds checking on dereferencing a span iterator, depend on ABI options that the vendor sets when building the library, and those can change class layout. Because the compiled library comes from your vendor, your macro mainly affects header based code.

A rollout path

  1. Build tests with debug mode and fix every assertion.
  2. Switch CI to extensive and measure runtime.
  3. Ship fast mode in production builds of network facing components.
  4. Revisit extensive after benchmarking real workloads.

Where it helps and where it does not

It adds checks to standard library calls, so it catches a bad index into a vector or a mismatched input range. It does not see raw pointer arithmetic in your own code, or lifetime bugs such as a dangling reference. The OpenSSF guide also lists the fast mode macro among its core flags.

Testing the hardening itself

Write a small test that indexes one past the end of a vector in a throwaway build, and confirm the program traps in fast mode instead of reading garbage. If it does not trap, your build is not using the mode you think it is. The ABI tag the documentation describes encodes the mode in its first character, which can help you confirm what was compiled, though the documentation says the tag format is not stable across releases.

Also decide what trapping means for your service. A fast mode trap ends the process with no message. That is the right behavior for an exposed parser, as long as your supervisor restarts it and your crash reporter captures the stack. If you have no crash pipeline yet, build that first, otherwise you will trade a hidden memory bug for an unexplained outage.

Quick check

Is hardening mode none the vendor default?

Yes, according to the libc++ documentation, so you have to opt in.

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.