[HN Gopher] Hardening the C++ Standard Library at scale
___________________________________________________________________
Hardening the C++ Standard Library at scale
Author : ndesaulniers
Score : 97 points
Date : 2025-11-23 04:22 UTC (6 days ago)
(HTM) web link (queue.acm.org)
(TXT) w3m dump (queue.acm.org)
| tialaramex wrote:
| > those that lead to undefined behavior but aren't security-
| critical.
|
| Once again C++ people imagining into existence Undefined
| Behaviour which isn't Security Critical as if somehow that's a
| thing.
|
| Mostly I read the link because I was intrigued as to how this
| counted as "at scale" and it turns out that's misleading, the
| article's main body is about the (at scale) deployment at Google,
| not the actual hardening work itself which wasn't in some special
| way "at scale".
| AshamedCaptain wrote:
| Of course there is undefined behavior that isn't security
| critical. Hell, _most bugs_ aren 't security critical. In fact,
| _most software isn 't security critical_, at all. If you are
| writing software which is security critical, then I can
| understand this confusion; but you have to remember that most
| people _don 't_.
|
| The author of TFA actually makes another related assumption:
|
| > A crash from a detected memory-safety bug is not a new
| failure. It is the early, safe, and high-fidelity detection of
| a failure that was already present and silently undermining the
| system.
|
| Not at all? Most memory-safety issues will never even show up
| in the radar, while with "Hardening" you've converted all of
| them into crashes that for sure will, annoying customers.
| Surely there must be a middle ground, which leads us back to
| the "debug mode" that the article is failing to criticize.
| charleslmunger wrote:
| >Not at all? Most memory-safety issues will never even show
| up in the radar
|
| Citation needed? There's all sorts of problems that don't
| "show up" but are bad. Obvious historical examples would be
| heartbleed and cloudbleed, or this ancient GTA bug [1].
|
| 1: https://cookieplmonster.github.io/2025/04/23/gta-san-
| andreas...
| samdoesnothing wrote:
| nooooo you don't understand, safety is the most important
| thing ever for every application, and everything else should
| be deprioritized compared to that!!!
| gishh wrote:
| Most people around here are too busy evangelizing rust or
| some web framework.
|
| Most people around here don't have any reason to have strong
| opinions about safety-critical code.
|
| Most people around here spend the majority of their time
| trying to make their company money via startup culture, the
| annals of async web programming, and how awful some type
| systems are in various languages.
|
| Working on safety-critical code with formal verification is
| the most intense, exhausting, fascinating work I've ever
| done.
|
| Most people don't work a company that either needs or can
| afford a safety-critical toolchain that is sufficient for
| formal, certified verification.
|
| The goal of formal verification and safety critical code is
| _not_ to eliminate undefined behavior, it is to fail safely.
| This subtle point seems to have been lost a long time ago
| with "*end" developers trying to sell ads, or whatever.
| kccqzy wrote:
| I appreciate your insights about formal verification but
| they are irrelevant. Notice that GP was talking about
| security-critical and you substituted it for safety-
| critical. Your average web app can have security-critical
| issues but they probably won't have safety-critical issues.
| Let's say through a memory safety vulnerability your web
| app allowed anyone to run shell commands on your server;
| that's a security-critical issue. But the compromise of
| your server won't result in anyone being in danger, so it's
| not a safety-critical issue.
| gishh wrote:
| Safety-critical systems aren't connected to a MAC address
| you can ping. I didn't move the goalposts.
| josephg wrote:
| Sure they are. Eg, 911 call centers. Flight control.
| These systems aren't on the open internet, but they're
| absolutely networked. Do they apply regular security
| patches? If they do, they open themselves up to new bugs.
| If not, there are known security vulnerabilities just
| waiting for someone to use to slip into their network and
| exploit.
|
| And what makes you think buggy software only causes
| problems when hackers get in? Memory bugs cause memory
| corruption and crashes. I don't want my pacemaker running
| somebody's cowboy C++, even if the device is never
| connected to the internet.
| gishh wrote:
| Ah. I was responding to:
|
| > Your average web app can have security-critical issues
| but they probably won't have safety-critical issues.
|
| How many air-gapped systems have you worked on?
| AlotOfReading wrote:
| Individual past experiences aren't always representative
| of everything that's out there.
|
| I've worked on safety critical systems with MAC addresses
| you can ping. Some of those systems were also air-gapped
| or partially isolated from the outside world. A rare few
| were even developed as safety critical.
| AlotOfReading wrote:
| The goal of formal verification and safety critical code is
| _not_ to eliminate undefined behavior, it is to fail
| safely.
|
| Software safety cases depend on being able to link the
| executable semantics of the code to your software safety
| requirements.
|
| You don't _inherently_ need to eliminate UB to define the
| executable semantics of your code, but in practice you do.
| You could do binary analysis of the final image instead.
| You wouldn 't even need a qualified toolchain this way. The
| semantics generated would only be valid for that exact
| build, and validation is one of the most expensive/time-
| consuming parts of safety critical development.
|
| Most people instead work at the source code level, and rely
| on qualified toolchains to translate defined code into
| binaries with equivalent semantics. Trying to define the
| executable semantics of source code inherently requires
| eliminating UB, because the kind of "unrestricted UB" we're
| talking about has no executable semantics, nor does any
| code containing it. Qualified toolchains (e.g. Compcert,
| Green Hills, GCC with solidsand, Diab) don't guarantee
| correct translation of code without defined semantics, and
| coding standards like MISRA also require eliminating it.
|
| As a matter of actual practice, safety critical processes
| "optimistically ignore" some level of undefined behavior,
| but that's not because it's acceptable from a principled
| stance on UB.
| AlotOfReading wrote:
| In fact, most software isn't security critical, at all. If
| you are writing software which is security critical, then I
| can understand this confusion; but you have to remember that
| most people don't.
|
| No one knows what software will be security critical when
| it's written. We usually only find out after it's already too
| late.
|
| Language maintainers have no idea what code will be written.
| The people writing libraries have no idea how their library
| will be used. The application developers often don't realize
| the security implications of their choices. Operating systems
| don't know much about what they're managing. Users may not
| even realize what software they're running at all, let alone
| the many differing assumptions about threat model implicitly
| encoded into different parts of the stack.
|
| Decades of trying to limit the complexity of writing
| "security critical code" only to the components that are
| security critical has resulted in an ecosystem where
| virtually nothing that _is_ security critical actually meets
| that bar. Take libxml2 as an example.
|
| FWIW, I disagree with the position in the article that fail-
| stop is the best solution in general, but there's
| experimental evidence to support it at least. The industry
| has tried many different approaches to these problems in the
| past. We should use the lessons of that history.
| criemen wrote:
| > Of course there is undefined behavior that isn't security
| critical.
|
| But undefined behavior is literally introduced as "the
| compiler is allowed to do anything, including deleting all
| your files". Of course that's security critical by
| definition?
| forrestthewoods wrote:
| > Undefined Behaviour which isn't Security Critical as if
| somehow that's a thing
|
| Undefined behavior in the (poorly written) spec doesn't mean
| undefined behavior in the real world. A given compiler is
| perfectly free to specify the behavior.
| on_the_train wrote:
| std::optional is unsafe in idiomatic use cases? I'd like to
| challenge that.
|
| Seems like the daily anti c++ post
| steveklabnik wrote:
| Two of the authors are libc++ maintainers and members of the
| committee, it would be pretty odd if they were anti C++.
| maccard wrote:
| I'm very much pro c++, but anti c++'s direction.
|
| > optional is unsafe in idiomatic use cases? I'd like to
| challenge that. std::optional<int>
| x(std::nullopt); int val = *x;
|
| Optional is by default unsafe - the above code is UB.
| TinkersW wrote:
| That is actually memory safe, as null will always trigger
| access violation..
|
| Anyway safety checked modes are sufficient for many programs,
| this article claims otherwise but then contradicts itself by
| showing that they caught most issues using .. safety checked
| modes.
| steveklabnik wrote:
| It is undefined behavior. You cannot make a claim about
| what it will always do.
| maccard wrote:
| >null will always trigger access violation..
|
| No, it won't. https://gcc.godbolt.org/z/Mz8sqKvad
| TinkersW wrote:
| Oh my bad, I read that as nullptr, I use a custom
| optional that does not support such a silly mode as
| "disengaged"
| canyp wrote:
| How is that an optional then?
|
| The problem is not nullopt, but that the client code can
| simply dereference the optional instead of being forced
| to pattern-match. And the next problem, like the other
| guy mentioned above, is that you cannot make any claims
| about what will happen when you do so because the
| standard just says "UB". Other languages like Haskell
| also have things like fromJust, but at least the
| behaviour is well-defined when the value is Nothing.
| maccard wrote:
| What do you return if there is no value set? That's the
| entire point of optional.
| wild_pointer wrote:
| You didn't read this, did you?
| https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/
|
| It's not a pointer.
| on_the_train wrote:
| But using the deref op is deliberately unsafe, and never used
| without a check in practice. This would neither pass a
| review, nor static analysis.
| canyp wrote:
| GP picked the less useful of the two examples. The other
| one is a use-after-move, which static analysis won't catch
| beyond trivial cases where the relevant code is inside
| function scope.
|
| I also agree with them: I am pro-C++ too, but the current
| standard is a fucking mess. Go and look at modules if you
| haven't, for example (don't).
| mohinder wrote:
| > This would neither pass a review, nor static analysis
|
| I beg to differ. Humans are fallible. Static analysis of
| C++ cannot catch all cases and humans will often accept a
| change that passes the analyses.
| einpoklum wrote:
| > Static analysis of C++ cannot catch all cases
|
| You're ignoring how static analysis can be made to err on
| the side of safety rather than promiscuity.
|
| Specifically, for optional dereferencing, static analysis
| can be made to disallow it unless it can prove the
| optional has a value.
| IshKebab wrote:
| > never used without a check in practice
|
| Ho ho ho good one.
| boulos wrote:
| They linked directly to
| https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/
| which did exactly what I'd guessed as its example:
|
| > The following code for example, simply returns an
| uninitialized value: #include <optional>
| int f() { std::optional<int> x(std::nullopt);
| return *x; }
| on_the_train wrote:
| But that is not idiomatic at all. Idiomatic would be too use
| .value()
| IshKebab wrote:
| Not only is this a silly No True Scotsman argument, but
| it's also absolute nonsense. It's perfectly idiomatic to
| use `*some_optional`.
| Maxatar wrote:
| Just a cursory search on Github should put this idea to
| rest. You can do a code search for std::optional and
| .value() and see that only about 20% of uses of
| std::optional make use of .value(). The overwhelming
| majority of uses off std::optional use * to access the
| value.
| electroly wrote:
| Sadly I have lots of code that exclusively uses the
| dereference operator because there are older versions of
| macOS that shipped _without_ support for .value(); the
| dereference operator was the only way to do it! To this
| day, if you target macOS 10.13, clang will error on use of
| .value(). Lots of this code is still out there because they
| either continue to support older macOS, or because the code
| hasn 't been touched since.
| canyp wrote:
| It is discussed in the linked post:
| https://alexgaynor.net/2019/apr/21/modern-c++-wont-save-us/
|
| tl;dr: use-after-move, or dereferencing null.
| xiphias2 wrote:
| It's great that finally bounds checking happened in C++ by
| (mostly) default.
|
| The only thing that's less great is that this got so much less
| upvotes than all the Safe-C++ languages that never really had the
| chance to get into production in old code.
| ris wrote:
| See also the "lite assertions" mode @
| https://gcc.gnu.org/wiki/LibstdcxxDebugMode for glibc, however
| these are less well documented and it's less clear what
| performance impact these measures are expected to have.
| BinaryIgor wrote:
| Interesting how C++ is still improving; seems like changes of
| this kind my rival at least some of the Rust use cases; time will
| tell
| galangalalgol wrote:
| The issue with safer c++ and modern c++ is the mirror of the
| problem with migrating a code base from c++ to rust. There is
| just so much unmodern and unsafe c++ out there. Mixing modern
| c++ into older codebases leaves uncertain assumptions
| everywhere and sometimes awkward interop with the old c++. If
| there was a c++23{} that let the compiler know that only modern
| c++ and libc++ existed inside it would make a huge difference
| by making those boundaries clear and you can document the
| assumptions at that boundary. Then move it over time. The
| optimizer would have an advantage in that code too. But they
| don't want to do that. The least they could do is settle on a
| standard c++abi to make interop with newer languages easier,
| but they don't want to do that either. They have us trapped
| with sunk cost on some gaint projects. Or they think they do.
| The big players are still migrating to rust slowly, but
| steadily.
| kaz-inc wrote:
| There kind of is. There's __cplusplus, which I'll grant you
| is quite janky. #IF __cplusplus==202302L
| josephg wrote:
| I'm not really sure how checks like this can rival rust. Rust
| does an awful lot of checks at compile time - sometimes even to
| the point of forcing the developer to restructure their code or
| add special annotations just to help the compiler prove safety.
| You can't trivially reproduce those all those guardrails at
| runtime. Certainly not without a large performance hit. Even
| debug mode stdc++ - with all checks enabled - still doesn't
| protect against many bugs the rust compiler can find and
| prevent.
|
| I'm all for C++ making these changes. For a lot of people,
| adding a bit of safety to the language they're going to use
| anyway is a big win. But in general guarding against threading
| bugs, or use after free, or a lot of more obscure memory issues
| requires either expensive GC like runtime checks (Fil-C has
| 0.5x-4x performance overhead and a large memory overhead). Or
| compile time checks. And C++ will never get rust's extensive
| compile time checks.
| semiinfinitely wrote:
| > Interesting how C++ is still improving
|
| its not
| Conscat wrote:
| Do you read the Clang git commit log every day? C++ improves
| in many ways faster than any other language ecosystem.
| fweimer wrote:
| How does this compare to _GLIBCXX_ASSERTIONS in libstdc++ (on by
| default in Fedora since 2018)?
| beached_whale wrote:
| My understanding that this is like that but both
| libstdc++/libc++ have been doing more since. Additionally,
| Google did a blog not to long ago where they talked to actual
| the performance impact on their large C++ codebase and it
| averaged about 0.3% I think
| https://security.googleblog.com/2024/11/retrofitting-spatial...
|
| Since then, libc++ has categorized the checks by cost and one
| can scale them back too.
| semiinfinitely wrote:
| by deleting it?
| dana321 wrote:
| Imagine hardening the regex library, its already as slow as
| molasses.
___________________________________________________________________
(page generated 2025-11-29 23:00 UTC)