Safeguard
Developer Tools•

A starter set of C and C++ hardening flags from the OpenSSF guide

The OpenSSF compiler options guide lists a core set of flags that make memory bugs harder to exploit. What each does, in a copy ready starter block.

Safeguard Security Team
4 min read

The OpenSSF Compiler Options Hardening Guide for C and C++ gives a short core set of flags: optimization at level two, warnings, fortify source at level three, stack protection, stack clash protection, position independent executables and full RELRO. They reduce exploitability, but do not make C code memory safe.

A starter block for GCC or Clang

CFLAGS="-O2 -Wall -Wformat -Wformat=2 -Wconversion -Wimplicit-fallthrough \
  -Werror=format-security \
  -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=3 \
  -fstrict-flex-arrays=3 \
  -fstack-clash-protection -fstack-protector-strong \
  -fPIE"
LDFLAGS="-pie -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now"

For C++ add -D_GLIBCXX_ASSERTIONS with libstdc++, or the libc++ hardening macro covered in a separate post.

What each does

  • -O2. Needed because fortify source requires at least level one optimization.
  • Warning flags. Surface suspicious conversions, format string misuse and silent switch fallthrough.
  • -Werror=format-security. Turns non literal format strings used without arguments into errors.
  • Fortify source 3. Adds compile time and run time checks on unsafe libc calls and buffer overflows. The leading undefine clears any predefined level.
  • -fstrict-flex-arrays=3. Treats only trailing arrays declared with empty brackets as flexible, giving the compiler more bounds information.
  • Stack clash protection. Probes large stack allocations so they cannot jump over the guard gap.
  • Stack protector strong. Adds canaries to detect stack buffer overflows.
  • PIE. Lets address space layout randomization apply to the executable.
  • Non executable stack and full RELRO. Block code on the stack and make the global offset table read only after load.

Architecture specific extras

On x86_64 the guide lists -fcf-protection=full for control flow enforcement, which needs kernel 6.6 or later and glibc 2.39 or later for the shadow stack. On AArch64 it lists -mbranch-protection=standard for branch target identification and pointer authentication.

What these flags do not do

They add checks and raise the cost of exploitation. They do not remove the bug, and they cannot stop a logic error that reads inside a valid buffer. Think of them as an interim control while you work through a memory safety roadmap.

Verifying the result

A flag in a Makefile does not prove the binary has the protection. Build systems drop variables, third party code ignores them, and linkers can behave differently across platforms. Add a post build check to CI that inspects the produced binaries for the protections you expect, such as position independence, a non executable stack and full RELRO, and fail the build when one is missing.

Keep a small table in your repository with one row per component: which flags it uses, which it is exempt from and the reason. When a toolchain upgrade lands, the table tells you what to retest. The guide also notes some flags need a minimum compiler version, for example zeroing used registers needs Clang 16 or later, so pin your toolchain versions in the same place.

Common pitfalls

  • Setting fortify source without optimization, which leaves it with no effect because it needs at least level one.
  • Forgetting to undefine a predefined fortify level before setting the new one.
  • Applying executable flags to libraries, or the reverse.
  • Treating the flags as a reason to postpone rewriting exposed parsers.

Rolling it out

  1. Enable warnings first and clean up.
  2. Add the runtime hardening flags on a branch and run the full test suite.
  3. Compare binary size and benchmark results.
  4. Gate releases on a script that checks the final binary for the expected protections.

Quick check

Can I combine fortify source with AddressSanitizer?

The guide says they are incompatible, so leave fortify source off in sanitizer test builds.

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.