a rule of thumb i follow is that the second someone starts comparing or talking about the number of CVEs, i just ignore whatever they say next. its hard to think of a more useless metric than "number of CVEs", especially now.
(edit: the people disagreeing are encouraged to share how you use "number of CVEs" to inform your decision making)
And only 20% of memory related bugs are use-after-free which the borrow checker fighting is for.
And 100% of the use-after-free exploits were to gain admin rights on an already hacked Windows (all windows) computer.
So for the vast majority of people the borrow checker adds nothing.
The vast majority of memory safety bugs (extreme pro level, super hard to exploit, only worth it in massively adopted evil outer world facing software) can be fixed by using C++26 with array bounds checking and forced initialisation.
These last two things that Rust forces catch 70-80% of the memory problems the borrow checker only 20-30% only use-after-free.
Most problems by far for normal developers are supply chain attacks, exposing api keys, remote code execution, wrong input validation, wrong auth-flow.
You're reading the CVEs of sudo and ssh and think your code will be hacked like that.
PHP is memory safe and still many people hack wordpress plugins.
Determining the impact of library bugs can be really hard. For example, some functionality might be so broken that it's simply impossible to use correctly, so a buffer overflow on top does not make a difference. Or a buffer overflow vulnerability triggers consistently during system boot, so that you never get to the login prompt. These can hardly be considered vulnerabilities. On the other hand, we have clear buffer management bugs, where we must expect that there is application code out there which specifies tight buffer bounds and requires that the library stays within that buffer. (Rust should help here, at least out-of-bounds accesses should turn into panics.) But most bugs are unfortunately somewhere in the middle. Tools like Debian Code Search can provide some evidence. But in the end, it's more subjective than I'd like it to be.
On the extreme end, we have compiler soundness bugs. I'm a bit of worried that I'm hitting any of those when I'm tweaking the types until the compiler no longer complains. Beyond the basics, I really don't have a grasp of Rust's type system rules. But I suspect they very difficult to hit by accident, and even if I do, the code must be miscompiled in meaningful, but difficult-to-notice way. All that seems rather unlikely, which is why these bugs aren't treated as vulnerabilities.
I really think we need some corrective factor (such as potential implication impact) when determining whether toolchain bugs are vulnerabilities.
Rust also requires libraries to be safe regarding unsafe, no matter what kind of insane input that is given to the library and that would otherwise potentially be security issues. Which is too difficult for many library authors.
And unsafe in Rust is so difficult that many library authors throw their hands up, use Miri, and hope for the best. Even though Miri, all respect to it, has bugs, probability-based testing and other limitations and issues.
UB in both user library and standard library:
https://materialize.com/blog/rust-concurrency-bug-unbounded-...
Take ladybird (last month blog; not that ladybird stands for all projects out there, of course; it is just an example):
https://ladybird.org/newsletter/2026-05-31/
"The HTML parser is now written in Rust" "The Rust parser is also about 10% faster than the C++ version it replaced,"
I am not saying this is a systematic analysis by far, but Rust is pushing into domains where C and C++ dominated in the past. And that seems to be a real push. To me it looks as if both C and C++ are standing to lose some ground in the next few years, directly to Rust. Perhaps even via snowball effect.
I know this doesn't stop runtime problems in release builds, but i'd have thought this sort of simple precondition check would help users find problems in their library useage.
It's not going to stop you passing a non-terminated string, or other such invalid input though, which is I guess more the point, that it's totally possible in C to produce good looking but actually invalid arguments that can't be spotted at runtime without UB (out of bounds access etc).
Edit: Actually thinking about this more, I guess the problem is that you are likely linking against a release library implementation, so it's not possible to add a precondition without introducing a runtime overhead, which is probably more likely what we are talking about with this case.
The borrow checker does add something, but it definitely costs something as well in multiple ways, also in terms of how it is done in Rust and at a programming language design perspective.
It would be very funny if you were batting for Rust, and just having a laugh at others here.
Unless your point is merely that average Joes write such terrible code that you don't even need memory safety issues to exploit their software, [citation needed]
Google says memory safety issues are 75% of exploited zero days. (https://security.googleblog.com/2024/10/safer-with-google-ad...)
I mean, this is basically true. And it goes beyond type safety - there have been CVEs filed against the Rust stdlib for TOCTOU problems of a kind that the C++ stdlib is absolutely replete with (often the exact same ones in the exact same places, to the extent that comparable APIs exist) which ended up being fixed quickly in Rust and largely ignored in C++, if anyone bothered to file in the first place.
For sure does create headaches for those who need to categorize CVEs by impact, but on balance I don't think it's a bad thing for the ecosystem. Creating a culture that wants to fix soundness issues rather than mark them as WONTFIX with a line of documentation is a core principle of Rust.
> I'm not convinced this is true.
But it is. It is true that Rust libraries could take this position of "any API misuse causing vulnerability is a CVE" more to the extreme, currently it is applied to memory safety but it could be applied to panics as well. However it is still true that pretty much all Rust libraries treat API misuses that cause UB as a CVE, and pretty much no C/C++ library does that, and that inflates the number of Rust CVEs.
> On the extreme end, we have compiler soundness bugs. I'm a bit of worried that I'm hitting any of those when I'm tweaking the types until the compiler no longer complains. Beyond the basics, I really don't have a grasp of Rust's type system rules. But I suspect they very difficult to hit by accident, and even if I do, the code must be miscompiled in meaningful, but difficult-to-notice way. All that seems rather unlikely, which is why these bugs aren't treated as vulnerabilities.
Rest assured that you are much more likely to hit a miscompilation in your compiler's backend, and that it is much harder to detect.
imo one of those soundness holes is caused directly from trying to prevent UB - integer overflows. It is inconsistent in Rust what happens in that scenario depending on compiler flags, which basically just makes it UB for any given piece of code. And, unfortunately, default release mode behavior is unsafe.
In broad strokes it's correct, this stuff happens and it's hard to be correct all the time. But are you trying to make a point? Or just ranting?
Also that linked issue was considered a CVE and is fixed (as the article says).
I directly tackle the concerns you mentioned, and as a followup I'm actually working on formally verifying the library as well (I've had some success and will publish an update regarding this).
On publicity side / propaganda / some specific areas you might have a point. Practically amount of C++ code being in active development (I wat to stress this particular point) dwarfs that of Rust despite all that high profile pressure.
Personally I consider languages as just a tool and do not get hung up when client prefers this or that and I have developed all kinds of software in many languages.
Personally I consider Rust very restrictive and poor expression-wise comparatively to C++.
I have no idea what are you talking about, no_std is just completely irrelevant here.
> Nor if one of the soundness holes in the Rust programming language itself is encountered
Have you actually examined those soundness holes? It is basically impossible to hit them without writing code which is meant to hit them.
And this is also noted in a footnote.
> Nor if there is UB in one of the libraries used as a dependency by the library you are using
If we treat a Rust program globally, this is kinda true. A more true statement will be that UB cannot happen without unsafe code somewhere, including in dependencies (and the original statement can be interpreted as saying that).
But the true power of unsafe is that it's local. If you've reviewed a library and its unsafe is sound, you can ignore it for the rest of the calculation. And of course, the more people review a library the more likely it is that it is sound.
> Which has happened many times, since the Rust standard library is full of unsafe
And here again the post's point stands: many CVEs in std are artificial, you can't exploit them without writing a program that is meant to be exploited. Such thing will never be a CVE in C/C++'s std.
> Rust also requires libraries to be safe regarding unsafe, no matter what kind of insane input that is given to the library and that would otherwise potentially be security issues. Which is too difficult for many library authors.
That is true, that is in fact the post's point: that if they fail this, a CVE will be filled, even if exploitation is just not possible realistically.
But there is a very simple solution for library authors: don't write unsafe code! You don't need to, the vast majority of times. And if you do not have the knowledge (which indeed is more complicated than in C/C++) how to not have an unsound API, then you just should not write unsafe code.
For a language as ugly as Rust, my thought is that people should actually be using Ada, and have a mathematically provable correctness angle; not just a replacement for C/C++ with memory safety.
Which is... true? but irrelevant. Such applications are not suggested to be ported to Rust. Of course, some people still do that, because they like Rust; but that's their personal choice.
The LLVM provenance bug is a really nice example. The Rust which tickles this bug (LLVM emits nonsense, claiming that two integers a and b are different but then calculating that a - b == 0...) is fairly clear, you wouldn't write it by accident but it's obvious what it should do, and unsettling to discover that the bug isn't in Rust's compiler frontend but in LLVM.
You can write equivalent C or C++ to show the bug with Clang - but when you try to write it you'll struggle, not to reproduce the bug per se, but to stop writing Undefined Behaviour, which invalidates your bug report because the LLVM devs will say "This is UB, working as intended". The non-UB reproducers are much more elaborate than the safe Rust was.
Am I missing something?
J/K, but seriously, some members of the Rust community might not be happy about your writings.
If they rewrote it in C++ again, they would have most likely got the same result because they got a chance to fix a design that might not have been most optimal.
C++ also does not have pattern matching, which is wildly popular in Rust.
Ladybird in the past used Swift apart from C++, but abandoned it. Swift has some peculiar issues, some of which might not be fixed over time. Ladybird's LLM-based conversion to Rust is interesting. I don't know much much unsafe the converted code has.
I think it's also a big sign that the linux kernel adopted rust and not c++. (only for small parts but still)
Is 100% relevant.
Did you use ChatGPT to generate your comment?
It's a bit like saying you should program in C, because formal verification tool X generates C code hence C is safe.
If memory safety issues are 75% of exploited zero days it sounds to me like they're the biggest issue in the ecosystem by far.
This speculation has been offered every time. It's not crazy to think this might be true, but it's also not crazy to think that if C++ keeps leaving performance on the table and Rust doesn't that adds up for real projects.
When Titus wrote "ABI: Now or Never" in 2020 he estimated 5-10% aggregate loss. Things that you could fix, if you started over, but C++ refuses to do that because of ABI and so it doesn't have these fixes, whereas in most cases† Rust does. So I can well believe that a blow-for-blow port could get you 10% perf win.
† One of the examples Titus cites is the "Small String Optimization". Rust deliberately doesn't do SSO for its standard library collection String, but several really nice SSO optimised types are available, including ColdString and CompactString, which are way better than what's provided in C++ if that's what you need.
When C++ people say they think there should be C++ in Linux, their proposal usually begins by proposing that it "should" be possible to just compile Linux as C++ software. This doesn't work because C isn't just "C++ but old", and they rapidly lose interest.
Which of course also feeds into Linus' semi-fair claim that not allowing C++ keeps out the low effort wannabes who would plague such a project. This makes C++ developers very angry, but part of the reason why is that it's true, C++ does attract these people.
The Rust for Linux people wrote a lot of code, a lot of documentation, they did Q&As, they worked very hard to actually deliver the idea to the kernel community, it's a totally different approach, it's a lot more work but some people thought it was worth the work.
Most exploited code probably exists in the application layer in a high-level, memory safe language. I would wager that but I don’t have time to cite ten papers on HN.
I don't think its fair to attribute it to lack of skills or bad intent, unless there's some proof to any of it.
https://old.reddit.com/r/rust/comments/1tuvxej/iddqd_or_the_...
Marketing of Rust is held very, very dear to some of the people in the community.
CVE is a database used for categorizing and reporting security vulnerabilities in software. There are various kinds of vulnerabilities that can be reported. Some of them are caused simply by bugs in the program logic (like a recent CVE reported in Cargo), but some of the most nasty ones are caused by memory unsafety, which can easily lead to exploits. In this post I want to focus on the latter kind of CVEs, how they are reported, especially in libraries, and how it differs between Rust and C or C++.
Because sometimes I see people online who compare the number of CVEs in Rust and C/C++ software, which tends to be accompanied by claims about Rust not being really memory safe or not being worth adopting when CVEs can still exist in it. And sometimes I also observe similar views when I teach Rust to programmers who are used to programming in C or C++.
Now, anyone is, of course, free to do such comparisons, and make their own conclusions based on it. But I think that there is an important difference in how potential vulnerabilities related to memory safety are treated in Rust and C/C++, which might not be obvious at first, especially if you don’t know how Rust works. I’d like to explain that in this post.
But first, I should clarify that it is absolutely possible to cause memory unsafety bugs and undefined behaviour in Rust. In the vast majority of cases1, the unsafe keyword is required for this to happen, but anyone who claims that Rust programs cannot experience UB at all is simply incorrect. It is also perfectly possible to cause general vulnerabilities (meaning those unrelated to memory unsafety) in Rust. Forgetting to add a check that your admin dashboard is only accessible to admins can happen in any language, after all.
And yet, there is something very different between potential vulnerabilities in Rust and C or C++, which is related to the core reason of why Rust is actually much more memory safe in practice than C or C++. I’ll try to demonstrate it on the curl networking library, which is written in C.
(lib)curl is one of the most used and well-maintained open source libraries in the world. Its primary developer, Daniel Stenberg, is one of the most prolific open source maintainers of our time, and together with many other people, he has been dilligently improving this library for the past 30 years. Despite having to deal with a recent avalanche of CVEs found by LLMs, he and his collaborators are doing a very good job of keeping curl safe from potential exploits and vulnerabilities, and they take pride in curl being a very robust piece of software.
So, let’s take that to the test, shall we? I opened the documentation of libcurl and found the first function I saw that accepts an argument, curl_getenv. This is supposed to be a simple function that provides a portable abstraction for getting the value of an environment variable across different operating systems. curl is supposed to be safe and robust, so surely this function doesn’t contain any UB or memory unsafety, right? So what about the following C program?
#include <curl/curl.h>
int main(void) {
curl_getenv(NULL);
}
This 5-line C program is as simple as it gets, it just calls the curl_getenv function with a NULL pointer argument, and compiles without any warnings. And yet, when you execute it, you (might) get a segfault, and thus a memory safety bug, and thus a potential vulnerability/exploit:
$ gcc test.c -otest -lcurl -Wall -Wextra
$ ./test
Segmentation fault (core dumped)
Of course, this program is artificially simple, but that’s kind of the point. In practice, situations like this can (and do) easily happen in larger programs by accident all the time.
Huh. So maybe curl isn’t so safe after all? Should I go and report this as a vulnerability in curl?!
No, of course not. That would be stupid. I know that, you know that. But how do we actually know it? That’s the interesting part.
Consider a very similar program that would call the function like this: curl_getenv("FOO"). What if that program would still segfault, and thus contain a potential vulnerability? I am sure that the curl maintainers would like to know about that happening, and would consider it to be a pretty big issue if I reported it! At the same time, I’m sure that they would (rightfully) tell me off if I reported the first program as a vulnerability in curl. Yet those two programs differ only by so little.
So, what gives? Well, in practice, UB like the one in my original example is said to be caused by “wrong usage”2, and it is not considered to be an issue in the library or API that I am using, but in my (application) code. This is done mostly for the following two reasons:
In C, it is often not possible to specify the contract (invariants, preconditions, postconditions, etc.) of APIs precisely3 due to its limited type system, and library authors often don’t bother describing all possible kinds of wrong usage, as it would not be practical.
Indeed, the documentation of curl_getenv does not say that calling it with NULL is forbidden and might lead to a segfault! The authors thus assume that you will use the library “correctly” (whatever that means), and if you don’t, then any caused vulnerabilities are your fault.
The fact that it is so simple to trigger UB by accident in C or C++ means that if we reported all the potential possibilities of causing a vulnerability, such as the one in my example program, most C or C++ libraries would be flooded by millions of CVEs. It wouldn’t make sense to do that, because there would be five different ways of potentially causing a vulnerability in every function call.
And thus, in C and C++, we usually do not consider similar situations to warrant a CVE in the used library. In other words, we create CVEs for specific misuses of a library, not for the existence of a library API that can be misused.
So, what is the crucial difference between how the situation above would be treated in C or C++ and in Rust? hyper is likely the most popular networking/HTTP library in Rust, spiritually similar to libcurl in C. Imagine that hyper would have a similar simple function that would take an argument, I would write a Rust program like this:
fn main() {
hyper::foo(None);
}
Then I’d hit cargo run, and the program would segfault. Would that be a CVE in hyper? Yes, absolutely4!
The program does not contain any unsafe blocks, so if a memory bug occurs, it had to be caused by the hyper library having a soundness bug.
The difference is that in Rust, when it is in any conceivable way possible to use a library such that a memory bug occurs, without using unsafe in the user code, it is always a bug in the library, not in the user code. That is why we call such APIs unsound, or say that they have a soundness hole, because there is a way to use them wrong (w.r.t. memory safety) in safe Rust.
In other words, we create CVEs when it is possible to use a safe library API in a way that might cause a memory bug, even if we haven’t (yet) found any program in the wild that would actually do so. This means that some of the CVEs reported in Rust are much more “strict” than the ones in C or C++, which some people don’t find “fair”.
If we applied the same logic to C, then curl_getenv should be flagged as a CVE in curl, because it is possible to use it in a way that causes a memory bug. But of course, this doesn’t really make sense in C, because there is no concept of safe and unsafe C (or rather, all C code is implicitly unsafe), which is why I said earlier that reporting this CVE would be stupid.
The answer to the question “do I use this function correctly” (with respect to memory safety issues, not logic bugs), which is often difficult to figure out in C or C++, is very simple in Rust:
unsafe, then the answer is simply YES. It is impossible to use it incorrectly.unsafe, I must mark the call with an unsafe block, which makes it immediately obvious during code review and in the codebase that this place is potentially dangerous. In this (usually very rare) case, we revert back to the level of C or C++.The first part of the answer is what enables Rust’s memory safety to scale in practice. If you do not use unsafe in your code, which is not needed in the vast majority of situations (unless you are writing something like an operating system or a lock-free data structure), and don’t encounter a compiler bug, you know that any potential causes of memory unsafety are not your fault. If a library does not expose any unsafe interface, you simply cannot use it in a way that would cause memory bugs, unless the library uses unsafe internally and has a bug. But if that happens, the bug is fixed within that library, and all its users are then again automatically safe from memory bugs.
This is the difference between Rust and C or C++. Even though the developers of curl are doing awesome work to build a perfectly safe and robust C library, the millions of other C programs that use it can still very easily introduce memory unsafety just by “holding it wrong”, without the curl developers having any way of preventing that.
I used curl as an example, but the same holds for pretty much any C or C++ library, including the standard libraries of those two languages (and also other memory unsafe languages in general). Originally I wanted to show more examples, but in the end I realized that they are all the same, so I stayed with the single curl function, because I think it demonstrates the difference well.
What I described in this blog post is not really ground-breaking in any way, and I think that it is kind of universally understood by most people who know how Rust works. But I also don’t remember seeing any blog post about this, and I was repeatedly explaining it to some people, so I wanted to write down some thoughts about it, so that I can simply link to them the next time this debate happens again.
I hope that what I described above shows that comparing raw numbers of CVEs per line of code of Rust and C or C++ is all kinds of misleading, and we should take that into account when comparing the memory safety of Rust and other systems programming languages.
If you have a different idea on how CVEs (should) work in Rust or C/C++, let me know on Reddit.
Essentially only except for compiler bugs. ↩
Someone might even say “skill issue”
↩
In C++, the type system offers many more amenities for describing invariants, yet it still also offers many other ways to sneakily introduce UB by accident, so the end result is similar. ↩
Again, barring any compiler bugs or hardware issues with my RAM module :) ↩