Andrew Lilley Brinker
Memory safety—the property that makes software devoid of weaknesses such as buffer overflows, double-frees, and similar issues—has been a popular topic in software communities over the past decade and has gained special prominence alongside the rise of the Rust programming language. Rust did not invent the idea of memory safety, nor was it the first memory-safe language, but it did break the dam on the last major holdout context where memory safety had not yet been achieved: what we often call "systems programming."
Rust's big step function was to offer memory safety at compile time through the use of static analysis borrowed and grown out of prior efforts such as Cyclone, a research programming language formulated as a safe subset of C. Rust, by offering a memory-safe-by-default experience for the "systems" domain, where operating systems, databases, file systems, embedded software, and the like are made, suddenly presented a new possibility to public policymakers and to leaders of engineering organizations: the mass reduction of memory unsafety across any domain.
In the years since Rust hit the scene, tech companies have published experience reports on the adoption of Rust for production systems, whether through rewrites of existing code or by producing new modules in Rust that might have otherwise been written in C or C++. The numbers were broadly consistent: a roughly 70 percent reduction in memory-safety vulnerabilities. Rust, more than just promising memory safety, was demonstrably translating safety guarantees into practical improvements in software security. This evidence, turning the abstract benefits of a semantic improvement into bottom-line improvements in business costs (vulnerabilities are expensive for all involved), meant that organizations beyond just engineering groups began to take notice.
Of course, the choice of programming language is a contentious one. Languages do not exist in a vacuum, and the "right" language for a job is heavily path dependent. What languages do the developers already know? What's the timeline and budget for the project? How serious are the correctness constraints? The performance constraints? Do you expect to hire more developers, and what resources can you allocate to train them? If you're an open-source project, you might ask which languages would possibly bring in more developers to contribute. For any project, your answer might be determined by what other libraries or tools you will need to integrate.
What's more, many projects have already made their programming-language decision years ago—possibly decades ago. What should they do? If the code you have today is memory unsafe, in C or C++, how can you pursue safety without throwing the whole thing out?
In some circles, the answer given might be to "rewrite it in Rust" to replace legacy software written in memory-unsafe languages with new Rust equivalents. The benefits, supporters argue, are clear: comparable performance, modern tooling, and memory safety.
Yet, experienced developers know rewrites are expensive and frequently misguided . Often, demands for large-scale rewrites are not a carefully reasoned argument about tradeoffs, but an aesthetic criticism of code that looks "ugly" or "too old." If anything, those calling for mass rewrites show their own inability to do the difficult work of understanding and working with an existing codebase that does a job and does it well.
There are paths between accepting memory unsafety as the cost of doing business or performing a mass rewriting of stable systems in a new language to achieve safety. Reflexive rejection of a move to memory safety is misguided, especially when the benefits of memory safety can be achieved in a cost- and schedule-efficient way.
If you're not yet sold on the value of memory safety, this article is for you. The goal in writing it is to treat the question of pursuing memory safety in legacy systems with the seriousness and rigor that it deserves.
Pursuing memory safety is worthwhile, with or without Rust, and I'd like to convince you to try.
In Budget, On Schedule
Software systems do not exist in isolation; software is built to do things, to serve the needs of businesses and individuals by making systems more efficient or automatic. The development of software is constrained not by the theoretical limits of software's abilities, but by the cost and schedule limitations of the team building it.
In "The Case for Memory Safe Roadmaps," a collection of government agencies from the "Five Eyes Countries" (the United States, United Kingdom, Australia, New Zealand, and Canada) collectively recommended that organizations develop roadmaps for transitioning their software development efforts to memory-safe languages.
It's worth being clear here: Their focus is on roadmaps, and they very explicitly accept and discuss the challenge of the cost and schedule impacts of any transition toward memory safety.
Budget and schedule constraints and the desire for efficiency are part of what motivates the creation of software in the first place. Once that software is in place, it's frequently mission critical, having replaced the knowledge and labor of people who would have previously done the jobs the software now performs. Instead of accountants, a company may have accounting software, with a smaller number of accountants who know how to interact with the software and use it to perform their own jobs built on the knowledge the software provides with its data and embedded business logic.
Rewrites to critical software systems are risky precisely because the software itself is so important. Rewriting a software system, whether as an in-place rewrite where components are swapped out piece by piece, or as a wholesale rewrite with a cutover date, risks the proper functioning of the business if the rewrite fails .
Complex, long-running software systems can face other severe constraints as well. They may be unable to be turned off—because, having become so business critical and time sensitive, any attempt to bring them offline for maintenance or replacement is unacceptable. They may also have become lost artifacts, where the expertise of the individuals who created them or previously worked on them is lost because it wasn't transferred to newer engineers, resulting in a current team that does not understand the system or feel comfortable making changes to it.
There are also ongoing costs associated with the development of software that might have to be diverted to support even an incremental rewrite. Depending on the business, there might not be funding available to support an increase to the development team, so diversion of resources toward a rewrite would mean reduction in the delivery of features for the project, which may be untenable.
All of this is to say that rewrites, even incremental ones, are business decisions that have to be made as tradeoffs with other strategic goals. While motivated developers can make the best case possible for the upside of a rewrite, they must also grapple with the businesses' needs to deliver features and address bugs impacting users of the system today.
At the same time, a transition to memory-safe languages can bring benefits beyond just the safety (and thereby security) claims that are often given priority in these discussions.
Memory-safety violations, such as null pointer dereferences or indexing outside of the bounds of a memory buffer, can result in denial of service (or, in the context of the classic security CIA triad, availability failures) of the relevant software. This might mean on-call pages to respond to a production incident, a failure to meet service-level agreement guarantees for customers, or reduced revenue from lost customers or interruption of business operations.
Memory-safety issues are also often a central building block in a kill chain for achieving remote code execution by attackers. Even as far back as "Smashing the Stack for Fun and Profit" in 1996, we could see cybersecurity professionals documenting how to turn a buffer-overflow weakness into remote code execution and full access to the host. With that foothold in place, attackers can begin to exfiltrate data, move laterally within a network, escalate privileges, lock down a system with ransomware, conscript a host into a botnet, and more.
Software problems are cheaper to fix the earlier they occur in the software development lifecycle. In the long term, stopping a bug from being written is cheaper than responding to a bug bounty submission or triaging a production outage.
This is not to say that all moves toward memory safety are cost effective or that all roadmaps for memory safety should be as aggressive as possible, but it is intended to make clear that there are both costs and savings to be had with any transition to memory safety.
Setting the Goalposts
What is memory safety? There should be a table-stakes answer to that question to have in hand amid the push toward memory safety in public discourse, but there is not a single, fully agreed-upon, and rigorous definition. There is a new effort, announced in a recent article published in Communications of the ACM, to develop a standard definition of memory safety, but it is just beginning.
There is, however, a rough consensus among practitioners of what kinds of program behaviors are memory unsafe. That's a good place to start.
My favorite short definition comes from Michael Hicks, an academic who works on programming languages:
"[A] program execution is memory safe so long as a particular list of bad things, called memory-access errors, never occur:
• Buffer overflow
• Null pointer dereference
• Use after free
• Use of uninitialized memory
• Illegal free (of an already freed pointer, or a non-malloc-ed pointer)"
You will sometimes see these categories broken down further, into spatial and temporal memory safety. Spatial covers memory-safety issues arising from accessing locations in memory that a program should not have access to (like a buffer overflow); temporal covers operations on memory done in the wrong order: for example, reading memory before it is initialized, trying to free an already freed pointer, or using a pointer after it has been freed.
There's also the CWE (Common Weakness Enumeration) category for memory-safety issues, which decomposes Hicks's list into more granular options. CWE is a taxonomy of software weaknesses, or as CWE puts it: "condition[s] in... software... that, under certain circumstances, could contribute to the introduction of vulnerabilities."
In CWE's memory-safety category, "buffer overflow" is further broken down into six different, more-specific weaknesses, some of which are further decomposed into their own variants. This can be useful when maximum precision is warranted but is perhaps too much detail for the purposes of this article.
These definitions provide a reasonably clear picture of what constitutes memory unsafety. So, memory safety is when a program is guaranteed not to have those weaknesses. This can be achieved by compile-time constraints on the semantics of programs or by runtime management of memory by a garbage collector, so long as the guarantee is upheld.
This is often when perceptive onlookers will cry foul. Rust permits unsafety! There's a whole unsafe keyword! How is that meaningfully different from the guarantees of C or C++?
Of course, they're right. Rust does permit programmers to write unsafe code, but as anyone who works in safety or security will tell you, defaults matter. In fact, defaults matter a lot!
Let's use seat belts as an example. Seat belts became generally mandatory across the United States between the late 1980s to the early 1990s. In 1985, when mandatory seatbelt laws first saw passage among the states, seatbelt usage sat at 21 percent of riders. In 1994, the average seatbelt usage rate in the U.S. was 58 percent. As of 2017, it was 89.7 percent. That change in defaults led to massive increases in seatbelt usage and therefore saved more lives. The National Highway Transportation and Safety Administration estimates that in 2017 alone, seatbelts saved the lives of nearly 15,000 Americans.
The same truth applies in software. Before version 4.0.0 (published in 2017), Redis, the extremely popular key-value store, offered no access controls in its default configuration. Frequently, new users of Redis would unintentionally expose their instance publicly, and this insecurity would result in data spills or become a vector for host exploitation. As of version 4.0.0, Redis enters a "protected mode" when run with its default configuration and without password protection. This limits access to loopback interfaces. As the Redis company itself has since touted, the introduction of protected mode has caused the number of publicly accessible Redis instances tracked on Shodan.io, a popular internet host aggregator, to decline substantially. In 2017, it had identified roughly 17,000 exposed Redis instances; in 2020, that number had declined to 8,000 in an audit by security company TrendMicro.
Bringing it back to memory safety, we can and should think of memory-safety guarantees by languages as a continuum, and we can split languages between "memory safe by default" and "non-memory safe by default" groups. This framing, recommended by the OpenSSF's (Open Source Security Foundation's) Memory Safety SIG (Special Interest Group), makes the options clearer:
• Using memory-safe-by-default languages
• Using memory-safe-by-default languages to interface with non-memory-safe-by-default languages
• Using non-memory-safe-by-default languages
Here, memory-safe-by-default languages include not only Rust, but also common garbage-collected languages such as Java, C#, Go, Swift, Python, Ruby, and more. Non-memory-safe-by-default languages include C and C++ most notably, but also Zig, which may be surprising to those who have watched memory-safety discussions from the sidelines.
While Zig does provide more ergonomic options for programmers to write memory safe programs themselves, Zig is not a memory-safe language, because it does not guarantee memory safety even in its most conservative configuration. Jamie Brandon's breakdown of Zig's memory safety is a good walkthrough of why Zig's guarantees are insufficient.
With a shared understanding of memory safety and memory-safe languages, let's now dig into the concrete strategies for pursuing memory safety in real-world programs.
Strategies for Safety
Any of the following strategies are intended to maximize the benefit of memory safety while minimizing the cost of pursuing it. The specific choice of which approach is right is context dependent and should be made with consideration of the importance of the component, the current and new target language, the team involved, and the timetable.
Make new code memory safe
The first and most obvious option is to make new code memory safe—that is, to write new components in a memory-safe language. While this seems simple, you must address certain caveats to make this approach successful.
First, you are unlikely to reap the benefits of memory safety if you try introducing memory-safe code alongside new memory-unsafe code. Think of it this way: In a fixed codebase that you continue to assure (via testing, code review, bug bounties, and more), the density of vulnerabilities decreases exponentially over time. As vulnerabilities become less and less dense in the codebase, the rate of new vulnerability discoveries also decreases, and so the overall assurance level of the code increases. The riskiest thing you can do to a codebase is change it. In the case of memory-unsafe languages, that change can induce memory-safety vulnerabilities.
The Google Chrome and Android teams have published extensively about their experiences incentivizing a move to memory-safe languages in their codebases. They instituted a rule called the "Rule of Two," where all new code must be either sandboxed or in a memory-safe language. In practice, because sandboxing is difficult, this naturally gave developers incentive to pursue memory safety in most cases.
Surprisingly to the team, they reaped the benefits of this new policy across the entire codebase—even the parts that weren't rewritten. Because they had certain assurances about the new code inherent in the safety mechanisms it came with, they could focus assurance efforts on old code, which was now static. Through this, they not only reduced the overall rate of memory-safety vulnerabilities in the codebase, but also decreased the prevalence of vulnerabilities overall.
Target rewrites to critical components
Sometimes, rewriting code in a memory-safe language can be the right choice, but this is often a path to pursue only once you fully understand the challenges faced by the current memory-unsafe code.
Early in its history, the development of Rust was funded by Mozilla, makers of the Firefox web browser, and the flagship Rust project besides Rust's own compiler was the Servo web-rendering engine. Despite this, the first actual Rust code that Mozilla integrated into Firefox was not Servo; it was an MP4 video file parser. They replaced the existing C++ parser with one written in Rust, moving from a memory-unsafe language to a memory-safe language, because it had long been a source of vulnerabilities. Firefox needs to parse MP4 files from untrusted sources, and failures to correctly handle that parsing could be dire. For Mozilla, it was a small but security-critical surface area that made sense to target for a rewrite.
Another helpful tool for targeting is Kelly Shortridge's SUX Rule: target code that is Sandbox free, Unsafe, and eXogenous. This means you should prioritize rewriting code that processes untrusted (exogenous) input, runs without a sandbox, and is written in a memory-unsafe language. Reviewing your own codebase for these areas can be a fast way to identify critical paths with high risk of exploitation in the presence of memory-safety vulnerabilities.
Wrap unsafe code with safe interfaces
When fully rewriting existing memory-unsafe code to a memory-safe language is not feasible, it might instead make sense to wrap it in a memory-safe interface. This does still lay the burden of ensuring safety properties on the programmer, both for the original code in the memory-unsafe language and for the correctness of the interface, but it then permits building safe and trusted new code on top of the old code. If you continue to work to assure the old code with techniques such as fuzz testing, analysis by sanitizers, or formal modeling, you can gain increased confidence in the latent unsafe code being wrapped.
This is in fact how many of the Rust standard library's common container types are written. Under the hood, they contain unsafe code to manage buffers and pointers in a way that is as efficient as possible, but the interface provided to the user does not give access to any materials (buffers, pointers, lengths) that would permit the user to violate memory-safety guarantees.
This "wrapping" approach helps constrain the "blast radius" of memory-unsafe code that can't feasibly be removed or replaced and constrains the auditing scope and assurance costs of that code as well.
"Get good" is not a strategy
There is a common reply in conversations about memory safety, coming from the most hardcore skeptics: Programmers should just write better code. They argue, explicitly or implicitly, that programmers who benefit from the guardrails of memory safety are bad programmers, and that real programmers are sufficiently skilled that they do not need a machine double-checking their work.
Let's be clear: This is anti-intellectual nonsense—macho self-aggrandizement masquerading as a serious technical argument. You should not take it seriously and should consider someone advancing this argument as fundamentally unserious and to be ignored.
There is no step function in quality of work in the history of human achievement that happened because people one day woke up and decided to be better at their jobs. Improvements in productivity or quality or reductions in error and harm happen because of the invention of new techniques, processes, and tools.
Reductions in traffic fatalities in the 1980s and 1990s didn't happen because drivers suddenly got better at driving; they happened because states enacted mandatory seat-belt laws.
While individuals can become more skilled at their jobs, working faster or producing fewer errors, large groups of people generally don't do so without some force that works to provide incentive or enable that change. Even when improvements are nontechnical, they come from enhancements to process or incentives for behavior. Over the past several decades, hospitals, for example, have reduced in-hospital mistakes because of increased use of standard checklists and provisioning of common materials needed for emergencies in crash carts.
Programmers who argue against memory safety by arguing for mass self-improvement are posing an impossible future as an alternative against a credible opportunity for improvements in software safety and security. While there are credible case-specific arguments against individual paths to memory safety, they do not include sudden mass improvement of skill and quality across the industry. It's important to make this clear.
Regulations and Requirements
No, governments are not banning C or C++
One specter of the conversations around memory safety is whether the use of memory-unsafe languages will become generally unacceptable, either through formal government regulation or a rise in common requirements for software purchasing.
Today no agency, either in the U.S. or outside of it, regulates against the use of languages that are non-memory safe by default. Nor are there purchasing requirements in place calling for the use of memory-safe-by-default languages or even the presence of memory-safety roadmaps, at least for governments.
The Five Eyes report mentioned previously, "The Case for Memory Safe Roadmaps," recommends that organizations establish roadmaps for the pursuit of memory safety, but this is nonregulatory, and no amendments have been made to federal software acquisition policy to require such a roadmap in the U.S. or elsewhere. The U.S. is not outlawing C or C++. While these agencies have recommended moving away from these languages for future software development, they have not recommended indiscriminate mass rewrites of existing code.
Also note that the processes for establishing regulation or requirements would face challenges and, whether successful or not, would be slow to take effect and offer ample time for feedback and consideration.
First, regarding the prospect of regulation around memory safety in the U.S., such regulation would need to be pursued by a relevant agency that can establish a relevant jurisdiction. With the end of Chevron deference in U.S. law, a requirement that judges defer to U.S. regulatory agencies' determinations in most cases that was abolished in 2024 by the U.S. Supreme Court in Loper Bright Enterprises v. Raimondo, this pursuit of memory-safety regulation would also likely need to be explicitly backstopped by Congressional mandate to ensure it survived legal challenges where judges may overrule agency rulemaking.
Second, regarding acquisition requirements (the federal government's term for the rules around purchasing done by the government), the FAR (Federal Acquisition Regulation) would need to be updated to incorporate requirements for memory safety. For reference, in 2020 President Biden signed Executive Order 14028, which included a request that federal agencies pursue an amendment to the FAR to require inclusion of an SBOM (software bill of materials) for all software purchased by the federal government. To date, those changes have not been made, and no such requirement is in place within the FAR.
This is not to say that regulation or future federal purchasing requirements are impossible, but simply to point out that none are in place today, and any such changes would take time to be enacted and implemented.
The U.S. government's role around memory safety has so far been to act as cheerleader and promoter of the idea, including with the Office of the National Cyber Director's report, "Future Software Should Be Memory Safe;" CISA (Cybersecurity and Infrastructure Security Agency) et. al.'s "Case for Memory Safe Roadmaps;" and CISA's inclusion of memory-safety recommendations within its Secure by Design effort to collaborate with industry on improving software security.
Governments do not need to ban C or C++
Even without government mandate, many organizations in the tech industry have publicly stated their support for pursuing memory safety. In 2023, the Office of the National Cyber Director put out an RFI (request for information) seeking advice from the public on how best to support improving the security of open-source software. That RFI included an interest in the possibility of promoting adoption of memory-safe languages in open source.
Respondents to the RFI, which includes a number of universities, think tanks, corporations, and individuals, overwhelmingly supported a move toward memory safety. Few espoused a hardline goal of rewriting all existing code from non-memory-safe languages, but many did recognize the value of pursuing memory safety in new code, and in rewriting critical components in security-sensitive contexts when possible.
Some companies, most notably Google, have been especially vocal about their experiences with the value of memory safety. To them, the promise of memory safety is a reduction in security-related costs for long-term products such as the Google Chrome web browser or the Android operating system. By reducing memory-safety vulnerabilities at the source, they shift vulnerability costs left in the software development life cycle; the cost of catching a bug during development and stopping it from being shipped at all is orders of magnitude cheaper than the cost of receiving a security report, perhaps paying out a bug bounty, and then coordinating, preparing, and releasing a patch.
In some corners there has been a paranoia and fear that recommendations around memory safety from the U.S. government and others portend some forced end to C or C++. Bjarne Stroustrup, originator of C++ and continued major participant in the ISO Working Group that maintains the C++ specification, has recently begun to sound alarm bells in papers and speeches about the existential threat posed to C++ by failing to address the demands for memory safety, with clear reference to the possibility that software written in non-memory-safe-by-default languages may be disallowed or become practically untenable to market and sell in the future.
This fear is simultaneously overblown and correct. It is overblown to suggest the U.S. or any other government is close to outlawing C or C++, but it is correct to note that the benefits of memory safety are becoming clearer with each case study performed at scale and that we should expect natural incentives to slowly accrue larger use and developer interest in memory-safe languages over non-memory-safe languages. C and C++ won't die, but they will likely decline and become legacy languages like Cobol or Ada. They will still sustain some degree of interest and community, and a smaller number of developers will likely continue to be able to make their careers as developers in these languages, but they will be languages that present developers with fewer labor-market opportunities in the future and are unlikely to ascend in popularity and use again without substantial changes to address these safety deficiencies.
Safety Is Worth Pursuing
Memory-safe languages present the clearest opportunity today to substantially improve software security. While memory safety does not eliminate all classes of software weaknesses, it does eliminate a particularly pernicious class that leads to disproportionately severe vulnerabilities. While there are other techniques for addressing these kinds of weaknesses (for example, hardware-based approaches such as CHERI), they are less mature and generally more difficult to adopt at scale.
The state of possibility with memory safety today is similar to the state of automobile safety just prior to the widespread adoption of mandatory seat-belt laws. As car manufacturers began to integrate seat belts as a standard feature across their model lines and states began to require that drivers wear seat belts while driving, the rate of traffic fatalities and severity of traffic-related injuries dropped drastically. Seat belts did not solve automobile safety, but they credibly improved it, and at remarkably low cost.
The same can be done with memory safety. There is an opportunity to make substantial inroads at addressing a serious class of vulnerabilities while also, long-term, saving money on the development and operation of software systems. Memory safety is not a silver bullet, but it is a credible and cost-effective assurance technique that we as an industry should pursue aggressively. We do not need to wait for regulation to catch up; it is in our best interests to act today.
Acknowledgments
Thank you to Steve Klabnik, engineer at Oxide Computer Company and former member of the Rust Core Team, and to Michael Chernicoff, senior software engineer at MITRE, for reviewing this article and providing feedback prepublication.
Andrew Lilley Brinker is a principal engineer at MITRE, where he works on software security. He contributes to the CVE Quality Working Group, serves on the OmniBOR Core Team, and leads development of Hipcheck. He lives in southern California with his wife and two dogs.
Copyright © 2025 held by author. Publication rights licensed to ACM.
Disclaimer
Approved for Public Release; Distribution Unlimited. Public Release Case Number 25–1514. The author's affiliation with The MITRE Corporation is provided for identification purposes only and is not intended to convey or imply MITRE's concurrence with, or support for, the positions, opinions, or viewpoints expressed by the author.
![]()
Originally published in Queue vol. 23, no. 4—
Comment on this article in the ACM Digital Library
More related articles:
Louis Dionne, Alex Rebert, Max Shavrick, Konstantin Varlamov - Practical Security in Production
The challenge of improving the memory safety of the vast landscape of existing C++ code demands pragmatic solutions. Standard library hardening represents a powerful and practical approach, directly addressing common sources of spatial safety vulnerabilities within the foundational components used by nearly all C++ developers. Our collective experience at Apple and Google demonstrates that significant safety gains are achievable with surprisingly minimal performance overhead in production environments. This is made possible by a combination of careful library design, modern compiler technology, and profile-guided optimization.
Christoph Kern - Safe Coding
Safe coding embodies a modular, compositional approach to building and reasoning about the safety of large, complex systems. Difficult and subtle reasoning about the safety of abstractions is localized to their implementations; the safety of risky operations within an abstraction must rely solely on assumptions supported by the abstraction's APIs and type signatures. Conversely, the composition of safe abstractions with safe code is automatically verified by the implementation language's type checker. While not a formal method itself, safe coding is grounded in principles and techniques from rigorous, formal software verification.
Jinnan Guo, Peter Pietzuch, Andrew Paverd, Kapil Vaswani - Trustworthy AI using Confidential Federated Learning
The principles of security, privacy, accountability, transparency, and fairness are the cornerstones of modern AI regulations. Classic FL was designed with a strong emphasis on security and privacy, at the cost of transparency and accountability. CFL addresses this gap with a careful combination of FL with TEEs and commitments. In addition, CFL brings other desirable security properties, such as code-based access control, model confidentiality, and protection of models during inference. Recent advances in confidential computing such as confidential containers and confidential GPUs mean that existing FL frameworks can be extended seamlessly to support CFL with low overheads.
Raluca Ada Popa - Confidential Computing or Cryptographic Computing?
Secure computation via MPC/homomorphic encryption versus hardware enclaves presents tradeoffs involving deployment, security, and performance. Regarding performance, it matters a lot which workload you have in mind. For simple workloads such as simple summations, low-degree polynomials, or simple machine-learning tasks, both approaches can be ready to use in practice, but for rich computations such as complex SQL analytics or training large machine-learning models, only the hardware enclave approach is at this moment practical enough for many real-world deployment scenarios.
