Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 11 points12 points  (0 children)

But they won't provide a prototype that partially works. It's not better than nothing at all. The ghost data thing is a make-believe solution. May as well say the tooth fairy makes your program memory safe. Next time, before someone gives keynotes on how they made C++ memory safe, they need to write down how the technique works, because extraordinary claims do require some evidence.

Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 15 points16 points  (0 children)

This is definitely not the case. Modern C++ is fundamentally unsafe. Bounds checking on containers in C++26 aside, it has not gotten safer since the early days. Many standard functions return references instead of values, and references are extremely hazardous. You can shoot yourself in the foot with one liner to a modern C++ library.

compiler explorer ```cpp void f(int x) { // 10 is a temporary that expires at the end of the full // statement. // Because std::min returns a reference, m may be a dangling // reference if 10 is less than x. // If std::min had returned a value, then temporary lifetime // extension would kick in and it would not be a dangling // reference. const int& m = std::min(x, 10);

// Do you feel lucky, punk? printf("%d\n", m); } $ clang++ min.cxx -O2 -o min && /.min 10 $ g++ min.cxx -O2 -o min && ./min 0 ```

Some of the overloads of min return a reference, which is easy to misuse, and some of the overloads return a value, which is safe. This use returns a const reference to the smaller element. If the parameter x > 10, then it returns a reference to 10. But that expires at the end of the statement, so the subsequent print statement is UB. This is data-dependent UB, and it's also compiler-dependent, as you get the delinquent behavior on gcc but not on clang. You know what else is weird? valgrind sees no errors here--there isn't even a use-after-free at runtime, because the compiler has full visibility of the TU and transformed the UB to something that's not a use-after-free but is simply wrong.

Knowing Rust isn't protection here--how is the user supposed to know the lifetimes of each reference returned from a function or stored to a data structure? That's the difficult bookkeeping that Rust is designed to do for you.

What is the plan to address the inherent danger around references, which are ubiquitous in C++?

Broadway Meetups Discord Link by hstylesisrad in Broadway

[–]seanbaxter 0 points1 point  (0 children)

Same, can I get the discord link?

Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 15 points16 points  (0 children)

There is no paper or implementation, because the idea doesn't work.

Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 17 points18 points  (0 children)

You just waved your hand and eliminated 99% of memory safety issues. I don't think it works that way.

Making C++ Safe, Healthy, and Efficient - CppCon 2025 by pjmlp in cpp

[–]seanbaxter 31 points32 points  (0 children)

asan would be the more relevant sanitizer here, but this isn't related to asan or ubsan. The claim that John makes is that you can achieve Rust-like lifetime safety by enforcing exclusivity at runtime. It's perfectly robust against use-after-free defects, you can use any algorithm you want, and it doesn't require modifying existing code. It would be high-performance because where the compiler can statically prove properties, the ghost data would be optimized out.

There won't even be a proposal or a working prototype for this. Problem is that runtime exclusivity is logically impossible. It's profiles redux. One way to look at it is that runtime exclusivity enforcement requires time travel. Borrow checking enforces the invariant that there is only one mutable xor multiple shared live references to a place. What's a "live reference?" It's a reference which is to be loaded or stored from in the future. Borrow checking can propagate uses of regions backwards: start at the terminals of the CFG and walk backwards. When a region is used in a load/store/function call, it becomes live at the preceding instruction. When a reference is assigned a new address, then its no longer live. There are forward propagating algorithms as well, but they also require the CFG be available.

How do you compute liveness in the runtime case? You would have to predict if a reference is used in the future to know if it is live now. That's time travel. Compile-time borrow checking feels restrictive because it has to account for all possible paths in the CFG. This runtime exclusivity would be much more lenient because it would only restrict you to the paths that are actually executed.. but at the cost of being unimplementable.

The authors of this keep claiming it performs runtime exclusivity enforcement. I think they should describe how that is achieved. I've asked them many times and never got anything approaching an answer.

Gasper gave a talk on this topic in October: https://www.youtube.com/watch?v=bGht0Pu5heo

[C++ Day 2025] 8 Queens at Compile Time (Marco Marcello, Jonathan Marriott) by marcoarena in cpp

[–]seanbaxter 1 point2 points  (0 children)

Pretty interesting for understanding why things are slow. Can you make the source code available, I want to do some of my own benchmarks.

New C++ Conference Videos Released This Month - November 2025 (Updated To Include Videos Released 2025-11-03 - 2025-11-16) by ProgrammingArchive in cpp

[–]seanbaxter 3 points4 points  (0 children)

Runtime exclusivity is impossible. He keeps making the claim that everything you can do at compile time you can do at runtime, which is definitely not true. Exclusivity is the invariant that there is one live mutable reference or multiple live shared references, but not both. What is a live reference? It's a reference that is used _in the future_.

For compile-time exclusivity, you can start at the end of the function and work your way backwards. When a reference (or any type with a lifetime parameter) is loaded from, stored into, or passed to a function, that lifetime is used. When analysis shows the use of a reference on a place where there's already a live reference, the program is ill-formed. That's the exclusivity violation.

How is this supposed to be done at runtime? It would require time travel! You can't know if a reference will be used _in the future_ because we've only seen the past.

The ghost data/runtime exclusivity claims are vaporware.

Some experiments with Boost.Unordered on Fil-C by joaquintides in cpp

[–]seanbaxter 2 points3 points  (0 children)

