Modern C++ is safer than much of the C++ software written decades ago, but it is still not yet a memory-safe language by default. This article surveys what currently exists to support safer C++, the practical work now underway in LLVM and the C++ standards process to reduce undefined behavior that leads to memory-related security and correctness bugs, and the potential for a near-100% memory-safe C++ programming environment in the near future.
Why memory safety matters
Of the CWE Top 25 Most Dangerous Software Weaknesses in 2025,1 six are directly related to memory access errors in unsafe languages like C++:
- #5: out-of-bounds write
- #7: use after free
- #8: out-of-bounds read
- #11: buffer copy without checking size of input
- #13: null pointer dereference
- #14: stack-based buffer overflow
(However, note that this is a significant reduction from the 2023 list, where memory errors occupied the top three spots: #1: use after free, #2: heap-based buffer overflow, and #3: out-of-bounds write.2) So while memory-safety issues did not dominate the reported vulnerabilities in 2025, memory-safety bugs remain one of the most persistent sources of serious software defects and security vulnerabilities. Even if software security is not a major concern for scientific and high-performance computing (HPC) codes, software correctness bugs caused by incorrect memory use are a major threat to the reliability of HPC software and can be among the most challenging and expensive bugs to diagnose and fix. Memory-related bugs in C++ HPC codes can escape testing and lie dormant for some time before causing problems in large, expensive simulation runs.
Large HPC codes are often long-lived, performance-sensitive, and deeply invested in C++ ecosystems. Those ecosystems include NVIDIA's CUDA, AMD's HIP, and the Khronos Group's SYCL, which are key programming models for GPU-accelerated HPC software and are essentially extensions of the C++ language. Rewriting everything in a different, safer programming language is rarely realistic and, in many cases, not even viable because of the limited tooling and libraries available for safer languages, like Rust. Yet, continuing to accept unchecked undefined behavior in these C++ HPC codes is becoming harder to justify.
While there is no single switch that turns ISO standard C++23 (or the proposed C++26) into a memory-safe language, there is now a substantial body of work that can make modern C++ materially safer, especially for spatial memory errors such as out-of-bounds access, which account for a substantial number of reported software security vulnerabilities. Much of the concrete progress is happening around the LLVM and Clang compiler toolchains.
What "modern C++" already provides for memory safety
Before looking at the newest compiler and library work, it is worth noting that the standard C++ library has already moved in a safer direction over several revisions.
- C++11 gave developers a better ownership vocabulary with
std::unique_ptr,std::shared_ptr, move semantics,std::array, and stronger resource acquisition is initialization (RAII)-based coding styles. - C++14 added
std::make_unique, which made ownership transfer less error-prone. - C++17 added
std::string_view,std::optional,std::variant, andstd::byte, all of which helped replace ad hoc conventions with explicit types. - C++20 added
std::span, ranges, and concepts. - C++23 added
std::mdspanandstd::expected, both useful for expressing intent more clearly in high-performance numerical and systems code.
While consistent use of those newer types eliminates many kinds of memory-use errors at compile time or runtime, it does not eliminate all undefined behavior. And C++ code benefits from these newer standard abstractions only if it is refactored to use them. The C++ standard alone does not provide that safety; compiler and library implementations must supply the necessary checks.
LLVM and Clang: Making existing C++ code safer
Large companies like Google, Apple, and others have made significant contributions to the LLVM/Clang compiler and tooling stack to improve C++ memory safety.
Apple started the Clang Safe Buffers effort,3 where setting the compiler flag -Wunsafe-buffer-usage triggers diagnostics that identify error-prone pointer arithmetic, unchecked subscripting, and other patterns that often result in out-of-bounds indexing errors and pointer read/write errors.
The intended migration path is to wrap raw buffers in safer abstractions such as std::span, std::mdspan, std::vector, std::array, std::string_view, etc., and to preserve bounds information across APIs instead of dropping back to pointer-plus-size pairs.
Clang also provides [[clang::unsafe_buffer_usage]] and #pragma clang unsafe_buffer_usage so teams can mark compatibility boundaries and adopt the model incrementally.
The second major piece is libc++ hardening,4 which was also started by Apple with major support from Google.
This hardening adds default always-on runtime checks to important standard-library operations so that some classes of undefined behavior become reliably diagnosed failures.
In today's libc++, hardened support already covers important facilities including std::span, std::string_view, std::vector, std::string, std::mdspan, std::optional, and std::expected, with several iterator checks available when application binary interface (ABI) settings permit bounded iterators.
LLVM's C++ Safe Buffers documentation explicitly treats hardened libc++ and compiler diagnostics as complementary pieces of one programming model.
The third piece is static checking via the LLVM tool clang-tidy.5
Clang-tidy's cppcoreguidelines checks line up with the C++ Core Guidelines6 and enforce proper usage of modern C++ encapsulated types (instead of using raw pointers).
Those checks operationalize the Core Guidelines' bounds rules: avoid pointer arithmetic, avoid array-to-pointer decay, prefer std::span and other types, and make ownership explicit.
While this is a significant improvement over the use of raw C++ pointers, it is not yet fully memory safe. But it is increasingly a practical engineering path rather than just advice. Existing unsafe C++ code can be incrementally refactored to use modern, safer C++ types and eliminate much of the undefined behavior that causes security and correctness problems.
How companies and organizations are using LLVM to improve C++ memory safety
Apple has become one of the most visible contributors to and users of LLVM C++ memory-safety work.
Its C++ language support page7 documents that Xcode 16 added C++ standard library hardening for Apple Clang and libc++, with production-oriented modes such as Yes (fast) and Yes (extensive) as well as a stricter debug mode.
Apple's WWDC25 session Safely mix C, C++, and Swift8 is especially revealing because it connects several strands into one developer workflow.
Google is pursuing the same LLVM direction at a much larger deployment scale and has published some of the best public evidence that the approach can pay off. In November 2024, Google reported on retrofitting spatial safety to hundreds of millions of lines of C++.9 After enabling hardened libc++ and rolling it out carefully, Google reported finding more than 1,000 bugs, estimating that the approach would prevent 1,000 to 2,000 new bugs per year at its current development rate, and reporting a 30% reduction in its baseline segmentation-fault rate across production. The same post claimed that LLVM hardened libc++ had already disrupted an internal red-team exercise and would have prevented another exploit path that predated deployment. The WG21 paper P3471,15 authored by Apple libc++ maintainers, cites Google's deployment experience and notes a performance impact as low as 0.3%.
Google is not stopping with library hardening. The company is expanding checking beyond the standard library and migrating code toward Clang Safe Buffers.3 Hardened containers catch misuse at access sites, while Safe Buffers aims to move APIs and data flow away from raw pointers so that bounds information is preserved instead of constantly discarded.
At the same time, Google has acknowledged that more work needs to be done. In its March 2024 Secure by Design perspective on memory safety,10 Google made the case that retrofitting protections into C++ is valuable but not sufficient. In Android 13, for example, Google reported that annual memory-safety vulnerabilities had dropped from 223 in 2019 to 85 in 2022, and from 76% to 35% of Android's total vulnerabilities, while new native code increasingly moved to Rust and other memory-safe languages.
The C++ Core Guidelines and the C++ standards pipeline
The C++ Core Guidelines6 provide best practices for using newer C++ standards and standard-library types to eliminate C++ memory errors.
Their bounds-safety profile says not to use pointer arithmetic, not to rely on array-to-pointer decay, to prefer std::span, and to avoid standard-library facilities that are not bounds-checked.
The guidelines also separate bounds safety, type safety, and lifetime safety.
That same separation is now showing up in proposals being considered by the ISO C++ standards group.
Bjarne Stroustrup's P3274, A framework for Profiles development,11 argues for portable, tool-supported profiles that can offer memory-safety guarantees rather than just style advice, while related papers such as P3081,12 P3402,13 and P344614 explore specific safety directions.
The most concrete C++26 proposal with existing deployment experience is P3471, Standard library hardening,15 which standardizes "hardened implementations" and "hardened preconditions" so that operations such as out-of-range indexing on containers, std::string_view, std::span, and std::mdspan become contract violations instead of silent undefined behavior.
P3608, Contracts and profiles: what can we reasonably ship in C++26,16 argues for a minimalist package: ship the Contracts minimum viable product (MVP), ship library hardening, and use a standardized profile mechanism to enable that hardening.
C++26 now includes the Contracts MVP with pre, post, and contract_assert, while deliberately leaving out contracts on virtual functions and function pointers to keep the feature set narrow.
The C++26 standard is also slated to include library hardening via hardened preconditions checked at runtime in hardened implementations.
What did not make it into C++26 is the profiles framework itself; broader profiles work, including the framework and core safety profiles, has been deferred to later standardization work (likely C++29).
Therefore, C++26 software will have hardening enabled by implementation-defined mechanisms such as compiler flags rather than a standard profile syntax.
How this compares with Rust
Discussions about C++ memory safety do not exist in a vacuum and almost always involve arguments for and against migrating C++ software to Rust.18
In Rust, ownership, borrowing, and slices are presented as core language mechanisms that enforce memory safety mostly at compile time without the need for garbage collection or most runtime checks (except for bounds checking).
Unsafe operations are still possible, but they are pushed into explicitly marked unsafe regions.
Rust software is largely memory safe by construction if it compiles, but it still requires runtime checks for certain operations, such as array indexing when the compiler cannot prove that an index is in bounds.
C++, by contrast, is assembling a layered system of safer library types, compiler diagnostics, runtime hardening, static analysis, and opt-in profiles. Those layers are useful, but they do not make the language uniformly memory safe, and they are easier to bypass accidentally or intentionally.
However, C++ has one advantage that rewriting code to Rust does not: it can be incrementally improved in place inside enormous deployed systems while preserving existing ABIs, interoperability, and performance envelopes. The recent C++ standards work and the added checking built into LLVM/Clang make this possible today. This offers a realistic migration path for codebases that will still be largely C++ for years to come.
For greenfield components that parse untrusted input or sit on critical attack surfaces, Rust often offers the cleaner answer. For established scientific and HPC codebases, modern C++ plus LLVM hardening and guideline-driven cleanup may be the most feasible way to remove a large fraction of today's risk.
Why pressure for memory safety is increasing
This technical work is happening in a government policy environment that is now openly focused on memory safety. In the United States, Executive Order 14028, Improving the Nation's Cybersecurity,19 signed on May 12, 2021, pushed federal attention toward software security and secure development practices. That direction continued through the White House's 2023 National Cybersecurity Strategy, Executive Order 14144, Strengthening and Promoting Innovation in the Nation's Cybersecurity,20 signed on January 16, 2025, and the June 6, 2025 amendment to that order.
The policy conversation has also become more explicit about language choice. CISA's Secure by Design effort21 now includes guidance such as The Case for Memory-Safe Roadmaps. That does not mean every C++ codebase must be rewritten. It does mean the burden of proof has shifted. Organizations are increasingly expected to show a credible plan for reducing memory-unsafe attack surface, whether by migration, hardening, safer subsets, or some combination of these.
Potential for memory-safe C++ in the future?
One useful way to think about current strategies for C++ memory safety is as a stack of complementary defenses, each aimed at a different source of memory-related undefined behavior.
Clang Safe Buffers and Core-Guidelines-oriented clang-tidy checks can drive raw-pointer buffer manipulation out of ordinary code and replace it with standard C++ containers and views that preserve bounds information across APIs.3,5,6
The libc++ hardening already turns many out-of-range operations on standard-library types into diagnosed failures rather than silent undefined behavior,4,15 and a stronger future runtime-checking mode, especially in debug builds, could plausibly go further by tracking the lifetime of views and iterators in the style of the Teuchos ArrayRCP and ArrayView debug-mode checking, which demonstrated that dangling-view and invalidated-reference errors can be caught reliably at runtime with tolerable development-time overhead.22
The C++ standards work suggests how the remaining major categories could be addressed more systematically.
A future profiles framework could combine an std::bounds profile to inject bounds checks, an std::lifetime profile to reject manual delete and free and to check for null dereference, and an std::initialization profile to verify that objects are initialized before use.11,12,13
That same direction could then be extended with a std::type profile to restrict unsafe casts and wrong-type access, plus an invalidation profile to prevent use of iterators, pointers, references, and views after a container mutation or destruction.14,17
Together with custom clang-tidy checks that discourage persistent raw C++ references, future LLVM lifetime and invalidation analysis, and other rule checks, this can create a subset of C++ that could come reasonably close to being memory safe in practice for most C++ HPC programs, while still maintaining near-maximum runtime performance.5,6,11
What would still remain are the places where C++ must deliberately escape that checked subset: low-level runtime and library internals, interoperability layers with C, Fortran, and other languages, operating-system APIs, custom allocators and raw-storage manipulation, and any code that explicitly suppresses safety checks for compatibility or performance reasons.11,22 In that sense, the most plausible future is not that every corner of ISO C++ becomes uniformly memory safe, but that tool-enforced safe regions become large enough that most scientific application code can be written in a style where memory-related undefined behavior is rare, diagnosable, and mostly confined to trusted boundary code.
But in the end, complex general programs cannot both be 100% safe and run at the maximum possible performance.
Even many Rust programs contain some unsafe code in order to achieve the necessary performance.23
And there will always be a need to hand off data between different libraries, which increases the surface area for memory-related defects.
Therefore, there will always be a trade-off between features, performance, and safety.
And from a correctness and security perspective, even if you eliminate all memory errors, you are still left with the other 19 of the CWE Top 25 Most Dangerous Software Weaknesses in 2025.1
A practical takeaway for scientific software teams
For HPC research software groups, the most useful stance is probably neither denial nor panic. Modern C++ can be made significantly safer today, but that requires conscious toolchain and API choices.
If you maintain a C++ codebase, a practical starting point is to target at least C++20 where feasible and prefer containers and views such as std::vector, std::array, std::span, std::mdspan, and std::string_view.
Teams can also enable libc++ hardening in development and CI before wider deployment, and run clang-tidy with the relevant cppcoreguidelines bounds and ownership checks as well as the Clang compiler option -Wunsafe-buffer-usage to ferret out remaining memory-unsafe C++ usage.
To summarize, the most honest answer to "Is Modern C++ Memory-Safe?" is "not completely," but it is much safer than what was possible just a few years ago. The interesting part of the current moment is that memory safety with C++ is no longer just a language-design conversation. LLVM/Clang is enabling it, Apple and Google are shipping it, and the C++ standards process is catching up.
The impact of artificial intelligence
As this article is being written, it is impossible to ignore the impact that artificial intelligence (AI), large language models (LLMs), and AI coding agents will have on C++ memory safety. It is likely that in the next few years (and perhaps much sooner), improved AI models and coding agents will automate much of the work needed to build out the tooling for memory-safe C++ and to incrementally refactor existing legacy C++ codebases to use memory-safe C++ types and idioms. While any type of change to existing software can be risky, the incremental approach of refactoring existing C++ code is likely less risky than trying to completely rewrite large, complex codebases (e.g., convert from C++ to Rust). It will likely take artificial superintelligence (ASI) to rewrite complete legacy codebases from C++ to Rust, but much less capable AI models and tools can likely incrementally refactor C++ code. (We may not even need artificial general intelligence (AGI) to achieve the latter.) It will be fascinating to see what is possible in the near future to automate away memory-safety issues in legacy C++ codes through the application of these tools and updated standards. This is an exciting time to be a C++ HPC developer and researcher!
Author bio
Roscoe A. Bartlett earned a PhD in chemical engineering from Carnegie Mellon University, researching numerical approaches for solving large-scale constrained optimization problems applied to chemical process engineering. At Sandia National Laboratories and Oak Ridge National Laboratory, he continued research and development in constrained optimization, sensitivity methods, and large-scale numerical software design and integration for computational science and engineering (CSE). Dr. Bartlett's recent work focuses on the application of agentic AI to reduce technical debt in large-scale legacy C++ HPC software projects.
References
- 1CWE Top 25 Most Dangerous Software Weaknesses in 2025
- 2CWE Top 25 Most Dangerous Software Weaknesses in 2023
- 3Clang Safe Buffers
- 4libc++ Hardening Modes
- 5clang-tidy
- 6C++ Core Guidelines
- 7Apple C++ language support in Xcode
- 8Safely mix C, C++, and Swift
- 9Retrofitting spatial safety to hundreds of millions of lines of C++
- 10Secure by Design: Google's perspective on memory safety
- 11P3274R0: A framework for Profiles development
- 12P3081R2: Core safety profiles for C++26
- 13P3402R2: A Safety Profile Verifying Initialization
- 14P3446R0: Profile invalidation - eliminating dangling pointers
- 15P3471R4: Standard library hardening
- 16P3608R0: Contracts and profiles: what can we reasonably ship in C++26
- 17P3984R0: A type-safety profile
- 18The Rust Programming Language: Slices
- 19Executive Order 14028: Improving the Nation's Cybersecurity
- 20Executive Order 14144: Strengthening and Promoting Innovation in the Nation's Cybersecurity
- 21CISA Secure by Design
- 22Teuchos Memory Management Classes paper
- 23'Against the Void': An Interview and Survey Study on How Rust Developers Use Unsafe Code


