LUNAROPS · OPERATIONAL UPLINK 100% UPTIME 1,247d POSTS 893 JEFF.MOON@LUNAROPS.DEV UTC --:--:--

Memory Safety in 2026: The CVE Data, the Regulatory Cliff, and the Long Goodbye to C

securitymemory-safetyrustcarboncppcyber-resilience-actcisasystems-programming

For about as long as anyone has measured it, roughly seventy percent of the serious security vulnerabilities in large C and C++ codebases have been memory-safety bugs. Microsoft has reported that number across more than a decade of its own CVEs. The Chromium project reports the same proportion for its high-severity issues. The figure barely moves, which is the entire problem: it has survived every generation of better tooling, better discipline, better static analysis, and better intentions. For twenty years the industry treated this as a craftsmanship failure — programmers should simply write more careful C — and for twenty years the seventy percent did not budge. What changed by 2026 is not the languages, which have existed for years, but the framing. Memory safety stopped being a matter of professional pride and became a matter of regulatory liability and hard empirical evidence. Governments now name memory-unsafe languages as a defect class in formal policy, and for the first time there is field data proving that the only thing that reliably drives the seventy percent down is to stop writing the unsafe code, not to write it more carefully. This is the story of that transition: the proof, the adoption, the law, and the genuinely difficult path of getting off a hundred billion lines of C.


The 70% Number and Why Discipline Never Fixed It

A memory-safety bug is a violation of an invariant the C and C++ languages decline to enforce: that you only read and write memory you legitimately own, of the size you think it is, while it is still alive. Break the spatial half and you get buffer overflows and out-of-bounds reads and writes. Break the temporal half and you get use-after-free, double-free, and use-after-return. These are not exotic. They are the daily output of competent engineers writing ordinary code, because the language provides no enforced notion of ownership or bounds and trusts the programmer to track both perfectly across every call path, forever.

The reason discipline never closed the gap is that the failure is emergent and statistical, not local. A use-after-free is rarely visible at the line where the bug lives; it is a property of the interaction between an allocation here, a free there, and a dereference somewhere else entirely, often across module boundaries written by different people years apart. No amount of staring at a single function reveals it. The tooling we built to compensate — AddressSanitizer, Valgrind, fuzzing — is genuinely valuable but fundamentally probabilistic. A sanitizer finds the bugs your test inputs happen to trigger; a fuzzer finds the ones it happens to reach before you run out of compute. None of them proves the absence of the bug class, and so the rate at which new memory-safety defects enter a C++ codebase stays roughly constant per line of new code. You can find and fix faster, but you are bailing a boat that takes on water at a fixed rate. The seventy percent is what a constant inflow looks like when you measure the result.


What “Memory Safe” Actually Means

A memory-safe language is one in which the bug classes above are prevented by construction rather than caught by audit. There are two broad mechanisms. Garbage-collected languages — Go, Java, C#, Python — achieve temporal safety at runtime by never freeing memory that is still reachable, and spatial safety by bounds-checking array access. Rust achieves both largely at compile time: its ownership and borrow-checking rules make use-after-free and data races into type errors the compiler rejects before the program runs, and it inserts bounds checks for indexing that the optimizer often elides when it can prove them redundant.

Bug class C/C++ default GC languages Rust (safe subset)
Buffer overflow (spatial) Undefined behavior Bounds-checked at runtime Bounds-checked, often elided when provable
Use-after-free (temporal) Undefined behavior Prevented by GC reachability Rejected at compile time by borrow checker
Double-free Undefined behavior Not possible Rejected at compile time
Data race Undefined behavior Possible (Java/Go) Rejected at compile time (Send/Sync)
Uninitialized read Undefined behavior Prevented Prevented (no uninit by default)

Two honest caveats keep this from being a religious claim. First, every practical safe language has an escape hatch — Rust’s unsafe, the foreign-function interface in all of them — and bugs concentrate in exactly those seams. Safety is a property of the safe fraction of the code, and the engineering task is to keep that fraction enormous and the unsafe seam small and audited. Second, “memory safe” is not “bug free.” It eliminates a specific, dominant, exploitable class. Logic errors, injection, and bad cryptography are entirely unaffected. The case for memory safety is not that it solves security; it is that it deletes the single largest and most reliably exploitable category, the one that has paid for two decades of zero-days.


The Empirical Proof: Android’s CVE Collapse

For years the counterargument to rewriting was economic and unanswerable in the abstract: surely the right move is to harden the billions of lines we already have, not to rewrite them in a new language. In 2024 Google’s Android security team published the data that reframed the entire debate, and it is the most important empirical result the field has produced.

Across Android, the fraction of total vulnerabilities that were memory-safety issues fell from about seventy-six percent in 2019 to about twenty-four percent in 2024. The startling part is how. Google did not achieve this by rewriting old C and C++. The volume of unsafe code in Android kept growing over the same period. They achieved it by writing the new code in memory-safe languages — overwhelmingly Rust — and letting the old code age.

