Why CVE Counts Misrepresent Rust and C/C++ Security
Comparing vulnerability counts ignores how the two language ecosystems define the boundary between library bugs and caller misuse.
In security discussions, raw metrics are frequently weaponized. A common argument raised against the adoption of Rust is the persistence of Common Vulnerabilities and Exposures (CVEs) in Rust libraries. Critics point to these security advisories to claim that the language's promise of memory safety is overstated, or that migrating legacy codebases from C or C++ is not worth the effort.
This perspective, however, rests on a fundamental misunderstanding of how vulnerabilities are defined and reported across different programming paradigms. While it is true that Rust programs can experience memory unsafety and undefined behavior (UB)—typically requiring the explicit use of the unsafe keyword—and are equally susceptible to non-memory logic flaws like broken access controls, comparing CVE counts between Rust and C/C++ is an apples-to-oranges comparison. The discrepancy lies not in the quality of the code, but in how each ecosystem defines the boundary between a library vulnerability and caller error.
The C/C++ Boundary: The "Wrong Usage" Paradigm
To understand this asymmetry, consider how the C and C++ ecosystems handle API boundaries. A prime example is curl, a ubiquitous and exceptionally well-maintained networking library. Led by Daniel Stenberg and a dedicated group of contributors for three decades, curl is widely recognized as a robust, highly secure piece of software.
Yet, a trivial five-line C program can easily trigger a memory safety failure using curl's public API:
#include <curl/curl.h>
int main(void) {
curl_getenv(NULL);
}
Compiling this code with standard warning flags (such as -Wall -Wextra) yields no compiler warnings. However, executing the resulting binary immediately produces a segmentation fault.
In the C/C++ world, this segmentation fault is not considered a vulnerability in curl, nor would it ever receive a CVE. Instead, it is classified as "wrong usage" by the application developer. The responsibility for ensuring that a pointer is not null before passing it to curl_getenv rests entirely on the caller, even though curl's official documentation does not explicitly state that passing a null pointer will cause a crash.
This approach is a pragmatic necessity in C and C++ for two primary reasons:
- Expressiveness of the Type System: The C type system cannot natively enforce complex API invariants, preconditions, or postconditions. Expecting library authors to document and validate every conceivable invalid input is often highly impractical.
- CVE Volume: Because triggering undefined behavior via improper API usage is incredibly easy in C and C++, treating every such instance as a library vulnerability would flood the ecosystem with millions of CVEs. Consequently, the security community only issues CVEs for specific, internal misbehaviors of a C/C++ library when it is used correctly, rather than for the mere existence of an API that can be misused.
The Rust Boundary: Soundness as a Library Requirement
Rust fundamentally shifts this boundary of responsibility. The core promise of Rust is that safe code cannot trigger undefined behavior. This design choice completely redefines what constitutes a library vulnerability.
In Rust, if a library exposes a safe API (one not marked with the unsafe keyword), that API must be impossible to misuse in a way that leads to memory unsafety or undefined behavior. If an application developer passes invalid input to a safe Rust function and triggers a segmentation fault or data race, the fault does not lie with the caller. It is classified as a "soundness bug" within the library itself.
Because safe Rust code must maintain absolute memory safety guarantees, any leak of undefined behavior through a safe interface is treated as a severe defect. Consequently, these issues are routinely reported, patched, and assigned CVEs.
This creates a stark reporting asymmetry:
- In C/C++, an API that crashes or corrupts memory when passed invalid arguments is considered an application-level bug. The library maintainers bear no responsibility, and no CVE is generated.
- In Rust, an identical API design that allows safe code to trigger memory unsafety is considered a library-level vulnerability, resulting in a formal CVE.
Implications for Language and Library Selection
When evaluating languages and libraries for security-critical systems, developers must look beyond raw CVE counts. A high number of memory safety CVEs in a Rust library often indicates that the ecosystem is actively identifying and fixing API boundaries that fail to guarantee absolute safety to their callers. Conversely, the absence of similar CVEs in a C/C++ library does not mean the library is immune to these failures; it simply means the burden of preventing them has been shifted entirely to the application developer.
For engineering teams, this distinction yields concrete takeaways:
- Defensive Programming Overhead: When using C/C++ libraries, developers must write extensive defensive wrapper code to validate inputs and prevent undefined behavior, as the compiler will not assist in enforcing these invariants.
- Compiler-Enforced Contracts: Rust shifts this validation overhead to the library author, allowing application developers to consume safe APIs with confidence that misuse will result in compile-time errors or controlled panics rather than exploitable memory corruption.
- Metrics Interpretation: Security audits that rely solely on CVE frequency to compare the safety of Rust and C/C++ components are fundamentally flawed. They measure differences in reporting philosophy and language guarantees, not the actual security posture of the running software.
Sources & further reading
- How memory safety CVEs differ between Rust and C/C++ — kobzol.github.io
Ji-ho covers the increasingly tangled overlap between cloud architecture and security, drawing on a background as a penetration tester to keep his reporting grounded in real-world attack paths. He never lets a vendor claim go unquestioned and insists that every buzzword come with a proof of concept.
Discussion 0
No comments yet
Be the first to weigh in.