How memory safety CVEs differ between Rust and C/C++
Analyzing the Divergence of Memory Safety CVEs: Rust vs. C/C++
A CVE (Common Vulnerabilities and Exposures) serves as a standardized database for identifying and documenting security flaws within software. While these reports cover a wide spectrum of issues, they generally fall into two categories:
- Logic Errors: Bugs in the program's intended flow (e.g., a recent issue found in
Cargo). - Memory Unsafety: Severe vulnerabilities that frequently lead to exploitable security breaches.
This article explores the latter, specifically how memory safety vulnerabilities are reported in libraries and why the process differs drastically between Rust and C/C++.
The Great CVE Debate
Online discourse often involves comparing the raw number of CVEs in Rust projects versus those in C/C++ projects. This data is frequently weaponized to claim that:
- Rust isn't actually memory safe.
- The effort to migrate to Rust is
worthlessnot worth it because CVEs still occur.
I encounter these same misconceptions when mentoring C/C++ developers transitioning to Rust. While anyone can draw their own conclusions from the numbers, there is a fundamental nuance in how these languages handle potential vulnerabilities that isn't immediately obvious to those unfamiliar with Rust's internals.
A Necessary Disclaimer
To be clear: Rust is not a magic shield. It is entirely possible to introduce undefined behavior (UB) and memory unsafety in Rust.
In the vast majority of instances, this requires the use of the
unsafekeyword. Anyone claiming that Rust is mathematically incapable of UB is simply wrong.
Furthermore, general security flaws—unrelated to memory—still exist. For example, forgetting to implement an authorization check on an admin panel is a logic error that can happen regardless of the language used.
The C Perspective: A Case Study with libcurl
To illustrate the difference, let's look at libcurl, a C-based networking library. It is one of the most robust and well-maintained open-source projects globally, led by the prolific Daniel Stenberg. Despite the recent surge of LLM-discovered CVEs, the team maintains an incredibly high standard of safety.
Consider a simple function: curl_getenv. Its purpose is to provide a cross-platform way to retrieve environment variables. One might assume such a basic function is "safe." However, consider this program:
#include <curl/curl.h>
int main(void) {
// Passing a NULL pointer to the function
curl_getenv(NULL);
}
This 5-line snippet compiles without warnings. Yet, upon execution, it may result in a crash:
$ gcc test.c -otest -lcurl -Wall -Wextra
$ ./test
Segmentation fault (core dumped)
Why isn't this a CVE?
In a massive application, passing a NULL pointer by accident is a common occurrence. If this happened in a critical path, it would be a vulnerability. However, if you reported this specific behavior to the curl maintainers, they would likely dismiss it.
This is because such a crash is categorized as "wrong usage."
Reasons for this classification:
- Type System Limitations: In C, it is nearly impossible to formally define API contracts (preconditions/postconditions) within the type system.
- Documentation Practicality: It is impractical for authors to list every single way a user could misuse a function. The
curl_getenvdocs don't explicitly forbidNULLbecause it's assumed the developer knows not to pass one.
The logic follows a specific flow:
If every possible misuse of a C API were logged as a CVE, libraries would be buried under millions of reports. Consequently, C/C++ CVEs are issued for specific bugs in the implementation, not for the inherent possibility of an API being misused.
Comparison Summary
| Feature | C / C++ Approach | Rust Approach |
|---|---|---|
| Contract Enforcement | Manual/Documentation | Compiler-enforced (Type System) |
| Null Pointers | Possible UB | Prevented by Option<T> |
| CVE Trigger | Specific implementation flaw | Often triggered by unsafe misuse |
| Responsibility | On the caller to avoid "Wrong Usage" | On the compiler to prevent "Wrong Usage" |
In mathematical terms, we could say that in C:
The Rust Contrast
Now, let's consider hyper, the Rust equivalent to libcurl. If hyper had a similar function, I would write a Rust program like t...
![]()