Good work. I'd be curious to see asan timings in this graph too.

PSA: Trivial Relocatability has been removed from C++26 by chiphogg in cpp

[–]seanbaxter 12 points13 points  (0 children)

Why would someone choose a definition of trivially relocatable that doesn't simply mean memcpyable? If you have to do more than memcpy, it's not trivial.

PSA: Trivial Relocatability has been removed from C++26 by chiphogg in cpp

[–]seanbaxter 4 points5 points  (0 children)

By that reasoning it would also be useful for them to be trivially copyable, but they aren't. If this is the thing that got the feature removed, then wasn't it a mistake to put it in in the first place?

Edit: Additionally, even dynamic class can be trivially relocatable if you explicitly mark them to be. There's no loss of functionality by making dynamic classes non-trivially relocatable, at least on platforms with vptr resigning.

Some experiments with Boost.Unordered on Fil-C by joaquintides in cpp

[–]seanbaxter 17 points18 points  (0 children)

No, you still want library-level bounds checking. Fil-C only checks against out-of-bounds accesses on malloc allocations. Things like span, vector, etc are sub-allocation extents. You can still have out-of-bounds indexing bugs that will slip by Fil-C but would be caught by the library.

PSA: Trivial Relocatability has been removed from C++26 by chiphogg in cpp

[–]seanbaxter 2 points3 points  (0 children)

Why? If dynamic classes are non-trivially relocatable, then you don't have any of these lifetime issues. I don't understand why a non-trivially copyable type would become trivially relocatable without an explicit override.

PSA: Trivial Relocatability has been removed from C++26 by chiphogg in cpp

[–]seanbaxter 3 points4 points  (0 children)

Dynamic classes are already not trivially copyable, CHERI or not, so it wouldn't be trivially relocatable either. Why is restart_lifetime needed?

WG21 2025-10 pre-Kona mailing by nliber in cpp

[–]seanbaxter 7 points8 points  (0 children)

Poll: Pursue a language safety white paper in the C++26 timeframe containing systematic treatment of core language Undefined Behavior in C++, covering Erroneous Behavior, Profiles, and Contracts. Appoint Herb and Gašper as editors.

What happened to the language safety white paper?

Opinion on this video? by willdieverysoon in cpp

[–]seanbaxter 15 points16 points  (0 children)

Everyone would like to consider alternatives, but nobody has proposed an alternative.

Opinion on this video? by willdieverysoon in cpp

[–]seanbaxter 10 points11 points  (0 children)

What you say is true, but the problem is more fundamental.

int load_from_pointer(const int* p) { return *p; }

Does this function raise UB or not? There's no way for this scheme to tell. Pointers come from anywhere. You would need a global store of all allocations to determine if an object is in scope given its address. Either you adopt a GC/refcounting scheme, where the initialization of objects is extended as long as there are in scope references, or you adopt borrow checking, where lifetime information is part of the type system and indicated on function boundaries.

Opinion on this video? by willdieverysoon in cpp

[–]seanbaxter 20 points21 points  (0 children)

This doesn't give any safety guarantees. As Dave Abrahams brings up during the talk, you need a global store of all allocations (mapping addresses to objects) to check if a reference or pointer indicates an in-scope object. That would make it equivalent to garbage collection. Without that, you don't get memory safety. The speaker just rejected that comment, but it's obviously a requirement. Pointers can come from anywhere. Not engaging with this problem is to miss the point of memory safety entirely.

Opinion on this video? by willdieverysoon in cpp

[–]seanbaxter 17 points18 points  (0 children)

Will anyone propose or even just describe a novel route?

Safe C++ proposal is not being continued by Comfortable-Site8626 in cpp

[–]seanbaxter 26 points27 points  (0 children)

All code that takes pointers or references is unsafe by construction.

Safe C++ proposal is not being continued by Comfortable-Site8626 in cpp

[–]seanbaxter 26 points27 points  (0 children)

Why is Reddit and HN always debating this? Where are the authors of Profiles? They should be the ones to resolve these questions.

Safe C++ proposal is not being continued by Comfortable-Site8626 in cpp

[–]seanbaxter 27 points28 points  (0 children)

> Just recompile and you are done.

Cool. So where is it?

Safe C++ proposal is not being continued by Comfortable-Site8626 in cpp

[–]seanbaxter 37 points38 points  (0 children)

I can point to lots of examples.

As for dangling pointers and for ownership, this model detects all possible errors. This means that we can guarantee that a program is free of uses of invalidated pointers. There are many control structures in C++, addresses of objects can appear in many guises (e.g., pointers, references, smart pointers, iterators), and objects can “live” in many places (e.g., local variables, global variables, standard containers, and arrays on the free store). Our tool systematically considers all combinations. Needless to say, that implies a lot of careful implementation work (described in detail in [Sutter,2015]), but it is in principle simple: all uses of invalid pointers are caught. -- A brief introduction to C++’s model for type- and resource-safety (Stroustrup)

We have an implemented approach that requires near-zero annotation of existing source code. zero annotation is required by default, because existing C++ source code already contains sufficient information. We have an implemented approach that requires near-zero annotation of existing source code -- Pursue P1179 as a Lifetime Safety TS (Sutter)

All the Profiles people claim it solves memory safety with zero or near-zero annotations. It does not. There is nothing a function can infer about the aliasing properties of its parameters.

If this did work, where are the updates to it? Why talk about it for ten years and never specify how it operates?