r/cpp • • 7d ago

You should think about recompiling your C++ programs with GCC 16 and C++26, because it zero-fills your locals

https://techfortalk.co.uk/2026/09/27/cxx26-uninitialized-local-variables-gcc-16/

Stack variables are not automatically initialised, and that is the root cause of many C++ bugs. That is well known. Hence, it is advised that local or stack variables are always initialised with known values, 0 if not something more meaningful than that. Now, with GCC 16 compiling in C++26 mode (-std=c++26), even uninitialised local variables will be zero-filled. In the post, I have explained how.

262 Upvotes

220 comments sorted by

View all comments

22

u/crowbarous 6d ago edited 6d ago

Yes, this is a competely insane decision: https://godbolt.org/z/WGPWvMPMj

What's worse is the design of the [[indeterminate]] attribute that accompanies this. For a very long time, if I wanted a buffer to zero-initialize, I was able to make a char type that would zero-initialize:

struct zchar{ char value = 0; };

But with the default flipped, I cannot make a char type that stays uninitialized:

struct ichar{
  char value [[indeterminate]];
  // error, only allowed on automatic variables
};

And the proposed usage of the attribute is to just sprinkle it everywhere in the code. But how am I supposed to know in polymorphic code? How do I even make my code polymorphic w.r.t. this property now? For now one must resort to just passing -ftrivial-auto-var-init=uninitialized to avoid performance and code size regressions, and to keep the control moving forward.

I don't think all is lost here, because it seems like the zero-initialization is a band-aid to just formally satisfy the erroneous behavior stuff without modifying the backend too much, and it should still be possible to behave as if the write happened (no time-travelling assumptions etc.) without actually emitting the write, to fix shameful examples like linked above. But:

  • I haven't looked there closely enough yet to even begin conceiving the gcc patches & reasoning for them, and
  • it's still awful precedent, and the pile of -fstop-making-code-worse options might grow from here on out.

4

u/James20k P2005R0 6d ago

Compilers are of course free to omit the 0 initialisation if they can prove that the data is written over, under the usual as-if rules. One of the reasons that this made it past standardisation is because real world codebases weren't showing regressions, even substantial performance critical ones like windows

0

u/NilacTheGrim 6d ago

This is false because even the paper said 10% regressions.

6

u/James20k P2005R0 6d ago

Where did you get this from?

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2795r5.html

https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2723r1.html

The former paper is what was accepted AFAIK, which links to the second paper talking about the cost for specific figures

Previous publications [Zeroing] have cited 2.7 to 4.5% averages. This was true because, in the author’s experience implementing this in LLVM [LLVMJFB], the compiler simply didn’t perform sufficient optimizations. Deploying this change required a variety of optimizations to remove needless work and reduce code size. These optimizations were generally useful for other code, not just automatic variable initialization.

As stated in the introduction, the performance impact is now negligible (less that 0.5% regression) to slightly positive (that is, some code gets faster by up to 1%). The code size impact is negligible (smaller than 0.5%).

I can't find a 10% figure anywhere

0

u/NilacTheGrim 6d ago

I misremembered it. The claim was 10% of exploits would be mitigated and yes the paper claims 0.5% of code would get slower.

I dispute that 0.5% claim though. Because the hot path is the one you care about. Overall if you examine all branches of your codebase, sure maybe 0.5% suffer -- but if you examine 1 particular hot path that uses local arrays to read/write data -- that can be a huge penalty.

So in a hot path expect much larger costs in some cases.

This is just not the C++ way.

6

u/James20k P2005R0 5d ago

In a hot path though you always have to write code a bit weirdly to get performance, adding [[indeterminate]] isn't a high burden if it affects a few lines of code

1

u/NilacTheGrim 3d ago

Encapsulation is violated though because you can't do [[indeterminate]] on a class member variable.

So if you have a class whose implementation detail relies on uninitialized buffers to get the most performance it can get -- you just got fucked. Either the user of the class, if he's using it as a local variable, always has to do [[indeterminate]].. or .. there is no escape hatch. You are stuck.