Skip to content(if available)orjump to list(if available)

Hardening mode for the compiler

Hardening mode for the compiler

45 comments

·August 2, 2025

wyldfire

A really good accompaniment to this is Carruth's "C++, bounds checking, performance, and compilers" [1]:

> ... strong belief that bounds checks couldn’t realistically be made cheap enough to enable by default. However, so far they are looking very affordable. From the above post, 0.3% for bounds checks in all the standard library types!

There's more to the hardening story than just bounds checks. But it's a big part IMO.

[1] https://chandlerc.blog/posts/2024/11/story-time-bounds-check...

tempodox

Even if bounds checks were only active in debug builds, that would already be of high value.

delta_p_delta_x

> Even if bounds checks were only active in debug builds

In MSVC or Clang, when compiled against the Microsoft C++ STL, they already are. So,

  auto x = std::vector{1, 2, 3, 4, 5};
  std::println("{}", x[5]);
throws a very specific exception at runtime under debug mode.

In fact on Windows, even the C runtime has debug checks. That's why there are four options to choose from when linking against the modern UCRT:

  /MT (static linking, no debug)
  /MTd (static linking, with debug)
  /MD (dynamic linking, no debug)
  /MDd (dynamic linking, with debug)
For what 'debug in the C runtime' entails, see this comment I made a while ago[1]. As I mentioned, Unix-likes have no equivalent; you get one libc, and if you want to statically link against it, you have to release your own source code because it's GPL. Not sure why people put up with it.

[1]: https://news.ycombinator.com/item?id=40361096

fweimer

The GNU equivalent is -D_GLIBCXX_ASSERTIONS: https://gcc.gnu.org/onlinedocs/libstdc++/manual/using_macros...

It mostly impacts templated code, so it's a compiler flag, not a linker flag. Many distributions have been using this flag to build C++ code for quite some time.

(And this concerns GNU libstdc++, not glibc, so different licensing rules apply.)

uecker

Not sure what you mean by "Not sure why people put up with it". Glibc is licensed under LGPL, so you can distribute it proprietary software even with static linking under some conditions. And there also other alternatives.

pjmlp

That at least has been covered almost since C++ exists.

First in compiler vendors frameworks, pre C++98, afterwards with build settings.

It is quite telling from existing community culture, that some folks only read their compiler manuals when government knocks on the door.

tester756

>It is quite telling from existing community culture, that some folks only read their compiler manuals when government knocks on the door.

What do you want to say?

Is this bad? I think this is desired. Only in c or c++ world people act like understanding how compiler internals work (often poorly) is desired

rwmj

This is why you should have an option (enabled routinely in your CI) to run your tests under valgrind.

throw-qqqqq

I do this too, but valgrind is slow! In my experience, the runtime increases a factor of 10/20/30x ..

another_twist

Maybe an easier way out is to add safe access instructions to LLVM itself. Its an IR after all, it should be possible to do a 3 phase update - add instructions to the IR, update the intermediate LLVM generator, then update the targetting backends.

rurban

They should also turn off the C11 Unicode identifier bugs with -fhardened, which enabled homoglyph attacks. There is no plan for C26 to fix this. No unicode identifiers without proper security measures

rwmj

What is the threat profile here? I don't understand how this would be exploited in the real world. Once you're linking to a library, there are so many ways for the library to exploit your main program (eg. by running arbitrary code in constructors).

rurban

https://github.com/rurban/libu8ident

Search for homoglyph attacks and the unicode security guidelines for identifiers

rwmj

OK that is pretty interesting. For the TL;DR crowd, the exploit was:

  if(environmentǃ=ENV_PROD){
    // bypass authZ checks in DEV
    return true;
  }
where the 'ǃ' is a Unicode homoglyph (U+1C3 "LATIN LETTER ALVEOLAR CLICK") which obviously completely changes the nature of the code.

I'll note that GCC gives a clear warning here ("suggest parentheses around assignment used as truth value"), so as always, turn on -Werror and take warnings seriously!

null

[deleted]

ajb

In the long term, it might be best to disable the ability to switch off checks using command line flags (which usually means, the whole executable) and only allow it on individual functions. Although the current mechanism to switch them off per function isn't idiot proof either (you need to remember to "#pragma diagnostic pop" after ) - we really need to be able to do it in a function attribute.

dilawar

> So this mode needs to set user expectations appropriately: your code breaking between compiler releases is a feature, not a bug.

Good luck. I feel that the C++ community values backward compatibility way too much for this to succeed. Most package maintainers are not going to like it a bit.

pjmlp

There has been plenty of breakage throughout ISO revisions.

The biggest problem is ABI, in theory that isn't something that standard cares about, in practice all compiler vendors do, thus proposals that break ABI from existing binary libraries tend to be an issue.

Another issue is that WG21 nowadays is full of people without compiler experience, willing to push through their proposals, even without implementations, which then compiler vendors are supposed to suck it up and implement them somehow.

After around C++14 time, it became cool to join WG21 and now the process is completely broken, there are more than 200 members.

There is no guidance on an overall vision per se, everyone gets to submit their pet proposal, and then needs to champion it.

Most of these folks aren't that keen into security, hence the kind of baby steps that have been happening.

dzaima

Compilers at least allow specifying the standard to target, which solves the ISO revision issue. But breaking within the same -std=... setting is quite a bit more annoying, forcing either indefinite patching on otherwise-complete functional codebases, or keeping potentially every compiler version on your system, both of which are pretty terrible options.

pjmlp

Breaking within the same std, is something impossible to prevent in compiled languages with enough freedom in build.

Even the C ABI many talk about, most of them don't have any idea of what they are actually talking about.

First of all, it is the OS ABI, in operating systems that happened to be written in C.

Secondly, even C binary libraries have plenty of breakage opportunities within the same std, and compiler.

ABI stability even in languages that kind of promise it, is in reality an half promise.

Bytecode, or some part of the language is guaranteed to be stable, while being tied to a specific version, not all build flags fall under the promise, and not much is promised over the standard library.

Even other good examples that go to great efforts like Java, .NET or Swift, aren't fully ABI safe.

dvtkrlbs

I wish the additional proposak that would add Eust like editions with the cpp moduled were expected. So sad it didnt pass.

charcircuit

Assuming the code is position independent why can't the linker translate the ABI?

porridgeraisin

I don't like that statement (or that whole paragraph) one bit either. My packages breaking between compiler releases is most definitely a big fat bug.

If bounds checks are going to be added, cool, -fstl-bounds-check. Or -fhardened like GCC. But not by default.

Working existing code is working existing code, I don't care if it looks "suspicious" to some random guy's random compiler feature.