Skip to content

5.6 Memory Safety and Language-Level Vulnerabilities

Throughout the previous sections, we have examined vulnerabilities as individual flaws to be discovered, patched, and remediated. But a deeper pattern underlies many of the most severe vulnerabilities in the software supply chain: memory safety. The majority of critical vulnerabilities in foundational software—operating systems, browsers, cryptographic libraries, network stacks—result from a category of programming errors that certain languages permit and others prevent entirely. Understanding this pattern has profound implications for supply chain security, as it suggests that language choice can eliminate entire vulnerability classes rather than addressing them one CVE at a time.

The Prevalence of Memory Safety Vulnerabilities

The data on memory safety vulnerabilities is remarkably consistent across major software projects:

Microsoft reported in 2019 that approximately 70% of all security vulnerabilities addressed in Microsoft products over the previous 12 years were memory safety issues. This figure has remained stable over time, despite decades of investment in secure coding practices, static analysis, and code review.

Google Chrome security data shows a similar pattern. Google's analysis found that roughly 70% of high-severity security bugs in Chrome are memory safety vulnerabilities—buffer overflows, use-after-free, and related issues.

Android security bulletins reflect the same distribution. Memory safety issues dominate the high-severity vulnerabilities patched in Android releases.

Apple products, though the company discloses less detailed statistics, show the same pattern in security updates—memory corruption vulnerabilities appear consistently in iOS and macOS patches.

This convergence around 70% is not coincidental. It reflects the fundamental nature of C and C++, the languages in which most foundational software is written. These languages provide direct memory access and manual memory management—powerful capabilities that also enable classes of errors that safer languages prevent by design.

Understanding Memory Safety Vulnerabilities

Memory safety vulnerabilities arise when programs access memory in unintended ways. To understand these, think of computer memory as a vast apartment building with numbered units. When your program runs, it rents specific apartments for its data—apartment 1000 for the username, apartments 1001-1100 for a file buffer, and so on. Memory safety vulnerabilities occur when programs accidentally read or write to apartments they have not rented, or continue accessing apartments after their lease has ended.

Several vulnerability classes fall under this umbrella:

Buffer overflows occur when a program writes data beyond the bounds of allocated memory. Imagine filling out a form with a 20-character field for your name, but writing 200 characters—the extra text spills over into adjacent fields, corrupting whatever was stored there. The classic stack buffer overflow can overwrite return addresses, enabling arbitrary code execution. Heap overflows corrupt adjacent data structures with similar potential for exploitation.

Use-after-free vulnerabilities occur when a program accesses memory after it has been deallocated. Continuing the apartment analogy: you move out of apartment 1000, but keep your key. A new tenant moves in. Later, you use your old key to enter and start rearranging furniture—or reading the new tenant's mail. If the memory has been reallocated for a different purpose, the access can corrupt unrelated data structures or leak sensitive information.

Double-free errors occur when memory is deallocated twice, potentially corrupting memory management structures and enabling exploitation. It is like returning your apartment key twice—the building manager's records become confused about who controls that unit.

Null pointer dereferences and uninitialized memory access can leak information or cause crashes, sometimes exploitable for denial of service or worse.

Type confusion occurs when memory is interpreted as a different type than intended, enabling attackers to manipulate program behavior.

These vulnerability classes share a common root: the languages permit operations that can corrupt program memory in ways the programmer did not intend. The compiler does not prevent these errors; they manifest at runtime, often exploitably.

For supply chain security, the implication is significant: dependencies written in C or C++ carry inherent risk of memory safety vulnerabilities. No amount of careful coding eliminates this risk entirely—the languages permit errors that even expert programmers make.

Government Guidance on Memory Safety

The prevalence and severity of memory safety vulnerabilities has prompted government agencies to issue formal guidance recommending transition to memory-safe languages.

The NSA published "Software Memory Safety" in November 2022, explicitly recommending that organizations shift development to memory-safe languages. The guidance stated:

"NSA recommends that organizations use memory safe languages when possible and bolster protection through code-hardening defenses."

The NSA specifically identified Rust, Go, C#, Java, Swift, JavaScript, and Python as memory-safe alternatives to C and C++.

CISA, jointly with the FBI, NSA, and international partners, published "The Case for Memory Safe Roadmaps" in December 2023. This guidance called on software manufacturers to develop plans for transitioning to memory-safe languages, particularly for new development and for security-critical components.

The CISA guidance acknowledged that immediate migration is impractical but emphasized that the status quo—continuously patching memory safety vulnerabilities without addressing their root cause—is unsustainable:

"Memory safe programming languages can eliminate entire classes of vulnerabilities... Memory safety should be a key consideration in product development."

This government attention elevates memory safety from a technical concern to a strategic priority, with potential implications for procurement requirements and regulatory expectations.

The Rust Transition in Critical Infrastructure

Rust has emerged as the leading memory-safe alternative for systems programming. Unlike garbage-collected languages (Java, Go, Python), Rust provides memory safety without runtime overhead through compile-time ownership checking. This makes Rust suitable for the performance-sensitive, resource-constrained environments where C and C++ have traditionally dominated.

