9 ms·
Hardening the C++ Standard Library at scale
- fighterhao 11mo ago[flagged]
- tialaramex 11mo ago> 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 11mo agoOf 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 11mo ago>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-win11-24h2-bug/ https://cookieplmonster.github.io/2025/04/23/gta-san-andreas...
- samdoesnothing 11mo agonooooo you don't understand, safety is the most important thing ever for every application, and everything else should be deprioritized compared to that!!!
- gishh 11mo agoMost 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 11mo agoI 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 11mo agoSafety-critical systems aren’t connected to a MAC address you can ping. I didn’t move the goalposts.
- josephg 11mo agoSure 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.
- AlotOfReading 11mo agoIn 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.
- hgs3 11mo ago> The people writing libraries have no idea how their library will be used. Unless you're paying them, the people writing the libraries have no obligation to care. The real issue is Big Tech built itself on the backs of volunteer labor and expects that labor to provide enterprise-grade security guarantees. That's entitled and wholly unreasonable. > Take libxml2 as an example. libxml2 is an excellent example. I recommend you read what its maintainer has to say [1]. [1] https://gitlab.gnome.org/GNOME/libxml2/-/issues/913#note_2439345 https://gitlab.gnome.org/GNOME/libxml2/-/issues/913#note_243...
- AlotOfReading 11mo agoThat's part of my point. As Nick says, libxml2 was not designed with security in mind and he has no control over how people use it. Yet in the "security only in the critical components" mindset, he's responsible for bearing the costs of security-critical development entirely on his own since daniel left. That sucks. But this isn't a conversation limited to the big tech parasitism Nick is talking about. A quick check on my FOSS system implicates the text editor, the system monitor, the office suite, the windowing system, the photo editor, flatpak, the IDEs, the internationalization, a few daemons, etc as all depending on libxml2 and its nonexistent security.
- criemen 11mo ago> 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?
- layer8 11mo agoArguably the effort presented assumes the context of LLVM, where there is information on the actual compiler behavior.
- forrestthewoods 11mo ago> 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.
- pjmlp 11mo agoWell, there is ongoing work to put all known UB into an UB annex on the standard, with hope that the size of the annex gets reduced over time. How well this will work out remains to be seen. Rust still needs to get rid of its dependency on C++ compiler frameworks, and I don't see Cranelift matching GCC and LLVM any time soon.
- on_the_train 11mo agostd::optional is unsafe in idiomatic use cases? I'd like to challenge that. Seems like the daily anti c++ post
- steveklabnik 11mo agoTwo of the authors are libc++ maintainers and members of the committee, it would be pretty odd if they were anti C++.
- deleted 11mo ago[deleted]
- maccard 11mo agoI’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 11mo agoThat 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 11mo agoIt is undefined behavior. You cannot make a claim about what it will always do.
- AlotOfReading 11mo agoAs a fun example, I worked on a safety-critical system where accessing all-bits-zero pointers would trigger an IRQ that jumped back to PC + 4, leaving the register/variable uninitialized. Great fun was had any time there was LR corruption and CPU started executing whatever happened to be next in memory after function return.
- xiphias2 11mo agoIt'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.
- pjmlp 11mo agoIt has always been the default in compiler provided frameworks before C++98, like Turbo Vision, BIDS, OWL, MFC and so on. Unfortunately the default changed when C++98 came out, and not everyone bothered with providing at least hardening mode in debug builds, VC++ followed by GCC, or compilers in high integrity computing like Green Hills. Sadly the security and quality mentality seems to be a hard sell in areas where folks are supposed to be Engineers and not craftsmen.
- xiphias2 11mo agoIt's not just simply security / quality, but speed of iteration matters as well: the earlier bugs are found in optimized nodebug mode as well, the more I like to use C++ itself for developing.
- ris 11mo agoSee also the "lite assertions" mode @ https://gcc.gnu.org/wiki/LibstdcxxDebugMode 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 11mo agoInteresting how C++ is still improving; seems like changes of this kind my rival at least some of the Rust use cases; time will tell
- semiinfinitely 11mo ago> Interesting how C++ is still improving its not
- Conscat 11mo agoDo you read the Clang git commit log every day? C++ improves in many ways faster than any other language ecosystem.
- deleted 11mo ago[deleted]
- Maxatar 11mo agoI think he was referring to the language specification, not a specific compiler.
- actionfromafar 11mo agoBut that is also wrong, as per the article C++ 26 got some improvements in a hardening profile.
- Maxatar 11mo agoI see that C++26 has some incredibly obscure changes in the behavior of certain program constructs, but this does not mean that these changes are improvements. Just reviewing the actual hardening of the standard library, it looks like in C++26 an implementation may be considered hardened in which case if certain preconditions don't hold then a contract violation triggers an assertion which in turn triggers a contract violation handler which may or may not result in a predictable outcome depending on one of 4 possible "evaluation semantics". Oh and get this... if two different translation units have different evaluation semantics, a situation known as "mixed-mode" then you're shit out of luck with respect to any safety guarantees as per this document [1] which says that mixed-mode applications shall choose arbitrarily among the set of evaluation semantics, and as it turns out the standard library treats one of the evaluation semantics (observe) as undefined behavior. So unless you can get all third party dependencies to all use the same evaluation semantic, then you have no way to ensure that your application is actually hardened. So is C++26 adding changes? Yes it's adding changes. Are these changes actual improvements? It's way to early to tell but I do know one thing... it's not at all uncommon that C++ introduces new features that substitute one set of problems for a new set of problems. There's literally a 300 page book that goes over 20 distinct forms to initialize an object [2], many of these forms exist to plug in problems introduced by previous forms of initialization! For all we know the same thing might be happening here, where the classical "naive" undefined behavior is being alleviated but in the process C++ is introducing an entire new class of incredibly difficult to diagnose issues. And lest you think I'm just spreading FUD, consider this quote from a paper titled "C++26 Contracts are not a good fit for standard library hardening" [3] submitted to the C++ committee regarding this upcoming change arguing that it risks giving nothing more than the illusion of safety: >This can result in violations of hardened preconditions being undefined behaviour, rather than guaranteed to be diagnosed, which defeats the purpose of using a hardened implementation. [1] https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p2900r13.pdf https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2025/p29... [2] https://www.amazon.ca/dp/B0BW38DDBK?language=en_US&linkCode=gg2&linkId=76565ec8504083fbaae116133da82c20&tag=bfilipek-20 https://www.amazon.ca/dp/B0BW38DDBK?language=en_US&linkCode=... [3] https://isocpp.org/files/papers/P3878R0.html https://isocpp.org/files/papers/P3878R0.html
- fweimer 11mo agoHow does this compare to _GLIBCXX_ASSERTIONS in libstdc++ (on by default in Fedora since 2018)?
- beached_whale 11mo agoMy 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-safety-to-hundreds.html 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 11mo ago[flagged]
- dana321 11mo agoImagine hardening the regex library, its already as slow as molasses.
- jeffbee 11mo agoThere are lots of parts of the standard library that nobody uses, and hardening has no performance impacts in code you don't call.
- Panzerschrek 11mo agoIt's a good decision to add at least some checks into C++ standard library. But no runtime check can find a bug in code like this: std::vector<int> v; v.push_back(123); auto& n= v.front(); v.push_back(456); auto n_doubled= n * 2; A better language is needed in order to prevent such bugs, where such compile-time correctness checks are possible. Some static analyzers are able to detect it in C++, but only in some cases.
- delta_p_delta_x 11mo agoIt seems to me statically checking this should be possible. The liveness of the result of std::vector::front() should be invalidated and be considered dead after the second invocation to push_back(). Then a static analyser would correctly mark the final line with red squiggles. Of course, compilers would still be happy to compile this, which they really ought not to.
- aw1621107 11mo ago> It seems to me statically checking this should be possible. Statically checking this specific example (or similarly simple examples) could be possible, sure. I'm not so sure about more complex cases, such as opaque functions (whether because the function is literally opaque or because not enough inlining occurred), stored references (e.g., std::span), unintentional mutation of the underlying data structure, etc. Thats basically one of the main reason Rust's lifetimes exist - to explicitly encode information about when lifetimes are valid in the type system. C++ doesn't have an equivalent (yet?), so unless you're willing to use global analysis an/or non-standard annotations there's only so much static analysis can do.
- optimalsolver 11mo agoWhat does this do?
- aw1621107 11mo agoPotential use-after-free. push_back() may reallocate, which would invalidate the reference returned by front(), rendering its subsequent use invalid.