11 year old unsoundness bugs found in Rust borrow checker
244 points by formerly_proven 3 hours ago | 115 comments

mrkeen 2 hours ago
Type systems are hard! Check out https://counterexamples.org/

I gave it a quick scan for Rust examples; there are 11 samples among the following pages:

  https://counterexamples.org/eventually-nothing.html
  https://counterexamples.org/mutable-matching.html
  https://counterexamples.org/selfishness.html
  https://counterexamples.org/scope-escape.html
  https://counterexamples.org/nearly-universal.html
The first thing I always look for is mutability (combined with some other tricky feature), e.g. "Now it's a Just(3):Maybe<Int>, now it's a Nothing:Maybe<Int>, now it's a Nothing:Maybe<String>."

I wonder if TFA's example could be reproduced without mutability.

reply
vrighter 58 minutes ago
so what I take from this is that the rust compiler does not guarantee the stuff it claims to. Because new features of the type system is just being yolod instead of formally proven
reply
roblabla 42 minutes ago
There never was formal verification of the entire rust compiler, or even its complete borrow checker/type system. There has been verifications on _subsets_ of them.

Also, a lot of those soundness issues aren't related to new features _at all_, but usually due to poor assumptions made in optimizations.

The soundness bugs _are_ taken seriously though, and are slowly being patched out. A lot of the links in OP are now closed because Rust either changed how it generates code to avoid the issues, or fixed LLVM.

reply
meltyness 8 minutes ago
https://github.com/model-checking/verify-rust-std

hm, there's an effort but scoped only to the std

It's terrifically complex: https://doc.rust-lang.org/beta/nightly-rustc/rustc_middle/ty...

reply
thfuran 50 minutes ago
No mainstream compiler is formally proven. If you want a verified compiler, you pretty much have to choose between using a subset of standardml or a subset of C and the one proven compiler for each.
reply
AnimalMuppet 22 minutes ago
Wait, there's a proven subset of C? Can you point me to some info?
reply
jeremyjh 42 minutes ago
There is a massive possibility space between “yolo” and formal proof. Rust has substantial evidence of correctness. For example, unit tests and types.
reply
carlmr 23 minutes ago
Spot on, but this makes me think of a larger problem.

You can be annoyed by Rust fanboys, sure, but the more annoying are the Rust haters, because the hate always comes from a place of ignorance.

The hate only works if you don't go beyond a surface level understanding of Rust.

E.g. a niche case on the borrow checker is slightly broken, so borrow checking doesn't make sense.

Or the "oh but Rust needs unsafe, so it's just like C++".

Yeah, but by that measure all of C++ is unsafe++, even unsafe has more guarantees than standard C++.

Only if your whole understanding of the concept is that the keyword is called "unsafe", does that argument make sense.

A bit of a rant, but if you hate Rust with a passion, ask yourself first where that opinion comes from and if it can stand on its own. In most cases it can't.

reply
_flux 2 minutes ago
I think the difference is here:

- If a compiled C++ program has unsound behavior, the program needs to be fixed

- If a compiled safe Rust program has unsound behavior, the compiler needs to be fixed

reply
cowlevel 8 minutes ago
As you can see, this is a true statement, but it attracts infinite HN downvotes for some reason.
reply
UncleMeat 29 minutes ago
The Java type system is also unsound. That’s life. It is hard to do this fully correctly. In practice this never comes up.
reply
afdbcreid 15 minutes ago
While true, there are two important differences:

1) Unsoundnesses in Java's type system are checked at runtime.

2) They (or at least some) are intentional or at least won't-fix bugs.

Both are not true for Rust.

reply
csande17 46 minutes ago
Yes. When people describe Rust as "a memory-safe programming language" they are describing an aspirational vision, and not a programming language that has actually been designed with sufficient rigor to guarantee memory safety. Any failure of rustc to actually guarantee this is just a bug in rustc itself, a failure to properly implement the Rust spec, because the closest thing Rust has to a spec is literally just the vague ideal that the compiler should guarantee safety somehow.

