C++ Profiles Are the Only Realistic Answer to the Memory Safety Problem at Scale
Source: isocpp
The pressure on C++ to address memory safety has intensified considerably over the past few years. NSA and CISA advisories have called out C and C++ by name, recommending organizations transition to memory-safe languages. Google’s Android team published data showing that Rust-written code has a dramatically lower rate of memory safety vulnerabilities than equivalent C++ code. The White House Office of the National Cyber Director weighed in. The message from governments and large platform vendors has been consistent: write new things in Rust, Go, or Swift, and consider rewriting the old things.
The C++ community has heard this. The response, formalized in proposals like P2816 and P3081, and discussed by Gabriel Dos Reis at the BeCPP Symposium 2026, is C++ profiles: an extensible, tooling-based framework that applies safety constraints to existing code without requiring rewrites. The argument is not that profiles are as safe as Rust. The argument is that they are the only feasible intervention at the scale of the existing C++ codebase.
What Profiles Actually Are
A profile is a named set of diagnostics that enforce safety constraints beyond what the C++ standard currently requires. Three core profiles have been defined through the C++ Core Guidelines:
Type.safety eliminates undefined behavior arising from type violations, including casts that reinterpret memory in ways the type system cannot verify and union access that reads a member other than the last one written.
Bounds.safety eliminates out-of-bounds memory access. This covers raw pointer arithmetic, array indexing without range checks, and standard library calls that accept iterators or pointers where the caller is responsible for ensuring validity.
Lifetime.safety eliminates dangling pointer and use-after-free bugs. The underlying analysis comes from the lifetime paper by Herb Sutter and Neil MacIntosh, which tracks ownership relationships between pointers and the objects they reference through flow-sensitive analysis.
The key design constraint is that these profiles are additive and independent. You can opt a translation unit, a module, or a codebase into one profile without enabling the others. This matters enormously for large codebases where different teams own different subsystems and cannot coordinate a simultaneous upgrade.
The Rust Comparison Is Partially a False Frame
Rust’s safety guarantees come from its type system. The borrow checker enforces ownership and lifetime rules at compile time by making unsafe patterns inexpressible in the language itself. If your code compiles in safe Rust, you cannot have data races, dangling references, or use-after-free. The guarantee is structural and total within the safe subset.
C++ profiles provide no equivalent guarantee. A profile checker is a static analysis tool. It can miss bugs, especially in code that is too complex for the analysis to reason about precisely. The fundamental difference is that Rust makes unsafe patterns unrepresentable while profiles make them detectable. A sufficiently clever or convoluted C++ program can fool the analysis.
But the comparison has a fundamental asymmetry that often gets ignored in these discussions. Rust’s guarantees apply to new Rust code. There are approximately zero lines of production Rust that were originally written in C++ and mechanically converted. The existing C++ codebase, which runs financial systems, operating system kernels, game engines, browsers, and embedded devices, cannot be rewritten in Rust on any reasonable timeline. Dos Reis frames this correctly in the BeCPP talk: the constraint is delivering improvements to code that is running right now and delivering value right now.
Profiles are not a substitute for Rust. They are an intervention in a problem space Rust cannot reach.
How the Analysis Works in Practice
The lifetime analysis that underpins the Lifetime.safety profile tracks two categories: owners and pointers. An owner is something that controls the lifetime of a resource, typically through RAII: a std::vector, a std::unique_ptr, a local object with a destructor. A pointer is something that references a resource without owning it: raw pointers, references, iterators, spans.
The analysis inserts implicit annotations at each declaration, tracking what each pointer can point to at each program point. When control flow merges at a join point, the analysis takes the union of possible pointees. A violation occurs when the analysis determines a pointer might outlive the object it points to.
// Lifetime.safety violation: reference to local variable returned
const int& bad() {
int x = 42;
return x; // x's lifetime ends here, reference dangles
}
// Bounds.safety violation: raw pointer arithmetic without range check
void process(int* data, int len) {
for (int i = 0; i <= len; ++i) // off-by-one: accesses data[len]
data[i] *= 2;
}
// Type.safety violation: unsafe reinterpret cast
void type_violation(float f) {
int* i = reinterpret_cast<int*>(&f); // type punning; use std::bit_cast instead
*i = 0;
}
A profile-aware compiler or analysis tool flags all three of these. The first two are detectable with moderate precision by current implementations in MSVC’s lifetime checker and clang-tidy’s cppcoreguidelines checks. The third requires type-system-level tracking.
For bounds safety specifically, the preferred fix is replacing raw array access with std::span, which carries a size alongside its pointer and enables the library to check accesses at runtime or, in some cases, at compile time:
// Bounds-safe version using std::span
void process(std::span<int> data) {
for (auto& elem : data) // range-for over span: no index arithmetic needed
elem *= 2;
}
This is the direction the standard library has been moving since C++20, and it aligns directly with what Bounds.safety profiles enforce: prefer owning or bounded views over raw pointers and sizes passed separately.
The Extensibility Angle
One aspect of the profiles design that deserves more attention is that the framework is intentionally open-ended. P3081 defines what a profile is and how tooling should report violations, but it does not fix the set of profiles to the three core ones. Organizations can define domain-specific profiles for their own safety constraints.
A real-time systems team might define a profile that prohibits dynamic allocation. A cryptography library might define a profile that flags constant-time violations. A security-hardening team might define a profile targeting patterns from the CERT C++ coding standard. The framework provides a unified mechanism for expressing and enforcing all of these, rather than requiring each team to build their own ad-hoc linters with incompatible reporting formats.
This extensibility is where the “single, extensible, and flexible framework” framing in Dos Reis’s abstract becomes concrete. The value is not just in the three core profiles; it is in giving the ecosystem a shared language for expressing what a safety constraint is and how violations should be communicated to developers and build systems.
The Standardization Picture
Profiles are not yet in the C++ standard. The committee has wanted more implementation experience before standardizing the design, and the most likely near-term path is a Technical Specification, which allows tools to implement the proposal and gather real-world data without the full commitment of a language change.
C++26 did standardize contracts (P2900), which provides a related but distinct mechanism: runtime-checked preconditions and postconditions. Contracts and profiles are complementary. Contracts catch violations at runtime with configurable violation handling; profiles catch violations at compile time through static analysis. A mature safety story for C++ will involve both, along with improved standard library types like std::span and std::mdspan that provide bounds-safe interfaces over raw memory.
Gabriel Dos Reis’s position at Microsoft, as an architect working on Visual Studio and MSVC, means his perspective on profiles is not purely academic. Microsoft has been the most visible industrial implementer of profile-style analysis through the lifetime checker and investment in clang-cl. Bloomberg, one of the largest C++ shops in the world, has contributed to the design discussions. The committee’s pace on profiles reflects genuine engineering difficulty, not indifference.
What Progress Looks Like
The practical question for any large C++ codebase is what the violation rate looks like when you enable a profile on existing code. If enabling Bounds.safety generates ten thousand warnings in a million-line codebase, the analysis is not useful: developers will suppress or ignore it. The design challenge for profiles is tuning precision so the signal-to-noise ratio is high enough to act on.
This is where the conservatism of the analysis matters. Both the lifetime checker design and P3081 explicitly accept false negatives to avoid false positives. A missed bug is bad; a false positive that trains developers to ignore warnings is worse. The tradeoff is different from Rust, where the type system can afford to be strict precisely because it was designed from scratch with safety as a first-order constraint. C++ profiles are retrofitting safety onto a language that has decades of idioms, libraries, and codebases that never anticipated these constraints.
The incremental adoption model is the key mechanism here. A team can start by enabling Bounds.safety in a single directory, fixing the violations, and expanding from there. The profile does not need to be enabled globally to provide value, and the value compounds as coverage grows. Dos Reis’s framing of profiles as “evolutionary” is accurate: the intent is not a big-bang transition but a gradual tightening.
The Broader Point
What Dos Reis is describing at BeCPP is an engineering constraint that often gets lost in discussions of memory safety: the difference between a greenfield safety problem and a brownfield one. Rust solves the greenfield problem well. The brownfield problem, which is the problem that actually exists for most organizations running C++ today, requires a different set of tools.
Profiles are those tools. They will not make C++ as safe as Rust. They will make existing C++ codebases meaningfully safer, incrementally, without stopping the world. For the billions of lines of C++ that will still be running in ten years, that is the only available path, and it is worth taking seriously on its own terms rather than measuring it solely against a language that cannot touch the same problem space.