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.
It's fascinating that this kind of bug is exactly you can't really avoid even when rust is supposedly to guard against. Rust mitigated this problem a lot in practice, but you can never be certain because of issues like this in practice. This is similar that a math proof that's already been peer reviewed and accepted as truth, may still be possible to be wrong, although the chance is very small but it's not zero. Epseicially when the complexity gets higher.
> It's fascinating that this kind of bug is exactly you can't really avoid even when rust is supposedly to guard against.
Note that Rust is not a theorem prover and Rust programs are usually not formally verified (there are some extensions like Creusot to verify Rust programs, but they are incomplete and a work in progress)
Compiler bugs are, like, just logic bugs. There's a project to eliminate compiler bugs that cause unsoundness, project zero [0] but, still, without formally verifying the implementation, there is nothing really guaranteeing there's no such bugs left.
(note: unsoundness in this context means having safe code cause UB due to a compiler bug; the fix is usually to make the compiler reject the code with a compiler error)
To make matters worse, Rust still doesn't have a spec, and there is not even a tight definition of what are the rules of unsafe code - that is, what rules you must follow so that you unsafe code doesn't trigger UB. Discussions around this are done mainly in issues of the unsafe code guidelines repo [1]
In theory it would be possible to formally verify the soundness of the borrow checker. Maybe with AI advances we'll be able to do that in the near future. I mean, how much more complicated can it really be than proving Navier-Stokes?
Interesting, the main argument is that C need too much expertise of programmer to avoid problems.
Now, you have a language sold as safe, with popularity made manily blaming C... but now we can say it gave the false sense of safety, distracting programmers from real issues in the name of its error messages that lead for endless refactoring if some new feature is added to project just to o have peace with the compiler. It is a language that program the programmer, an inversion of roles.
Not knowing anything about rust, the problem in these type of case is making it rejecting that bad code without also rejecting valid programs. But maybe this is indeed easy to fix, I don't know.
In this case the issue is that the compiler just isn't properly type checking the implicit call to the drop method that's inserted at the end of the nested block. AFAIK, no correct code should depend on the absence of that check.
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).
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.
The idea is that safe Rust promises that you can't cause UB without unsafe.
Unfortunately there are cases, where the current rust compiler implementation fails to keep that promise. These cases are all tracked as bugs and it's expected they'll be fixed eventually. AFAIK non of the known cases is unfixable, it just takes time and effort, sometime a lot.
Also note that C++ does not make such promises in the first place, so the equivalent code there isn't a bug, it's just UB.
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.
It is well known that proof assistants can be wrong. Fortunately, the theories underlying formal methods are usually simple enough that it is feasible to write verifiers for these proofs.
I think that’s a myth! What tends to happen in mature software is there are unfixable tradeoffs that can be made that favor one use case or another. And of course people will keep adding features too
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 just try not to shove llms into every single conversation.
Is there a hackernews type space where the word is banned? Even lobste.rs has this problem. I need some sort or way to just hide all content on certain sites where LLMs/AI are mentioned.
I'm not a huge AI hype man, but I think you'll just have to accept it. It's a new useful tool, of course people are excited to apply it to everything.
Like it or not, programming will become largely LLMs. And the comment you're replying to is actually making a pretty good case for where the use of LLMs is good: generating code to be formally verified.
The compiler. That’s the whole point here. Type systems can prove the absence of entire classes of bugs, at compile time. If AI identifies bugs in type systems that can be corrected, then all future programs benefit, including those written by AI.
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
The emotional reactions to this stuff are so weird. Yeah, software has bugs, what a surprise. That doesn't mean that static safety is useless or that we should all just continue YOLOing stuff in C.
You can iterate through all unsafe sections of the codebase by simply grepping for `unsafe`, the rules are followed everywhere else. It's a great improvement.
That's not how "unsafe" works at all. If "unsafe" is so commonplace in a code base that your statement becomes relevant then the project is badly run, and memory safety is just another problem among many others. Similar to accepting code with empty or nonsensical `catch` blocks.
As always the same tropes get repeated ad nauseam.
Rust code using `unsafe` != C code:
-) you can encapsulate `unsafe` internals behind safe interfaces
-) you can concentrate operations requiring manual safety reasoning in small parts of the codebase
-) safety obligations become explicit
-) `unsafe` does not turn off the borrow checker, type checking, ordinary bounds checks, or automatic cleanup and you still benefit from Rust's provided abstractions and its composability
I'm not a Rust shill, and not a single programming language I use is part of my online identity, but if "Rust is a cult", the anti-Rust gang isn't?
Getting more and more annoyed by the latter to be honest.
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.
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.
'static lifetime bounds are kind of confusing in Rust, but here goes: A 'static lifetime constraint on T says: types that satisfy T must not contain any borrowed value/content whose lifetime will be shorter than the life of the program. So suppose you have a string s, and Option<&String> q. If q has the constraint 'static, and you do q = Some(&s) this should be rejected by the compiler since a reference to s gets a shorter lifetime than 'static by default (you could do q = Some(&'static s) if s is say a string literal or a leaked box but not for a normal string). In the examples it looks like you have to use a tuple to trick the borrow checker but the concept is the same: Rust allowed violation of the 'static lifetime bounds. The drop implemenation demonstrates it: drop happens in reverse declaration order, so the string s gets dropped first. Now q has a dangling reference, a pointer to dropped memory essentially, and we get a pretty classic use after free bug.
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.
https://github.com/JelleZijlstra/unsoundness is also a great collection of Python type system holes. In these cases, it's mostly deliberate design decisions, not bugs. The unfortunate fact is that a fully sound Python type system would be a pain to use.
Perhaps I'm just an old dog that doesn't want to learn new tricks, but I still can't think of Python as typed and I never use type hints. I started with Python during the 2.3 era. I still view Python types as merely docs. So it's nice that someone tracks mypy quirks but I can't imagine relying on the results for anything serious.
At least Rust has a chance to be largely correct because it has much greater effort per amount of code (in the compiler) invested into it than most any application code.
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
EDIT: I said new features of the type system and more importantly, their interactions with the ever growing list of other features, not new features in the compiler. Totally different things. The compiler could be perfectly implementing the wrong choice of algorithms in the first place.
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.
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.
Formal verification of the type system does seem a worthwhile goal, and to my understanding it's something the community has been making progress towards, but I also understand it's still relatively uncommon for most languages.
Is this more an abstract concern over the state of the industry, or is there a specific language you had in mind that you'd prefer Rust was modeled after?
Important to note that this is is not a bug in the type system itself, it's a bug in the borrow checker not enforcing invariants that the type system says that it should, and formal verification of the type system would not have helped here.
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.
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...
A 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.
That's a misunderstanding. This is not a security vulnerability and Rust explicitly does not defend from malicious programs, only from mistakes. This bug however is quite dangerous, because unlike many soundness bugs it seems plausible to hit by mistake.
Why bother? Most systems these days have over commit and so you can never get an out of memory error. The OS will happily give you more and more RAM, and then when you try to use that memory kill a program (may or not be your program). The only out of memory error you can see is if you are trying to allocate more memory than the address space allows. This is so unlikely that nobody considers it worth bother to try to handle.
It is important to try, for a few reasons. Among other things, increasing amounts of Rust written in the Linux kernel itself, and other environments where you need to take more care with memory allocation.
Rust as a language is capable of that. If you don't use the standard library then you can provide that functionality yourself. The standard library is trying to support it, without creating interfaces that are a massive pain to use.
For instance, it's easy for us to support `Vec::try_reserve`, and we do, but that's not trivial for users to use everywhere; it's error-prone, and it's easy to make a mistake and call a method that goes past your reservation. It's painful for us and for users to make `Vec::push` support this (and having a `try_` variant for every function), so we've been working on a better solution. One current possibility is to have a dedicated type for a Vec that has OOM handling, where `push` can return an error, so you know if you use that type you can never accidentally fail to handle OOM.
Sure if you ignore the most popular desktop OS in the world.
Also overcommit (at least how Linux does it) is dumb. It leads to broken OOM behaviour (killing a totally random process, or more often locking up the system and hard rebooting). And some apps do need to handle OOM properly, e.g. formal verification systems which can use enormous amounts of memory. Jasper has to resort to monitoring page fault stats to know when it is using too much memory.
the biggest design wart of rust (well one of them) is that support for custom allocators wasn't built into the type system from the start, with allocation osmething that returns Result
As much as I would love this on principle, I don't think Rust can make it ergonomic without huge changes like bolting on algebraic effects or something.
On a typical Linux box, overcommit and eager or confused OOM killer will ruin your day anyway.
For no_std style work where running out of memory is actually a hard limit, you have two choices:
1. disable the global allocator and use APIs that take Allocator, to force yourself to only consume APIs that can return errors from allocation. (The ergonomics aren't always great, and fattening all types with a pointer to Allocator isn't great; might be able to use a zero-sized Allocator, with trickery. Say goodbye to many libraries.)
2. use static analysis and try to avoid ever hitting a code path like Vec::with_capacity that doesn't return a Result. Make sure the tooling also analyses your dependencies. (Tooling for the static analysis is painful. And still, say goodbye to many libraries.)
I do wish there were more conveniences for alloc-can-fail APIs, like the failable version of Vec::push needs both try_reserve and push_within_capacity (nightly only).
The Rust compiler (rustc) is written in Rust, but there can always be bugs in the compiler, regardless of whether it's written in a memory-safe language or not.
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.
Nobody writes variance at hand in Rust because it's impossible (since before 1.0). You sometimes need to enforce stricter variance than the default (can cause unsoundness and is very subtle but actual unsoundness here is rare) or give more permissive variance (harder and also cannot cause unsoundness, just annoyance).
When implementing the type system, however, variance does have to be treated with care. Historically it wasn't much, now it is.
This makes me feel better about my programming language I'm building. No amount of eyes can solve every problem.
I'm using sqlite's testing principles that Dr Hipp explains (MC/DC, mutation testing, fuzzy checking, property testing) and even some loose lean proofs written by LLMs, but in the end, it's all just kayfabe. I'll find out some bug one day anyway.
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.
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.
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).
Not at all. There are way more valid lifetimes than the lifetime system of Rust can express. There are countless scenarios where you could accidentally use the unsound behaviour while not producing a segmentation fault because the lifetimes happens to be arranged such that no invalid memory access actually happened.
A program isn't invalid just because it violates the ownership and borrowing rules of Rust. There are tons of programs in the world with shared, mutable access, devoid of Mutexes, Arcs and Locks that nonetheless are completely valid.
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.
That's not how it works. rustc doesn't "check" for specific cases explicitly, the borrow checker (apparently at fault here by reports) has a set of rules that should reject invalid programs. So either one of the fundamental rules was broken (unlikely here from what I've heard), or some optimization caused the rules to not be implemented correctly.
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).
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.
> On a serious note, old software is generally more trustworthy than new software, because bugs are discovered over time and with usage.
The age of software means bugs are more likely to be discovered but not necessarily fixed. In the case of Rust, the developers have documented a whole lot of unsoundness bugs that require a new borrow checker architecture to fix.
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".
It seems the bug here was caught by Asan instead of at compile time, so basically, for this one bug, rust is reduced to C++-like behavior. You’re still better off using rust because of all the bugs the borrow checker does catch.
The practical upshot to me is that you should be running your rust tests under asan.
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.
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.
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".
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.
Please stop registering accounts to post these kinds of comments. On HN it's unacceptable to attack or mock people, or make baseless accusations of being shills or stooges like this. It's not what HN is for and destroys what it is for. Please take some time to read the guidelines and make an effort to observe them if you want to participate here. We have to ban accounts that sign up to continue pursuing personal agendas. https://news.ycombinator.com/newsguidelines.html
Hey, but it's genuinely a nice language. Maybe they're not the most brilliant of type theorists, but even without ownership/borrowing, just stuff like traits, structs/enums/unions with pattern matching, etc, is done quite well.
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.
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.
It's possible to scan compiled programs at the assembly or machine code level. Not enough people do, which is fair enough because it's a slog, but it makes all compiler UB transparent to the analysis.
I think one social issue I’m seeing is that the Rust community bashes other languages for safety but then glosses over or even completely ignores their own safety risks (see supply chain). I would rather see Rust focus on its actual features without bashing others and maybe start moving away from claiming the gold metal on security. Being less toxic is always a good thing for long term health.
It's important to separate the community ethos and the rust core dev team policies, because I'd be surprised if the official stance of the rust team would be condescending to other technologies.
Rust is my favorite language in most domains I program in; I've learned to ignore the online rust OSS community due to a mix of preferring heavy abstractions (then shaming your code if you don't use them), and rude behavior.
This is fine, for the reasons you state: I can use the language and tools while ignoring the jerks.
I’d be shocked if people working on and passionate about thing didn’t shit on other thing. That’s natural and even good in moderation. It’s up to a leader to keep it under control.
Can you give an example of this toxic bashing from rust contributors or are you talking about just random HN posters when you say “rust community”?
Moreover, I wouldn’t say they’re completely ignoring supply chain risks, as there have been discussions about this as early as 2021 [1] and there are ongoing efforts to address this [2]
Yeah Ive actually never seen any Rust team members be toxic. Keep in mind there is a micro niche of tech influencers who try to politicize rust because they associate it with liberalism or something like that and paint Rust community as an opposing force to some status quo they think exists or should exist. Those notions have taken on a life of their own.
People who say this just dont know what they're talking about and are repeating youtubers and X takes.
the only thing more exhausting than rust evangelism is people who drop in on any rust related thread and start whining about unrelated things just because it's rust.
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.
It's fascinating that this kind of bug is exactly you can't really avoid even when rust is supposedly to guard against. Rust mitigated this problem a lot in practice, but you can never be certain because of issues like this in practice. This is similar that a math proof that's already been peer reviewed and accepted as truth, may still be possible to be wrong, although the chance is very small but it's not zero. Epseicially when the complexity gets higher.
> It's fascinating that this kind of bug is exactly you can't really avoid even when rust is supposedly to guard against.
Note that Rust is not a theorem prover and Rust programs are usually not formally verified (there are some extensions like Creusot to verify Rust programs, but they are incomplete and a work in progress)
Compiler bugs are, like, just logic bugs. There's a project to eliminate compiler bugs that cause unsoundness, project zero [0] but, still, without formally verifying the implementation, there is nothing really guaranteeing there's no such bugs left.
(note: unsoundness in this context means having safe code cause UB due to a compiler bug; the fix is usually to make the compiler reject the code with a compiler error)
To make matters worse, Rust still doesn't have a spec, and there is not even a tight definition of what are the rules of unsafe code - that is, what rules you must follow so that you unsafe code doesn't trigger UB. Discussions around this are done mainly in issues of the unsafe code guidelines repo [1]
[0] https://goals.rust-lang.org/2026/roadmap-project-zero.html
https://github.com/rust-lang/unsafe-code-guidelines/issues
1 reply →
How is this different from any other compiler bug?
There was one in the Linux kernel a while ago that mistakenly eliminated an important null check.
Is this different?
2 replies →
In theory it would be possible to formally verify the soundness of the borrow checker. Maybe with AI advances we'll be able to do that in the near future. I mean, how much more complicated can it really be than proving Navier-Stokes?
7 replies →
Interesting, the main argument is that C need too much expertise of programmer to avoid problems.
Now, you have a language sold as safe, with popularity made manily blaming C... but now we can say it gave the false sense of safety, distracting programmers from real issues in the name of its error messages that lead for endless refactoring if some new feature is added to project just to o have peace with the compiler. It is a language that program the programmer, an inversion of roles.
10 replies →
... and even if you formalize it, it could still be false due to an error in the theorem prover itself.
7 replies →
Not knowing anything about rust, the problem in these type of case is making it rejecting that bad code without also rejecting valid programs. But maybe this is indeed easy to fix, I don't know.
In this case the issue is that the compiler just isn't properly type checking the implicit call to the drop method that's inserted at the end of the nested block. AFAIK, no correct code should depend on the absence of that check.
There are no such valid programs. Proof by contradiction:
A valid program would be exploiting undefined behavior in rust, but rust has no undefined behavior. Thus such a program must not exist.
Now a program may appear to work before the fix, but it was never a valid Rust program.
14 replies →
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).
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.
Writing manual Drop isn't usual. And writing drop with such lifetimes is definitely on the weirder scale.
[flagged]
Not really.
The idea is that safe Rust promises that you can't cause UB without unsafe.
Unfortunately there are cases, where the current rust compiler implementation fails to keep that promise. These cases are all tracked as bugs and it's expected they'll be fixed eventually. AFAIK non of the known cases is unfixable, it just takes time and effort, sometime a lot.
Also note that C++ does not make such promises in the first place, so the equivalent code there isn't a bug, it's just UB.
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.
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.
Perhaps they meant "like an iron, it will stamp out the bugs"
Until they find bugs in the formal proof program ;)
https://www.lesswrong.com/posts/zHhGa3iQBQcdSifFL/a-critical...
It is well known that proof assistants can be wrong. Fortunately, the theories underlying formal methods are usually simple enough that it is feasible to write verifiers for these proofs.
1 reply →
The UB goes all the way down!
> complete reliability.
I think that’s a myth! What tends to happen in mature software is there are unfixable tradeoffs that can be made that favor one use case or another. And of course people will keep adding features too
That’s why they said “closer”
Just wait until someone relies on that bug too.
1 reply →
What aspects of it are remotely ironic?
Obviously, many people have viewed AI’s contribution to programming as just producing unreliable garbage.
Yes, that’s an incomplete and tenuous belief, but that’s where the irony is. If you’re omniscient you don’t get to experience irony.
Hey, if Alanis Morissette can make the same mistake…
“Slop generator makes all future programs more reliable”
5 replies →
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 just try not to shove llms into every single conversation.
Is there a hackernews type space where the word is banned? Even lobste.rs has this problem. I need some sort or way to just hide all content on certain sites where LLMs/AI are mentioned.
I browse Hacker News stories via this: https://unslop.news/
3 replies →
Reddit r/programming banned AI topics and AI written content and comments. Unfortunately there is not a whole lot going on there anymore as a result.
I'm not a huge AI hype man, but I think you'll just have to accept it. It's a new useful tool, of course people are excited to apply it to everything.
Like it or not, programming will become largely LLMs. And the comment you're replying to is actually making a pretty good case for where the use of LLMs is good: generating code to be formally verified.
11 replies →
lobste.rs has a `vibecoding` tag that you can hide.
2 replies →
and what stops AI from "accidentally" writing code with such bugs?
Code review and testing? As always?
2 replies →
The compiler. That’s the whole point here. Type systems can prove the absence of entire classes of bugs, at compile time. If AI identifies bugs in type systems that can be corrected, then all future programs benefit, including those written by AI.
Or we realize that it is not possible, and Rust accepts to be "just" a definitely safer C.
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
12 replies →
The emotional reactions to this stuff are so weird. Yeah, software has bugs, what a surprise. That doesn't mean that static safety is useless or that we should all just continue YOLOing stuff in C.
[flagged]
You can iterate through all unsafe sections of the codebase by simply grepping for `unsafe`, the rules are followed everywhere else. It's a great improvement.
1 reply →
That's not how "unsafe" works at all. If "unsafe" is so commonplace in a code base that your statement becomes relevant then the project is badly run, and memory safety is just another problem among many others. Similar to accepting code with empty or nonsensical `catch` blocks.
4 replies →
As always the same tropes get repeated ad nauseam.
Rust code using `unsafe` != C code:
-) you can encapsulate `unsafe` internals behind safe interfaces
-) you can concentrate operations requiring manual safety reasoning in small parts of the codebase
-) safety obligations become explicit
-) `unsafe` does not turn off the borrow checker, type checking, ordinary bounds checks, or automatic cleanup and you still benefit from Rust's provided abstractions and its composability
I'm not a Rust shill, and not a single programming language I use is part of my online identity, but if "Rust is a cult", the anti-Rust gang isn't?
Getting more and more annoyed by the latter to be honest.
2 replies →
I feel like these things get waved around as gotchas without any sense for whether any real code is impacted.
It will be interesting to see if fixing this bug actually causes any crates to not compile.
We should know shortly, the PR to fix this will presumably be paired a crater run, a build of the entire public crates universe.
The heart of the matter:
I wonder if the certified compiler for safety critical uses (edit: Ferrocene) has the same issue.
> 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).
Can someone ELI5 for people who don't know Rust (but e.g. C)?
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.
14 replies →
'static lifetime bounds are kind of confusing in Rust, but here goes: A 'static lifetime constraint on T says: types that satisfy T must not contain any borrowed value/content whose lifetime will be shorter than the life of the program. So suppose you have a string s, and Option<&String> q. If q has the constraint 'static, and you do q = Some(&s) this should be rejected by the compiler since a reference to s gets a shorter lifetime than 'static by default (you could do q = Some(&'static s) if s is say a string literal or a leaked box but not for a normal string). In the examples it looks like you have to use a tuple to trick the borrow checker but the concept is the same: Rust allowed violation of the 'static lifetime bounds. The drop implemenation demonstrates it: drop happens in reverse declaration order, so the string s gets dropped first. Now q has a dangling reference, a pointer to dropped memory essentially, and we get a pretty classic use after free bug.
which compiler?
Ferrocene
DeepSeek v4.1 Flash thinks it should NOT compile. Whereas in reality it DOES compile which is why it is filed as a bug.
They're seriously using AI as a judge for what to file?
3 replies →
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:
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.
https://github.com/JelleZijlstra/unsoundness is also a great collection of Python type system holes. In these cases, it's mostly deliberate design decisions, not bugs. The unfortunate fact is that a fully sound Python type system would be a pain to use.
> The unfortunate fact is that a fully sound Python type system would be a pain to use.
Even beyond Python, most gradual type systems are unsound by design.
Perhaps I'm just an old dog that doesn't want to learn new tricks, but I still can't think of Python as typed and I never use type hints. I started with Python during the 2.3 era. I still view Python types as merely docs. So it's nice that someone tracks mypy quirks but I can't imagine relying on the results for anything serious.
7 replies →
> Check out https://news.ycombinator.com/item?id=27252899 - May 2021 (62 comments)
Aren’t some type systems (eg Lean) able to essentially verify/prove arbitrary mathematics?
Yes, until you introduce inconsistent axioms. Then you can prove anything.
"Type systems are hard!" As hard as its safety is pushed on us, not even Rust is free from hand waving.
At least Rust has a chance to be largely correct because it has much greater effort per amount of code (in the compiler) invested into it than most any application code.
10 replies →
Rust’s type system enables the possibility of discovering and fixing a bug like this, and ruling out this class of bugs for all future programs.
The hand-waving comes from other languages which can’t do anything to prevent their users encounter such bugs and the ensuing CVEs.
I mean, Rust's whole shtick is safety, so that's really the only thing there is to "push" about the language.
32 replies →
> not even Rust is free from hand waving.
It is moral though, so who cares really.
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
EDIT: I said new features of the type system and more importantly, their interactions with the ever growing list of other features, not new features in the compiler. Totally different things. The compiler could be perfectly implementing the wrong choice of algorithms in the first place.
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.
11 replies →
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.
7 replies →
There is a massive possibility space between “yolo” and formal proof. Rust has substantial evidence of correctness. For example, unit tests and types.
31 replies →
Formal verification of the type system does seem a worthwhile goal, and to my understanding it's something the community has been making progress towards, but I also understand it's still relatively uncommon for most languages.
Is this more an abstract concern over the state of the industry, or is there a specific language you had in mind that you'd prefer Rust was modeled after?
1 reply →
Important to note that this is is not a bug in the type system itself, it's a bug in the borrow checker not enforcing invariants that the type system says that it should, and formal verification of the type system would not have helped here.
The Java type system is also unsound. That’s life. It is hard to do this fully correctly. In practice this never comes up.
1 reply →
The font color is so close to the background now that I can't even read your edit. Sorry, bud.
[flagged]
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.
17 replies →
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...
A 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.
That's a misunderstanding. This is not a security vulnerability and Rust explicitly does not defend from malicious programs, only from mistakes. This bug however is quite dangerous, because unlike many soundness bugs it seems plausible to hit by mistake.
8 replies →
I just wish Rust handled out of memory more gracefully like a normal error.
Why bother? Most systems these days have over commit and so you can never get an out of memory error. The OS will happily give you more and more RAM, and then when you try to use that memory kill a program (may or not be your program). The only out of memory error you can see is if you are trying to allocate more memory than the address space allows. This is so unlikely that nobody considers it worth bother to try to handle.
It is important to try, for a few reasons. Among other things, increasing amounts of Rust written in the Linux kernel itself, and other environments where you need to take more care with memory allocation.
Rust as a language is capable of that. If you don't use the standard library then you can provide that functionality yourself. The standard library is trying to support it, without creating interfaces that are a massive pain to use.
For instance, it's easy for us to support `Vec::try_reserve`, and we do, but that's not trivial for users to use everywhere; it's error-prone, and it's easy to make a mistake and call a method that goes past your reservation. It's painful for us and for users to make `Vec::push` support this (and having a `try_` variant for every function), so we've been working on a better solution. One current possibility is to have a dedicated type for a Vec that has OOM handling, where `push` can return an error, so you know if you use that type you can never accidentally fail to handle OOM.
> Most systems these days have over commit
Sure if you ignore the most popular desktop OS in the world.
Also overcommit (at least how Linux does it) is dumb. It leads to broken OOM behaviour (killing a totally random process, or more often locking up the system and hard rebooting). And some apps do need to handle OOM properly, e.g. formal verification systems which can use enormous amounts of memory. Jasper has to resort to monitoring page fault stats to know when it is using too much memory.
1 reply →
For micros and other embedded spaces.
the biggest design wart of rust (well one of them) is that support for custom allocators wasn't built into the type system from the start, with allocation osmething that returns Result
Sure, but it's worth acknowledging that that specific wart is being fixed.
https://doc.rust-lang.org/std/alloc/trait.Allocator.html
As much as I would love this on principle, I don't think Rust can make it ergonomic without huge changes like bolting on algebraic effects or something.
On a typical Linux box, overcommit and eager or confused OOM killer will ruin your day anyway.
For no_std style work where running out of memory is actually a hard limit, you have two choices:
1. disable the global allocator and use APIs that take Allocator, to force yourself to only consume APIs that can return errors from allocation. (The ergonomics aren't always great, and fattening all types with a pointer to Allocator isn't great; might be able to use a zero-sized Allocator, with trickery. Say goodbye to many libraries.)
2. use static analysis and try to avoid ever hitting a code path like Vec::with_capacity that doesn't return a Result. Make sure the tooling also analyses your dependencies. (Tooling for the static analysis is painful. And still, say goodbye to many libraries.)
I do wish there were more conveniences for alloc-can-fail APIs, like the failable version of Vec::push needs both try_reserve and push_within_capacity (nightly only).
The question is. If rust used rust would that have prevented this?
Rust does not prevent all bugs, only certain ones (that are common and dangerous).
The Rust compiler (rustc) is written in Rust, but there can always be bugs in the compiler, regardless of whether it's written in a memory-safe language or not.
it's a joke, riffing on the "would rust have fixed this?" referring to any vulnerability
Rust allows for type-proofing your logic, but it can't force you to.
no
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.
Nobody writes variance at hand in Rust because it's impossible (since before 1.0). You sometimes need to enforce stricter variance than the default (can cause unsoundness and is very subtle but actual unsoundness here is rare) or give more permissive variance (harder and also cannot cause unsoundness, just annoyance).
When implementing the type system, however, variance does have to be treated with care. Historically it wasn't much, now it is.
This makes me feel better about my programming language I'm building. No amount of eyes can solve every problem.
I'm using sqlite's testing principles that Dr Hipp explains (MC/DC, mutation testing, fuzzy checking, property testing) and even some loose lean proofs written by LLMs, but in the end, it's all just kayfabe. I'll find out some bug one day anyway.
Could you share any reading material for this?
I'm sure we'll get more of those on other languages / frameworks too. I tend to believe that it's for good
There is a collection of these someplace. Rust is better but it'll never be perfect, news at 10
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.
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.
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.
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).
7 replies →
> is definitely something you'd easily come across in normal code.
The fact that nobody reported this before means it's not, by definition.
Not at all. There are way more valid lifetimes than the lifetime system of Rust can express. There are countless scenarios where you could accidentally use the unsound behaviour while not producing a segmentation fault because the lifetimes happens to be arranged such that no invalid memory access actually happened.
A program isn't invalid just because it violates the ownership and borrowing rules of Rust. There are tons of programs in the world with shared, mutable access, devoid of Mutexes, Arcs and Locks that nonetheless are completely valid.
2 replies →
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.
That's not how it works. rustc doesn't "check" for specific cases explicitly, the borrow checker (apparently at fault here by reports) has a set of rules that should reject invalid programs. So either one of the fundamental rules was broken (unlikely here from what I've heard), or some optimization caused the rules to not be implemented correctly.
1 reply →
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).
I've written dozens of custom Drop implementations. Well, many of them are named cleanup because of the async Drop mess in Rust, but...
15 replies →
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.
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.
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?
12 replies →
Shoulda coded it in C
source?
Beg, Borrow, or Steal: a guide to Rust object ownership
I don't understand how something like this could take so long to be discovered. Pretty crazy stuff.
> 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.
> On a serious note, old software is generally more trustworthy than new software, because bugs are discovered over time and with usage.
The age of software means bugs are more likely to be discovered but not necessarily fixed. In the case of Rust, the developers have documented a whole lot of unsoundness bugs that require a new borrow checker architecture to fix.
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".
Because it's rarely used like this, just like weird bugs caused by edge cases found in all kinds of software in the world.
[flagged]
[flagged]
[dead]
[flagged]
It seems the bug here was caught by Asan instead of at compile time, so basically, for this one bug, rust is reduced to C++-like behavior. You’re still better off using rust because of all the bugs the borrow checker does catch.
The practical upshot to me is that you should be running your rust tests under asan.
[flagged]
That was a bit generic and easy to swap "Rust" for whatever...
[flagged]
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.
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.
6 replies →
People in sugar glass houses shouldn't throw stones :P
You got my upvote and this one decade old issue:
https://github.com/rust-lang/rust/issues/25860
Is this the one exploited in cve-rs?
[flagged]
You got my upvote.
And I laugh at downvoters.
[flagged]
[flagged]
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.
What's l- here? Literal?
2 replies →
in compare to c and cpp? it is
And in absolute terms?
8 replies →
Let us not conflate a bug in an implementation (rustc) with a defect in the language (Rust).
AFAIK there isn't a language spec. Rustc is the language for now.
7 replies →
Which clause of the language specification is being violated?
14 replies →
[flagged]
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".
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.
The borrow checker has been rewritten to improve this once, and another attempt (that is thought to clear up the rest) is on the way.
Things taking time to fix is not the same as not caring.
[flagged]
Please stop registering accounts to post these kinds of comments. On HN it's unacceptable to attack or mock people, or make baseless accusations of being shills or stooges like this. It's not what HN is for and destroys what it is for. Please take some time to read the guidelines and make an effort to observe them if you want to participate here. We have to ban accounts that sign up to continue pursuing personal agendas. https://news.ycombinator.com/newsguidelines.html
Hey, but it's genuinely a nice language. Maybe they're not the most brilliant of type theorists, but even without ownership/borrowing, just stuff like traits, structs/enums/unions with pattern matching, etc, is done quite well.
What's wrong with Wales?
The tech of the rust language is real though and is providing real world value
very very good job
And a fork fixed it before upstream rust :)
...yes? that's usually the case in open source software.
Was this done by LLM? this is a doubt. because recently we are seeing many such bug reports.
wonder what happened to this https://raphlinus.github.io/rust/2020/01/18/soundness-pledge...
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.
What do you mean?
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
It's possible to scan compiled programs at the assembly or machine code level. Not enough people do, which is fair enough because it's a slog, but it makes all compiler UB transparent to the analysis.
That is less and less true by the day, for better or worse.
I think one social issue I’m seeing is that the Rust community bashes other languages for safety but then glosses over or even completely ignores their own safety risks (see supply chain). I would rather see Rust focus on its actual features without bashing others and maybe start moving away from claiming the gold metal on security. Being less toxic is always a good thing for long term health.
It's important to separate the community ethos and the rust core dev team policies, because I'd be surprised if the official stance of the rust team would be condescending to other technologies.
Rust is my favorite language in most domains I program in; I've learned to ignore the online rust OSS community due to a mix of preferring heavy abstractions (then shaming your code if you don't use them), and rude behavior.
This is fine, for the reasons you state: I can use the language and tools while ignoring the jerks.
Have you met humans?
I’d be shocked if people working on and passionate about thing didn’t shit on other thing. That’s natural and even good in moderation. It’s up to a leader to keep it under control.
2 replies →
Can you give an example of this toxic bashing from rust contributors or are you talking about just random HN posters when you say “rust community”?
Moreover, I wouldn’t say they’re completely ignoring supply chain risks, as there have been discussions about this as early as 2021 [1] and there are ongoing efforts to address this [2]
[1] https://internals.rust-lang.org/t/about-supply-chain-attacks...
[2] https://kerkour.com/fixing-rust-supply-chain-security
Yeah Ive actually never seen any Rust team members be toxic. Keep in mind there is a micro niche of tech influencers who try to politicize rust because they associate it with liberalism or something like that and paint Rust community as an opposing force to some status quo they think exists or should exist. Those notions have taken on a life of their own.
People who say this just dont know what they're talking about and are repeating youtubers and X takes.
[flagged]
2 replies →
the only thing more exhausting than rust evangelism is people who drop in on any rust related thread and start whining about unrelated things just because it's rust.
> being less toxic
There's a difference between people you disagree with and toxic behavior. Can you give examples of toxic behavior?
Dream on