Rust cannot fail, it can only be failed.

reply
insanitybit 23 minutes ago
I don't think the definition of "memory safe" has ever required full end to end stack conformance with a proof. Very few languages have achieved that, though some have. Most people call Java memory safe, C#, Python, Typescript, etc - all of those fall short of a formally verified, sound type system with a verified toolchain.
reply
afdbcreid 7 minutes ago
Not to talk about Go, which has intentional unchecked-at-runtime UB (data races) yet many argue to consider it memory safe (and I have a feeling that the same people arguing Rust isn't memory safe are also there. Just a feeling though).
reply
DriverDaily 7 minutes ago
> Rust cannot fail, it can only be failed.

“Please build the perfect system. Because I specified ‘perfect’ any flaws are due to your incomplete implementation, not my awful brief.”

If the person writing the rust spec was a client I would have to drop them because they would be setting themselves up for dissatisfaction.

reply
UncleMeat 28 minutes ago
Would you say the same thing about Java? It’s type system is also unsound.
reply
csande17 20 minutes ago
I'm not an expert on the Java specification, but my understanding is that Java as a language is well-defined enough that we can say things like "Java's type system is unsound", independently from any specific implementation of Java.

You can't do that with Rust because "Rust's type system" is literally just the vague concept of "a type system that is safe". The implementation of this ideal in rustc can have flaws, but Rust itself cannot, because Rust itself has no flaws by definition.

reply
lolakutty 35 minutes ago
>Rust cannot fail, it can only be failed...

Sweet. But why should anyone care..

reply
GrantMoyer 5 minutes ago
It's surprising this bug went unnoticed for so long, but at least it's easy to fix, since it's not a bug with the type system itself; it's just a bug in the compiler. The compiler should always have been rejecting that code according to lifetime rules of Rust's type system, so the fix is to just make it reject the code, rather than having to change the fundamental rules of lifetimes.
reply
weinzierl 40 minutes ago
This is remarkable but not super unusual. There are more than 100 issues labeled unsound currently. Some are long standing but from what I understand the new trait solver will unblock fixes for some of them (but probably not the one in OP).
reply
theemathas 8 minutes ago
Finding unsoundness bugs isn't the most unusual, yes. But this one is unusual in how simple the code that causes it can be (see the versions of the reproducer code in the comments). For other unsoundness bugs, the code would need to contort itself a lot to avoid the compiler checks.
reply
lambdaone 2 hours ago
Perhaps ironically, AI-based searching of the Rust compiler for bugs is likely to find more of these, pushing Rust closer and closer to the point of complete reliability.

The eventual formalization of Rust and a creation of a formally proven compiler is presumably going to be the end point of this process.

reply
lifeisloving 31 seconds ago
I asked myself, How many comments to do I have to collapse before someone says something about AI. 3.

I used to compare this hype to crypto hype, but its so much worse. Can we jusy like not try to shove llms into every single conversation.

Is there a hackernews type space where the word is banned? Even lobster.rs has this problem. I need some sort or way to just hide all content on certain sites where LLMs/AI are mentioned.

reply
stingraycharles 17 minutes ago
Why would that be ironic? AI is helping us find so many subtle bugs and isolate the root cause, it’s nearly perfect for this type of use case.
reply
malkia 3 minutes ago
and what stops AI from "accidentally" writing code with such bugs?
reply
kimi 41 minutes ago
Or we realize that it is not possible, and Rust accepts to be "just" a definitely safer C.
reply
wongarsu 33 minutes ago
There is a formally proven compiler for a large subset of C, so I don't see why formalizing (a large subset of) rust and making a formally proven compiler shouldn't be possible
reply
afdbcreid 3 minutes ago
There is a severe misunderstanding here. Beside being only a subset, CompCert definitely has UB. It just formalizes codegen for codebases not containing UB, which obviously won't be enough for Rust.
reply
UncleMeat 26 minutes ago
Because people want a performant optimizing compiler.
reply
mi_lk 12 minutes ago
What aspects of it are remotely ironic?
reply
brzz 33 seconds ago
Hey, if Alanis Morissette can make the same mistake…
reply
bbor 54 minutes ago
Until they find bugs in the formal proof program ;)

