Posted by formerly_proven 11 hours ago
There was one in the Linux kernel a while ago that mistakenly eliminated an important null check.
Is this different?
Here's the current list of similar (unsoundness-causing) open compiler bugs in Rust:
https://github.com/rust-lang/rust/issues?q=state%3Aopen%20la...
I think it's a good thing to keep in mind, that the actual implementation of Rust almost certainly isn't sound. Even an "ideal Rust" still has unsafe, which can affect the soundness of even faraway safe code. But I don't think it's nessesarily wrong to talk about Rust as if it were safe. Much of our communication has implicit assumptions, and the way we talk about Rust is no different. It would be exhausting to qualify every discussion of saftey with "assuming the compiler has no bugs", "assuming all unsafe code follows the (not fully specified!) rules", etc.
Ultimately it's like any other implementation of theory into practice. The theoretical model of Rust lets us reason about and design an implementation to achieve certain practical goals even though the model is a simplification of reality. In Rust's case, practical use has demonstrated it achieves design goals like reducing bugs and reducing development time (compared to peers like C++) to a substantial degree.
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.
By Rice's theorem you cannot be sound and complete, i.e. accept all valid programs and reject all invalid ones. Most languages choose to be sound but not complete, i.e. reject all invalid programs but also some valid ones (C and C++ are the notable exception, although most languages have escape hatches). GP was saying that the fix might reject valid programs that compile today. This is true for some existing soundness bugs, but not necessarily for this one specifically.
That said, I was sarcastic.
Rice's theorem implies that it's impossible to precisely distinguish valid and invalid programs just by looking at their source code. Thus Rust rejects some valid programs in order to be sound (e.g. "if false { None.unwrap_unchecked() }").
In the case of the bug report in OP, I don't think that there are many valid programs in practice that exploit this soundness issue, so fixing it just means rejecting more invalid programs.
You’re ignoring the possibility that Rust semantics are sound, and the implementation doesn’t fully implement them. In that case, we do not have to find sound semantics.
That, I think, by far is the most likely option.
Reason: I’ve not seen a single comment claiming the example should compile, and zillions (implicitly) acknowledging this is not what Rust should do.
> but not necessarily for this one specifically
> Maybe, maybe not.
But "the Rust semantics" are not defined, formally. The closest we have is what Rust implements currently, and that was shown here to not be sound. Other than that, we maybe have vague ideas in the brains of some T-types members, which are not detailed enough to determine soundness (source: those type system people want to formalize the semantics so they will be).
And for the record, we know of other soundness issues where the likely resolution path will involve rejecting valid programs we accept now, and the only question is how many.
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.
The eventual formalization of Rust and a creation of a formally proven compiler is presumably going to be the end point of this process.
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
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.
https://www.lesswrong.com/posts/zHhGa3iQBQcdSifFL/a-critical...
The basic contradiction of the rust fad, coders can't be trusted to be memory safe so those same coders must use rust and then all of sudden they can be trusted not to make common mistakes. And before some tart steps in to bring up safe rust, you're missing my point entirely
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.
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.
Programming will never be dead.
1. Because the situation you describe is fantasy.
2. Even if it is a reality and all software can be verified (it cant), I will still be programming and so will millions of others because its fun, programming and sex are really damn close to things I cant live without. Not everyone got into engineering for the money. I legitimately love this activity, it is my actual passion. I would rather be poor than give it up.
Its like im being told everyday all day, non stop that the thing I love is not worth loving.
I'm not talking about mission critical software, but bespoke business applications that are produced by software engineering sweatshops and where the actual users are inured to incomplete features with sharp edges, arcane bugs, and incomprehensibly long intervals between bug report and fix.
Even in mission critical software we will likely see a lot of LLM tool usage. Even if the copyright plug is pulled and all vibecoded applications are regarded as copyright violations, its uses to analyze existing code and give advice will remain. In this world, writing applications entirely by hand will become an artisanal discipline.
Nothing is stopping you from enjoying the sort of programming that you like. But I think it's healthy to accept that that style will soon not be mainstream. It will be like knitting your own clothes, and that's okay.
Regarding formal verification, it's not needed, as LLMs already make on average about as many errors as the average human programmer.
For me, 90% of programming is just a tool to get the job done. It's like a machine in a factory that spits out 50 spoons a minute, and it doesn't need to be any more beautiful than that. If you enjoy woodcarving and want to spend an hour making your own spoon, do that and don't let the existence of the machine make you feel that hobby is any less worth loving.
The truth is LLMs is what you need to be optimising around as they will become the standard.
Maybe tech isn’t for you if you don’t like trying to stay on top of it.
Anyways, I probably know more about llms than you, I probably use them more than you. I just dont want to turn every conversation into a discussion about them.
I would argue the smartphone is 1000x more impactful than LLMs, along with many other inventions since the internet. I'm not really sure what contribution llms have had that are actually THAT impactful for peoples lives. Its possibly (but unlikely) a net negative, time will tell. Most people using them are not getting smarter, thats concerning. Most are just making more money for their bosses who are itching to replace them with an llm.
If we didn't let people live off the work of others, as the capitalists do, then we could all rejoice in that we've found yet another way of reducing the labor required to produce stuff.
Yet, in the real world, it seems as despite the progress of technology, the labor of the everyman is constant, and only the richest get more and more shiny jets and yachts.
This 2022 article [0] says it generates binaries that run about 7% slower than what GCC produces with -O1.
[0] https://www.cs.cornell.edu/courses/cs6120/2022sp/blog/compce...
It will be interesting to see if fixing this bug actually causes any crates to not compile.
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.
Even beyond Python, most gradual type systems are unsound by design.
I've found type hints to be remarkably useful and catch significantly more bugs than I ever would have thought, and have quickly picked up the practice of using them wherever I code. My general finding is that if I have to struggle to put useful type hints in place, I probably need to rethink my code.
I even type hint my unit tests because I've learned that sometimes type incorrectness means my tests haven't been testing what I thought they were testing.
Frameworks like pydantic[0] can help you turn those type hints into something runtime enforcing too.
[0] https://pydantic.dev/docs/validation/latest/get-started/
But of course Python function were always duck typed so it often didn't matter to the user.
A few past discussions if anyone is curious:
Counterexamples in type systems (2021) - https://news.ycombinator.com/item?id=48718972 - June 2026 (4 comments)
Counterexamples in Type Systems: programs that crash, segfault or explode (2021) - https://news.ycombinator.com/item?id=36213704 - June 2023 (97 comments)
Counterexamples in Type Systems - https://news.ycombinator.com/item?id=27252899 - May 2021 (62 comments)
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.
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...
- people still get value off of an incomplete implementation, because we are allowed to be mindful of the issues (see, eg, UB in C++). In other words, many, many more bugs have been prevented by the borrow checker working as intended than have been caused by it working poorly. It's not particularly close.
- theory almost always moves slower than practice
The mutable state situation pointed out by the grandparent poster was a famous issue in typing SML under mutability that required serious changes in the language, see Section 4.2 here: https://smlfamily.github.io/history/SML-history.pdf
These were Turing-award caliber people figuring it out and, according to your definition, "yolo"ed it. This stuff is _hard_, and these driveby comments truly aren't helpful.
But the unsoundness is because the type system itself, abstractly, was not verified. Or the interactions between parts. I.e. If you verified the compiler you'll find that it implements the algorithms perfectly. But tells you nothing about whether those implemented algorithms, however perfectly implemented, actually provided those guarantees.
Would it be nice if we had even higher level of quality? Yes. But what’s neat is we have empirical evidence - Rust code does achieve the memory safety goals over C++ even though there’s bugs in the compiler. That’s why Rust is very quickly eating all things C and C++. A huge amount of value that the industry would just be unable to access for a long time if you waited for everything to be formally proven.
There are pros and cons to tests, but they do some things very well. For starters a test is a lot better sign that things work as intended than a formal proof which is often so abstract most people can't be sure how it relates to what was intended. You can prove something correct that is clearly wrong. While a test will only test the one case, at least it is typically easy to understand that case and why that answer is correct. Of course if your requirements are correct your proof will tell you it is correct for all cases while a test is limited to that one case. Again pros and cons, and people speaking against tests are not honest about those.
- 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
Not really: the program still needs to be fixed, but fixing the compiler ensures that this unsound behavior never happens again to anyone. So you have two bugs to solve (vs C++'s one bug) but then you kill an entire class of unsoundness bugs just by recompiling.
Can you provide an example of such a program that can’t be fixed at the program level like a C++ example?
For what it's worth, Rust fanboys also seem just as vulnerable to their ignorance of C/C++ compiler extensions and tooling (e.g. sanitizers, bounds annotations, standard library hardening modes and so on.) which are just normal part of professional C/C++ use today.
This is what advocates of Rust are guilty of and CVE-2025-68260 is an example of it. Ultimately TSAN and KTSAN exist for a reason, and rust devs should learn how to use them effectively.
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) 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.
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.
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.
You seem to be implying that because of this, Rust has no definition of its type system (other than whatever happens to be implemented in the current compiler). I would disagree.
As evidence, I would like to point out that not a single person argued that this flaw was not a bug, but was in fact the officially defined behavior. Clearly, the community has a shared understanding of what Rust's type system "ought" to do.
This is a nice ideal, but it doesn't actually exist in concrete terms as a design. It can never have flaws because the definition of it is just "rustc without all the flaws".
Sweet. But why should anyone care..
Java's byte code verification algorithm had a proof. This was important for running untrusted java in the browser. If it said the byte code was ok, it was.
Turns out, nobody made a proof for the verifier actually finishing. So it could return true, or false, or run longer than the universe has time. So around Java 6(?), someone wrote some bytecode that basically took forever to validate, DOSing the validator. Sun had to change the file format and add extra information to get the guarantees back.
> Rust's type system is *intended to prevent memory unsafety. Upholding this promise is something we take very seriously*, .... It is a large priority of the Types Team to *fix all type system unsoundnesses* and to work towards there being a solid foundation to minimize the risk of unknown ones.
So, yes, if you have a group who "takes very seriously" the job to "fix all type system unsoundness", I would expect them to utilize some manner of formal proof verification that their type system is mathematically correct up to some defined limit.
PS: The term you are looking for is formally verified not mathematically proven.
`sum(a:int, b:int)`
not guaranteed ANYTHING.
The gulf between how much correct Rust is and the vas majority of languages is HUGE.
The hand-waving comes from other languages which can’t do anything to prevent their users encounter such bugs and the ensuing CVEs.
Safety is a nice side-benefit. The main reason why I like the safety is because it allows you to fearlessly write high-level code without being constantly paranoid about memory as in C++. It’s more about enabling ergonomics without loosing performance, rather than security or correctness.
I'd disagree with this point, there is more typing involved with a first draft of code than Python or Typescript, that's certainly true, but if we bring all the tools (e.g. docblock comments for "type declaration") that are habitual to make up for the shortcomings in most developer's minds then that gap shrinks and, imo, ends up swapping. It's cheaper to have automatically enforced and consistently maintained checks than moving that information out of band and the resulting code in a strictly typed and memory enforced language ends up removing a significant cognitive overhead when reading and maintaining the code.
I have a strong aversion to boilerplate code so it's important to try and allow terse expressions when possible - but unstated assumptions are worse than overstated assumptions.
With access to inline asm, all speed is ultimately more a measure of effort applied than PLang. So those perf ideas are just "with my level of effort". Even Python via Cython has pretty direct access to asm. The "do it in pure X" that Sun popularized (even in a meta-/cross-PLang-sense) as a business attempt to define Java virtual machines as The platform has had many casualties, not the least of which is the quality of discussion of PLang practicalities, not just on HN, but everywhere.
PS: I was very enthusiastic about Cython for a good while and I went deep into it, but I found that it is rather a mess to use in practice. Yes you get C performance within Python, kinda, but you also get all the C footguns, amplified by the complex interactions with Python features.
EDIT: With Cython how I mostly kept things organized was the "caller allocation" so common in C where memory was always managed the usual mostly refcounting way of CPython/NumPy, but I had dozens (maybe even 100) little "kernels" I could pass that memory into that could even use Intel/MSFT "intrinsic" asm calls (things like immintrin.h) or even just #include C code which did similar. It didn't seem too bad to me, but Nim always seemed a more complete solution (at the PLang level - not counting ecosys). (And these days there is even NimPy (https://github.com/yglukhov/nimpy) to write low-level speed-up code for Python in Nim.)
Yes, you can pass around an object of procedures, but that is awkward, doesn't have any support from the language or stdlib, and from what I can tell isn't a normal thing that is done.
I think you & I are really back & forth-ing "the PLang not enforcing/strongly encouraging a very specific way of doing things that a whole ecosystem/community organizes around". Relying instead on self-organization has many pros & cons, to be sure. Lisp suffered much the same "until" Common Lisp (which still leaves many things open). My tag line lately is: delegation affords so much, but trust sure is tricky. Much of the time people seem to discount the often rather large cost of establishing/maintaining trust. (I'm not saying you are - just that it's an easy thing to do. I think it's why there is this is a big megathread on "trusting rust", even though "rust" is a veritable suffix of "trust". Lol.)
If I need the absolute fastest code, then that code will be tortured under valgrind. That’s non-negotiable in my book.
Code is not done until its memory management is proven correct.
The real shortcoming it is very common for people to not write modern C++. I know several C++ developers who refuse to use smart pointers because they don't like magic happening.
C++ has a terrible reputation which is well associated with, say C++98, if you have a good linter you can bop people on the head for using news anywhere outside a few limited locations where you consider it warranted. All working environments have language features or even programming paradigms that are considered detrimental to code ecosystem health and fighting against these is an organizational lift when working in any programming language. Full type security doesn't mean someone can't create an overly generic object and just start breaking code contracts across the code base. C++ has an awful lot of footguns and more warts than most but it can be made to be sane for development and the new language features really bring it on par with other modern languages.
I have worked in C++ (primarily within the Qt framework which pushed hard into const refs instead of pointers) and it's fine. I've also worked in PHP which can also be fine. Rust isn't something I've worked at scale in but it certainly is very attractive if I ever jump to an org working in it - but even in Rust you'll want to restrict style divergence and bad patterns with linting.
std::unique_ptr<int> p(foo());
std::print("{}\n", *p);
The answer depends on how foo() is implemented. You can't reason about it locally, and strictly speaking it's not possible to determine at compile time. Now, I should be declaring this gsl::not_null<std::unique_ptr<int>> to comply with the core guidelines, but I think we can agree that optional support libraries aren't relevant to this discussion.These issues will exist for all time. The only question is how rare can we make them.
The obvious follow-up is to find a minimal set of rules that allow us to express what we want without permitting temporal unsafety. The borrow checker is one set of fairly minimal rules that works pretty damn well. Alternatively you could define away the unsafety by giving it semantics, like fil-C does.
It is kind of annoying to use, but at least there is an obvious default choice.
Consider reading past the hashtag.
It is moral though, so who cares really.
The report seems to claim the opposite:
> Looking at the bug itself, it is also very likely that this unsoundness has not caused memory unsafety by accident. While it looks easy to trigger, to exploit this you need an intentionally malicious Drop implementation.
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.