The mechanism behind the result is a principle Google called the decay of vulnerabilities. The density of latent bugs in a piece of code drops sharply the longer it has existed, because the most-exercised, most-fuzzed, most-audited paths get their bugs found and fixed over time. New and recently-modified code is where the overwhelming majority of fresh vulnerabilities live. That means the marginal dollar is wildly mis-spent on rewriting battle-tested old code and wildly well-spent on ensuring that the new code — which is where tomorrow’s CVEs would otherwise come from — is memory-safe from the first commit. Google reported millions of lines of Rust in Android and, strikingly, zero memory-safety CVEs originating in that Rust code. You do not have to rewrite the world. You have to stop adding to the unsafe pile, and the curve bends on its own.


Where Rust Actually Landed by 2026

The theoretical case has been winnable for years. What makes 2026 different is that Rust crossed from advocacy into the load-bearing infrastructure of the industry’s most conservative codebases.

In the Linux kernel, Rust support merged in 6.1 and has grown from a proof of concept into real, shipping drivers: the Apple Silicon GPU driver in the Asahi project, a Rust rewrite of the Android Binder driver, network and NVMe pieces, and the Nova driver effort for newer NVIDIA hardware. The road has been politically rough — high-profile maintainer friction and a couple of prominent contributors stepping back in 2024 — but the direction did not reverse. Microsoft has rewritten security-sensitive portions of the Windows kernel in Rust, including parts of the Win32k GDI region handling and its SymCrypt cryptographic library, with leadership publicly committing to the direction. In the cloud, the pattern is everywhere underneath you: AWS Firecracker and large parts of the Nitro system are Rust, Cloudflare replaced its nginx fleet with the Rust-based Pingora, and Rust is the default for new firmware and hypervisor work at multiple hyperscalers. Userland tooling has simply been conquered — ripgrep, fd, the Astral toolchain (uv, ruff), Deno, and a large share of the new JavaScript build tooling are Rust.

The honest friction is real and worth naming. Bidirectional interop with a large existing C++ codebase is painful, because Rust and C++ have incompatible object models and the boundary requires hand-maintained unsafe glue. Async Rust remains genuinely hard to learn and to reason about. Compile times are a tax. And hiring a team fluent in the borrow checker is still harder than hiring C++ engineers. None of these is fatal, and all of them are improving, but anyone selling Rust as frictionless is selling something.


Carbon, and Why Google Hedged

It is tempting to read Carbon, Google’s experimental C++ successor, as a competing memory-safety play. It is not, and understanding why clarifies the whole landscape. Carbon exists for the codebases that cannot practically adopt Rust precisely because of that interop friction: tens of millions of lines of C++ with deep bidirectional dependencies, where a clean-slate language with an impedance mismatch at the boundary is a non-starter. Carbon’s bet is seamless, bidirectional C++ interoperability — the ability to migrate file by file inside an existing C++ program — and its memory-safety story is explicitly a roadmap, not a delivered guarantee. As of 2026 Carbon remains experimental, pre-1.0, with no production users and no shipping safe subset. The deeper coverage lives in the companion piece on Carbon as a C++ successor; the point here is the positioning. Rust is the answer when you can write new, mostly-isolated components. Carbon is a bet on incremental migration for the codebases too entangled for that, and it is years from proving the safety half of its pitch.

Meanwhile the C++ committee is having its own version of the argument, and it matters because the bulk of the world’s systems code is not leaving C++ this decade regardless. Sean Baxter’s “Safe C++” proposal demonstrated that a genuine borrow-checked safe subset of C++ is technically possible. The committee’s center of gravity instead favored the lighter-weight “Profiles” approach championed by Bjarne Stroustrup and Herb Sutter — a mix of static and runtime checks enabled per translation unit. Safety advocates argue Profiles cannot deliver the guarantee that matters, and the live, unresolved question of 2026 is whether C++ can evolve a credible safe dialect fast enough to matter against the regulatory clock, or whether the regulators have already decided the answer is no. The modern language itself is covered in the C++20/23 deep dive; the safety debate is a layer above it and far less settled.


The Regulatory Cliff

The reason any of this moved from conference slides into kernel commits is that the economics changed, and the economics changed because the law did. Memory safety is now written into government policy as a named defect class with consequences attached.

In the United States, the Office of the National Cyber Director published “Back to the Building Blocks” in February 2024, a White House report that explicitly calls on the technology community to adopt memory-safe languages and frames memory-unsafe code as a national-security liability. CISA, jointly with the FBI and international partners, pushed “The Case for Memory Safe Roadmaps” and urged software manufacturers to publish concrete memory-safety roadmaps, with public messaging pointing at the start of 2026 as the line by which serious vendors should have one. None of this is yet a binding US statute, but it reshapes procurement, liability exposure, and the “secure by design” expectations that large buyers now write into contracts.

The European Union went further and made it law. The Cyber Resilience Act — Regulation (EU) 2024/2847 — entered into force in December 2024, with vulnerability-handling obligations applying from September 2026 and the main requirements from December 2027. It mandates security-by-design and secure-by-default for essentially any product with digital elements sold in the EU, imposes vulnerability-handling and disclosure duties across the support lifetime, and carries fines up to fifteen million euros or 2.5 percent of global turnover. It does not name Rust, but it makes shipping a predictable, well-understood class of exploitable defect into a compliance and liability problem rather than a reputational one.