https://www.lesswrong.com/posts/zHhGa3iQBQcdSifFL/a-critical...

reply
j4k0bfr 2 hours ago
Took me a good couple minutes to wrap my head around. Man, what a nasty bug. I guess this specific use-case is rare. Time to see if any of the big libs are affected...
reply
coliveira 4 minutes ago
And nasty and rare bug can and will be exploited once it is found. The only thing necessary is for malicious actors to start adding this case in innocent looking code and there you have.
reply
vincent-uden 2 hours ago
What the hell. This is not the esoteric, crazy, impractical constellation I expected to find with a title like this. The minimal example is so simple and is definitely something you'd easily come across in normal code.
reply
insanitybit 2 hours ago
For a long time `loop {}` was undefined behavior. Lots of l-unsounds are really nuts but a few are not.

The reality is that if this were actually code that people wrote, well, it wouldn't have sat around for a decade.

reply
masklinn 14 minutes ago
Afaik loop {} has never been UB, however because llvm started as a c++ compiled where it is, this has been causing miscompilations in rust and C for… nearly two decades (llvm issue 965).
reply
cowlevel 5 minutes ago
Since the Rust language is defined by the Rust compiler, if it's UB in the compiler then it's UB in the language.
reply
fg137 36 minutes ago
> is definitely something you'd easily come across in normal code.

The fact that nobody reported this before means it's not, by definition.

reply
LoganDark 2 hours ago
I wonder if the reason it works is because you'd easily come across it in normal code, and someone didn't realize that saving the effort of implementing a check for this would result in unsoundness.
reply
jvanderbot 2 hours ago
Not really. I've worked with rust for 4 years, systems side, and never had to write a custom drop implementation. That's the gotcha here, not the rigamarole with the static+Send option type (which _would_ be something you'd encounter in prod).
reply
bryanlarsen 2 hours ago
I've written dozens of custom Drop implementations. Well, many of them are named cleanup because of the async Drop mess in Rust, but...
reply
dwattttt 30 minutes ago
How many of them only conditionally implement Drop, based on a generic param lifetime?
reply
Xirdus 18 minutes ago
Why would you ever conditionally custom impl Drop? I don't think I've ever seen that pattern, and I can't think of a use case.
reply
jvanderbot 27 minutes ago
OK. That's good to know. I'm not sure why sharing our professional experience with this type of code is controversial, but here we are.
reply
neysofu 2 hours ago
That's easily the exception rather than the rule. I've seen custom `Drop` implementations in almost every non-trivial Rust codebase I worked on.
reply
gspr 2 hours ago
A custom `Drop` appears very quickly with FFI code. Suppose for example you interface with some C library that expects you to call a function to release some resources.
reply
rvz 30 minutes ago
Now we are at the phase where, Rust is “safe” but only if you use it “correctly” and without using X language feature or a custom Y feature.

This is the problem when a language advertises with “guarantees” and it turns out it never had any due to many false assumptions by their promoters.

So what other soundness bugs are there in the language that we need to know about?

reply
jvanderbot 28 minutes ago
I'm not defending rust here - a correctness bug invalidates the premise. But I am saying that I've never seen a custom drop in production.

For some reason this is controversial, but it remains true.

reply
bryanlarsen 10 minutes ago
You said/implied 2 things:

- you've never written a custom Drop

- "Not really". Implying that your experience generalizes

I've written a bunch of rust programs without custom Drop. I believe your first statement. There are large classes of programs that don't need it. There are also many that do. I think your statement says a lot more about what kind of programs you work on than about custom Drop not being common. People are downvoting the implication that custom Drop is not a common pattern.

reply
cmrdporcupine 24 minutes ago
Custom drops are all over the place. As others have said.

But not custom drops that are doing variadic type stuff like this, usually.

reply
cmrdporcupine 25 minutes ago
No we're in a situation where a bug in the type checker was found and will be fixed and a new release will be made.

That it took 11 years for someone to come across this is a fairly intense counterargument to your attempt to paint this as a language with dangerous footguns.

reply
FuzzywarmWalnut 2 hours ago
Worked on a variance-related lint once and the scary part is how rarely anyone writes down variance by hand, so bugs there just sit untested for a decade.
reply
Hugsbox 2 hours ago
I don't understand how something like this could take so long to be discovered. Pretty crazy stuff.
reply
lelanthran 2 hours ago
> I don't understand how something like this could take so long to be discovered.

Maybe they wrote it in C :-)