Major infrastructure projects have begun incorporating Rust:

Linux kernel accepted Rust as a supported language for driver development starting with kernel 6.1 (December 2022). While the core kernel remains in C, new drivers can be written in Rust, enabling memory-safe development for new functionality. This represents a significant philosophical shift for a project that has used C exclusively for over 30 years.

Android has progressively increased Rust usage. Google reported that as of 2023, roughly 21% of new native Android code was being written in Rust. Google's analysis showed that as Rust adoption increased, memory safety vulnerabilities in Android decreased—providing empirical evidence for the security benefits.

Windows includes Rust components, with Microsoft actively rewriting portions of the Windows kernel in Rust. The company has been public about its Rust investment and its motivation in reducing memory safety vulnerabilities.

curl, the ubiquitous data transfer library, has integrated HTTP backend support written in Rust (hyper). This allows users to choose a memory-safe implementation for HTTP handling in a tool with over 20 billion installations.

Sudo and su received a Rust reimplementation (sudo-rs) sponsored by Amazon Web Services' Prossimo project, targeting one of the most security-critical Unix utilities.

These adoption examples demonstrate that memory-safe systems programming is not theoretical—production infrastructure is actively transitioning.

Practical Migration Considerations

For organizations evaluating their supply chain exposure to memory safety vulnerabilities, several practical considerations apply:

Prioritize based on exposure and criticality. Not all C/C++ dependencies carry equal risk. Network-facing code, input parsers, cryptographic libraries, and privilege-critical components warrant more attention than internal utilities. Focus migration pressure on dependencies where memory safety vulnerabilities would be most consequential.

Assess dependency migration trajectory. Some C/C++ dependencies are actively adding Rust alternatives or components. Others have no migration path. Prefer dependencies with memory-safe evolution underway.

Consider alternative implementations. For some dependency categories, memory-safe alternatives already exist. A Rust HTTP library might replace a C library. A Go network service might replace a C++ implementation. Evaluate whether such substitutions are feasible.

Support memory-safe rewrites. The Prossimo project (Internet Security Research Group) funds memory-safe rewrites of critical infrastructure. Corporate sponsorship and contribution to these efforts accelerates ecosystem-wide safety improvements.

Apply hardening to remaining C/C++ dependencies. Where migration is not feasible, mitigation helps. Compile-time hardening (ASLR, stack canaries, control flow integrity), memory-safe standard library usage, and fuzzing reduce exploitation likelihood without eliminating underlying vulnerability risk.

Challenges and Realistic Timelines

Despite compelling security benefits, memory-safe transition faces significant challenges:

Expertise scarcity: Rust, while growing rapidly, has a smaller developer community than C/C++. Finding developers with Rust expertise—particularly Rust expertise combined with domain knowledge in areas like operating systems, embedded systems, or cryptography—is difficult. Training existing developers takes time.

Ecosystem maturity: The Rust ecosystem, though developing rapidly, lacks equivalent libraries for every C/C++ use case. Some specialized domains have limited Rust tooling. Interoperability between Rust and existing C/C++ code adds complexity.

Performance considerations: While Rust matches C/C++ performance for most use cases, some highly optimized code may require careful porting. Certain low-level operations may be awkward to express in Rust's ownership model.

Existing codebase scale: Billions of lines of C/C++ code power global infrastructure. Complete replacement is a generational project. Even with aggressive investment, critical C/C++ code will remain in production for decades.

Interoperability complexity: Transitioning incrementally—rewriting modules while maintaining interfaces with existing code—introduces FFI (Foreign Function Interface) boundaries that can be error-prone and that can reduce some safety benefits.

These challenges argue against expecting rapid transformation. Memory-safe transition is a multi-decade endeavor, not a quick fix.

Balanced Recommendations

Given both the security imperative and practical constraints, we recommend a balanced approach:

1. Favor memory-safe dependencies for new development. When selecting new dependencies, prefer memory-safe implementations where quality options exist. A Rust TLS library may be preferable to another OpenSSL binding.

2. Track memory-safe evolution in critical dependencies. Monitor whether key dependencies are adding Rust components or alternatives. Factor this into long-term dependency strategy.

3. Support ecosystem transition. Contribute to memory-safe rewrite efforts, whether through funding, code contribution, or adoption that validates new implementations.

4. Apply defense in depth to remaining C/C++ code. While waiting for memory-safe alternatives, use available hardening: modern compilers with security features enabled, fuzzing, static analysis, and careful code review.

5. Set realistic expectations. Memory-safe transition will take years to decades. Communicate timeline realistically to stakeholders. This is not a quick fix but a generational improvement.

6. Evaluate supply chain exposure. Understand which of your critical dependencies are written in memory-unsafe languages and assess the risk implications. This awareness informs both patching priority and long-term strategy.

The memory safety challenge illustrates a broader supply chain security principle: some risks cannot be eliminated through patching and monitoring alone. Fundamental improvements to the software development ecosystem—in this case, language evolution—can eliminate entire vulnerability classes in ways that addressing individual CVEs cannot. Supply chain security strategy should include awareness of and support for these foundational improvements alongside operational security measures.