Instrument Who Status / key date Teeth
ONCD “Back to the Building Blocks” US White House (ONCD) Published Feb 2024 Policy direction; shapes federal procurement
CISA “Memory Safe Roadmaps” CISA + FBI + partners Guidance; roadmaps urged by early 2026 Secure-by-design expectations, soft pressure
EU Cyber Resilience Act 2024/2847 European Union In force Dec 2024; full apply Dec 2027 Fines to €15M or 2.5% global turnover

The shift the table understates is psychological. For twenty years memory safety was an internal engineering virtue you could trade away under deadline pressure. It is now an external obligation with a regulator and a number attached, which is why conservative organizations that ignored the argument for a decade suddenly have Rust working groups.


The Honest Path: You Cannot Rewrite a Billion Lines

The single most common mistake in this conversation is imagining the goal is a Grand Rewrite. It is not, the math forbids it, and Android proved you do not need it. The realistic migration is a funnel, prioritized by where exploitation actually happens.

                ALL EXISTING C/C++  (cannot rewrite)
                          |
        ----------------------------------------
        |                 |                     |
   NEW code          BOUNDARY code         LEGACY core
   (highest ROI)     (parsers, network,    (old, fuzzed,
        |             attacker-reachable)   battle-tested)
        v                 v                     v
  WRITE IN RUST     REWRITE FIRST or       HARDEN IN PLACE
  (stop the bleed)  isolate / sandbox      (MTE, fortify,
                                            bounds-safety)
        |                 |                     |
        +--------- shrink the unsafe seam ------+

Three concrete tracks fall out of that funnel. First, stop the bleeding: write new components in a memory-safe language, which is the Android lesson distilled to one sentence. Second, rewrite by attack surface, not by line count — the parsers, decoders, and network-facing code that attackers can actually reach are worth rewriting or sandboxing first, because that is where the reachable bugs are. Where you do interoperate, keep the unsafe boundary tiny and audited, using interop tooling like the cxx crate or Google’s Crubit rather than hand-rolled FFI.

Third, harden the C and C++ you are keeping, because most of it is staying for years. The toolbox is unusually good in 2026:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
# Compile-time and runtime hardening for the C/C++ you keep
clang -O2 \
  -D_FORTIFY_SOURCE=3 \          # bounds-checked libc wrappers
  -fstack-protector-strong \      # stack canaries
  -fstack-clash-protection \
  -fsanitize=cfi -flto \          # control-flow integrity
  -fcf-protection=full            # Intel CET / branch protection

# Hardened C++ standard library (libc++): bounds & iterator checks in prod
clang++ -std=c++23 -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST app.cpp

Below the compiler, hardware is finally helping. ARM’s Memory Tagging Extension (MTE) tags allocations and pointers and traps on mismatches, giving probabilistic detection of both spatial and temporal bugs at production speed; it ships on recent Pixel hardware behind Advanced Protection and is the most deployable hardware backstop available. ARM’s Morello/CHERI prototypes go further, making spatial and temporal safety a property of the hardware capability model itself, though that remains research-grade. Apple deployed -fbounds-safety, a bounds-checked dialect of C, in its own kernels. None of these makes C memory-safe in the Rust sense, but together they meaningfully raise the cost of exploiting the code you cannot yet replace, and they buy time for the funnel to do its slow work.

The discipline that ties it together is the same one from the supply-chain attack anatomy: you cannot fix what you cannot inventory. Knowing which of your binaries are memory-unsafe, attacker-reachable, and unpatched is the prerequisite for spending the rewrite budget where the curve actually bends.


Verdict

The argument is over, and the interesting questions are now about execution. The seventy-percent figure stood unmoved for two decades not because engineers were careless but because memory-unsafe languages produce that defect class at a fixed rate no discipline has ever throttled, and the Android data settled the long debate about what to do: writing new code in a memory-safe language drove memory-safety vulnerabilities from three-quarters of the total to under a quarter in five years, without rewriting the legacy core, because vulnerabilities decay and new code is where the danger lives. Rust has crossed into the kernels, hypervisors, and userland tooling that run the industry, friction and all. Carbon and the C++ “Safe C++ versus Profiles” fight are the same problem seen from the other side — what to do about the codebases too entangled to leave — and that side is years from a guarantee.

What changed in 2026 is that none of this is optional anymore. The US has named memory-unsafe code a national-security liability, and the EU has attached fines of up to 2.5 percent of global turnover to shipping the predictable consequences. The honest path forward is not a rewrite and never was: write new code memory-safe, rewrite the attacker-reachable boundary first, harden the legacy core with _FORTIFY_SOURCE, hardened standard libraries, control-flow integrity, and hardware memory tagging, and shrink the unsafe seam until a human can audit it. The hundred billion lines of C are not going anywhere fast. But the curve has already started to bend, the regulators have removed the option of ignoring it, and for the first time the question is not whether the industry will get off memory-unsafe languages but how quickly, and at what cost, it can manage the decade-long goodbye.


Sources

Comments