On a serious note, old software is generally more trustworthy than new software, because bugs are discovered over time and with usage.

reply
zaphar 2 hours ago
Old C software is generally more trustworthy than new C software. I don't think this rule of thumb transfers across languages very well. And the definition of new vs old has a fairly large effect. If a language has been under constant change it doesn't matter when it was started a large part of the codebase is "new".
reply
fg137 36 minutes ago
Because it's rarely used like this, just like weird bugs caused by edge cases found in all kinds of software in the world.
reply
MrBuddyCasino 2 hours ago
The heart of the matter:

  struct Q<T: 'static>(Option<T>);

  impl<T: 'static> Drop for Q<T> {
    fn drop(&mut self) {
        // Code in here has access to a non-'static &String
        // but assumes that it's a &'static String
    }
  }

  fn main() {
    let l = String::new();
    let mut q = Q(None);
    q.0 = Some(&l);
  }
I wonder if the certified compiler for safety critical uses (edit: Ferrocene) has the same issue.
reply
afdbcreid 2 hours ago
> I wonder if the certified compiler for safety critical uses has the same issue.

It uses the same code, so yes.

(Although I don't know what are their guidelines, it might be that they require avoiding code relying on implied bounds, for example).

reply
1718627440 43 minutes ago
Can someone ELI5 for people who don't know Rust (but e.g. C)?
reply
thinkharderdev 22 minutes ago
Rust is supposed to guarantee statically that a reference cannot outlive its referent. The `T: 'static` bound means that the value that Q holds must have a 'static lifetime (e.g. either be an owned value or a reference to a static value). So this program should not compile since a reference to l is not 'static. This code is unsound because rust will drop the string l before q so when the Q destructor runs the underlying string has already been freed yet we have a valid reference to it.
reply
1718627440 18 minutes ago
What does "q.0" and "Some(...)" mean?
reply
cowlevel 2 minutes ago
Some(...) is just constructor for an option type as seen in other languages e.g. java's Optional, haskell's Maybe, c++'s std::optional. I assume that it's needed here because the field can't be a null pointer otherwise.

q.0 is the first field of q, which is anonymous. They could have just as well called it x and written q.x.

reply
bonzini 3 minutes ago
"Some(...)" means that this Option<T> does have a value of type T in it; "None" means it doesn't.

"q.0" is not really needed, it's a struct with unnamed fields. Here is the same with named fields:

  struct Q<T: 'static> { pub x: Option<T> }
  ...
  let l = String::new();
  let mut q = Q { x: None };
  q.x = Some(&l);
reply
high_na_euv 2 hours ago
which compiler?
reply
sshine 2 hours ago
Ferrocene
reply
wg0 2 hours ago
DeepSeek v4.1 Flash thinks it should NOT compile. Whereas in reality it DOES compile which is why it is filed as a bug.
reply
jug 2 hours ago
They're seriously using AI as a judge for what to file?
reply
afdbcreid 2 hours ago
The issue and none of the comments mention AI. It is pretty clear your parent said it by themselves.
reply
wg0 2 hours ago
I was just checking what outcome an LLM would have based on the patterns in its probabilistic distribution it has seen the wild.

Most LLMs would probably say this piece of code won't compile.

reply
ChickeNES 48 minutes ago
chatgpt and grok linked to the the soundness issue, claude I am in usage timeout, and gemini shit the bed as usual: "The compiler rejects the program with error[E0597] on the assignment q.0 = Some(&l)"
reply
fnord77 56 minutes ago
Here's the weakness of compiled programs. Does this compiled code have this vulnerability? You have no idea. At least with a VM or interpreter you can scan for vulnerable versions and replace the runtime or the specific library.

With this you have to recompile everything

reply
baq 30 minutes ago
That is less and less true by the day, for better or worse.
reply
beanjuiceII 2 hours ago
reply
jeremyjh 45 minutes ago
I think it has no relevance here at all. Actix was unsound because of prolific use of unsafe. That was a poor design decision but a conscious one. TFA is just a bug.

Having ideals does not mean there are never bugs.

reply
ModernMech 57 minutes ago
What do you mean?
reply
badatnames 2 hours ago
There is a collection of these someplace. Rust is better but it'll never be perfect, news at 10
reply
zer00eyz 2 hours ago
Rust is good, I generally do not dislike it - but it does have flaws.

I will happily continue to write things in Golang first. I don't enjoy refactors in Rust, and it's rare that I see things that genuinely need its raw performance.

Cloudflare's Oxy is a stellar example of build in Golang till your design is settled and then port over for performance when you need it.

reply
JABVOLT 2 hours ago
very very good job
reply
akoboldfrying 2 hours ago
As an old C++ guy, my take is: Heh heh heh.

ETA: Downvoters, I salute you. Without your selfless courage, the forces of light humour would surely prevail.

reply
fg137 49 minutes ago
This is not the first soundness bug. Some bugs aren't fixed for years. The Rust community is well aware that such bugs exist, will be found, and address them just like other high impact bugs. In other words, if you paid the slightest attention to the development of Rust language, you would know this is surprising but not unexpected, and has happened many times.
reply
akoboldfrying 30 minutes ago
Given that TFA is currently #1 on HN, I suspect I'm not the only person who is aware of Rust's borrow checker but not aware, until now, that it has a history of serious soundness bugs.
reply
wg0 2 hours ago
You got my upvote and this one decade old issue:

https://github.com/rust-lang/rust/issues/25860

reply
Philpax 38 minutes ago
People in sugar glass houses shouldn't throw stones :P
reply
mrlonglong 2 hours ago
You got my upvote.
reply
rvz 2 hours ago
Yeah. A reminder that no language is a silver bullet even if it is heavily marketed. Especially with the “written in XYZ™” tag line.
reply
rvz 2 hours ago
This should not be a surprise at all given the amount of soundness bugs that were collected in the Rust compiler but no-one cared. [0]

EDIT: I don’t even think HN cared either up until now. If you search this paper on HN, guess who posted it?

Lets hope that this will be the only occurrence of soundness bugs in Rust and perhaps it needs a tiny disclaimer on claims of “safety guarantees”.

[0] https://arxiv.org/abs/2608.28713

reply
optionalsquid 2 hours ago
It looks like 18 of the 30 issues listed in table 4 had already been fixed when that paper was released, and there is activity in most of the rest. One was recently flagged as being fixed by -Znext-solver, and another has the submitter write that it is fixed, but the issue is not closed. And that's before you consider the impact of the individual issues; how many of the remaining issues impact "real" code?

So I don't think it's fair to claim that "no-one cared".

reply
purplesyringa 2 hours ago
I don't think "no one cared" is a fair characterisation. The paper doesn't support this conclusion, and from experience I'd say that the two difficulties in fixing unsoundness are doing so without breaking existing code (Rust guarantees stability unless the code is unsound, and introducing previously missing checks can cause sound code to stop compiling, which is a problem if it occurs often) and doing so in a reliable way that can be supported long-term. Rust is currently in the progress of stabilizing next-solver and Polonius, both of which fix multiple long-standing bugs and have been worked on for years now due to intrinsic complexity, performance considerations, compatibility issues, etc. It's not that no one cares, it's just that this stuff is hard and takes time.
reply
cowlevel 2 hours ago
Rust is not a memory-safe language. cve-rs proved it years ago but commenters always argue that cve-rs is too contrived to matter, or that it's a compiler bug so it doesn't count.
reply
insanitybit 2 hours ago
lol do you think that cve-rs was required? there is literally an l-unsound tag on rustc's github, this isn't some embarrassing or secret thing.

Safe rust is safe, granting `unsafe` and compiler errors. This isn't a secret.

reply
psychoslave 2 hours ago
What's l- here? Literal?
reply
pas 2 hours ago
reply
insanitybit 45 minutes ago
lol I was writing `l` because I thought it was `labeled-unsound` for like a fucking decade
reply
high_na_euv 2 hours ago
in compare to c and cpp? it is
reply
cowlevel 2 hours ago
And in absolute terms?
reply
qweqwe14 2 hours ago
Literally nothing can be anything in absolute terms, what's your point? The fact is that Rust is miles better than any other language in terms of memory safety & performance ratio. That's all that matters.
reply
cmrdporcupine 13 minutes ago
"People still die in cars despite airbags and seatbelts so I don't see the point in wearing a seatbelt."
reply
high_na_euv 2 hours ago
cmon, it is time move on.

it has been discussed bilion times

reply
david-gpu 2 hours ago
Let us not conflate a bug in an implementation (rustc) with a defect in the language (Rust).
reply
mrkeen 2 hours ago
AFAIK there isn't a language spec. Rustc is the language for now.
reply
afdbcreid 2 hours ago
But bugfixes are also allowed. So the fair conclusion is that the implementation is broken, but the language is not - because it's possible to fix, and it's possible for all known soundness issues (including the cve-rs one). For this one specifically, I've heard people are already working on a fix.
reply
cowlevel 2 hours ago
If the compiler is the language that implies there is currently a bug in the language, which will be fixed when the compiler is fixed.
reply
david-gpu 2 hours ago
My understanding is that it is work in progress, via Ferrocene.
reply
beanjuiceII 58 minutes ago
Ferrocene has his same flaw
reply
david-gpu 45 minutes ago
If that's the case then I was wrong.
reply
cowlevel 2 hours ago
Which clause of the language specification is being violated?
reply
lkey 2 hours ago
Lifetimes can be effectively erased via covariant assignment. It's in the title. Even a beginner could understand these assignments are not in spec? Are you just being contrary to be contrary?
reply
csande17 59 minutes ago
If that's the definition you're using, then C is also a safe programming language. In fact, C is even safer, because it actually does have a specification that lists all the invariants that the programmer has to uphold themselves because the compiler doesn't check them.
reply
cowlevel 2 hours ago
So point to the spec
reply
Ukv 42 minutes ago
https://public-docs.ferrocene.dev/main/specification/types-a...

Specifically the `q.0 = Some(&l);` violating 4.15.2:40 ("An assignment expression requires that the type of its value operand is a subtype of the type of its assignee operand.")

reply
beanjuiceII 56 minutes ago
this is probably a great opportunity for rust to get one, if they want to be taken seriously
reply
uniqueusername7 38 minutes ago
I totally understand that people have favourite languages and that rust, like any language and any ecosystem around that language, has idiosyncrasies that are not for everyone, but how would one ever come to the genuine conclusion that rust is not being taken seriously? By whom?
reply