C++ Lifetime-End Pointer-Zap and OOTA Progress
Both lifetime-end pointer zap and out-of-thin-air (OOTA) accesses made great progress during the C++ standards committee meeting in Brno in early June 2026!
Lifetime-End Pointer Zap
Lifetime-end pointer zap was described in detail in this August 2025 blog post. This post provided detailed descriptions of four C++ working papers, and the successors of three of them have now been voted into C++29:
- Davis Herring's P2434R4 (“Nondeterministic pointer provenance”) provides the basis for the other three papers.
- P2414R10 (“Pointer lifetime-end zap proposed solutions”) defines atomic and volatile operations on invalid pointers. (Actually, it was an updated P2414R12 that was accepted, but this will not be readily available until the upcoming post-Brno mailing.)
- P3347R5 (“Invalid/Prospective Pointer Operations”) defines loads and stores of invalid pointers. (Actually, it was an updated P3347R6 that was accepted, but again this will not be readily available until the upcoming post-Brno mailing.)
These three papers are all that is needed to enable the infamous LIFO Push algorithm to be written straightforwardly in C++29. In other words, these papers are solving a problem dating back to the very first C++ standard in 1998. And if we can get analogous changes into C, then the Linux kernel's zap-susceptible code in the include/linux/llist.h file will be retroactively blessed, all the way back to the very first C standard in 1989.
The fourth paper, P3790R1 (“Pointer lifetime-end zap proposed solutions: Bag-of-bits pointer class”), faced no fundamental objections, but needed large-scale wording revisions. An updated P3790R2 will appear in the upcoming post-Brno mailing. With luck, this will be voted into the C++29 standard during the upcoming November 2026 meeting.
Learn from my P3790R1 mistakes: Get wording help sooner rather than later!
Out-of-Thin-Air (OOTA) Accesses
The May 2026 edition of the Draft C++ standard, N5046 (“Working Draft, Programming Languages — C++”, has this to say in 32.5.4p8 [atomics.order]:
Implementations should ensure that no “out-of-thin-air” values are computed that circularly depend on their own computation.
The draft standard goes on to give a couple of examples of to-be-forbidden OOTA cycles, first the classic example:
// Thread 1:
r1 = y.load(memory_order::relaxed);
x.store(r1, memory_order::relaxed);
// Thread 2:
r2 = x.load(memory_order::relaxed);
y.store(r2, memory_order::relaxed);
And then this slightly more elaborate example:
// Thread 1:
r1 = x.load(memory_order::relaxed);
if (r1 == 42) y.store(42, memory_order::relaxed);
// Thread 2:
r2 = y.load(memory_order::relaxed);
if (r2 == 42) x.store(42, memory_order::relaxed);
In both cases, the OOTA outcome of r1==r2==42 is to be forbidden. This is all good advice, but one has to have some sympathy for those left wondering exactly what it is that they must do. Or not do. Especially in light of the more-complex examples shown in P3692R4 (“How to Avoid OOTA Without Really Trying”).
Fortunately, there is a way out in (most) C++ implementations that compile to assembly instructions, which are then executed on real hardware. After all, executing instructions and communicating data both take time, which means that the OOTA cycles shown above cannot close. At least if we are willing to accept the empirical evidence thus far that shows that time does not go backwards.
Although this intuition is simple and convincing, the wide variety of compiler optimizations provide some highly non-trivial challenges. Much of P3692R4 deals with these challenges, including the challenge of convincing the reader that their semantic-dependency intuitions are insufficient. In addition, the bulk of P3692R4 consists of some carefully chosen definitions and clever informal proofs supplied by Alan Stern. Alan's definitions and proofs allow us to assert the following:
[ Note 7 — An implementation that translates to instruction sequences for a physical machine and treats non-volatile atomic accesses as though they were volatile does not produce out-of-thin-air values for a program free of undefined behavior. An implementation that performs only thread-at-a-time analysis in support of optimizations that omit and merge non-volatile atomic accesses to the same object and reorder non-volatile atomic accesses to different objects (when permitted by the as-if rule) and otherwise treats non-volatile atomic accesses as though they were volatile does not produce out-of-thin-air values for a program free of undefined behavior. The possibility of out-of-thin-air values remains for implementations not meeting these restrictions. — end note.]
[ Note 8 — Unlike abstract machines, physical machines are subject to instruction-execution and information-transfer latencies. Physical machines carefully manage any speculative execution so as to avoid generating out-of-thin-air values at the hardware level. — end note. ]
So if your code is free of undefined behavior and uses only volatile atomics, then code produced by a conventional C++ compiler cannot produce OOTA, period, end of story.
However, if you use instead non-volatile atomics, then the compiler must restrict its optimizations, but not to an unreasonable extent. There is still some debate as to whether existing production C++ compilers avoid OOTA for non-volatile atomics, with the current (unsatisfying) state being that we cannot prove that they always avoid OOTA, but on the other hand, we don't have specific examples of OOTA.
Nevertheless, P3692R4 does give a straightforward recipe for avoiding OOTA in conventional compilers, even for non-volatile atomics. This is an important milestone in the quarter-century history of the OOTA problem, and this paper was therefore voted into C++29.
Important though this milestone is, it is not the end of the OOTA story. C and Rust implementers would likely also benefit from similar assurances, with C's volatile-only atomics making life relatively easy.
In addition, P3692R4 does nothing for atemporal formal-verification tooling, which includes things like herd7 and CPPMEM. Because these tools do not model time, the passage of time cannot prevent OOTA loops from closing. In addition, P3692R4's reliance on object code means that it in turn relies on the compiler using valid optimizations, and the definition of “valid optimization” is currently a case-by-case social process. Fortunately, Mark Batty and his students and collaborators are working on this, as can be seen in the bibliography citations [34] and [35] in P3692R4.
But if Mark Batty et al. are working on a full mathematical solution to the OOTA problem, why bother with P3692R4's more limited approach?
Because: (1) P3692R4 relieves them of the need to fit within the CPU and memory budgets of production compilers and (2) P3692R4 is available now, and addresses the most urgent needs, thus allowing them to take their time and get it right. Me, I hope that the current state of their work already gets it right, but in my experience, Mr. Murphy has the last word.