| ▲ | Memory Safety Bugs in Rust Standard Library(forge-blog.github.io) |
| 125 points by pjmlp 2 days ago | 54 comments |
| |
|
| ▲ | simonask 2 hours ago | parent | next [-] |
| Just because emotions seem to be always boiling whenever Rust is mentioned, I want to clarify for people who only follow casually that there is nothing unusual or scandalous about these finds. The Rust project as a whole currently has 178 open issues with the tag I-unsound[1]. Many of these describe issues that only exist in the nightly compiler, of course, but it does happen that bugs are not caught before shipping. That's software. [1]: https://github.com/rust-lang/rust/issues?q=is%3Aissue+state%... |
| |
| ▲ | cowlevel 22 minutes ago | parent | next [-] | | That there's nothing unusual about these finds, the Drop static lifetime bug, or cve-rs is proof that Rust is not, and probably never will be, memory-safe. It's still an improvement though. | |
| ▲ | Sytten an hour ago | parent | prev | next [-] | | Even more, the fact that unsafe blocks are a thing allows for this kind of deep scrutiny of the STD. | |
| ▲ | shevy-java 11 minutes ago | parent | prev | next [-] | | > there is nothing unusual or scandalous about these finds I would not go so far. I just mentioned in a separate comment, a minute before writing this here, that it is rather
outrageous that the premier language for AI these days (aka Rust),
has not used AI to find these bugs before. Would you not
also consider that it should follow from simple logic that
a language that is the premier AI language now, also would
use AI as it preaches for it, to find bugs? | | |
| ▲ | WOTERMEON 3 minutes ago | parent [-] | | Are you paddling some? I’ve never seen rust described as premier language for AI |
| |
| ▲ | fsckboy 2 hours ago | parent | prev [-] | | >that there is nothing unusual or scandalous about these finds that there is nothing unusual is the scandal | | |
| ▲ | simonask 2 hours ago | parent [-] | | Why would you think that? Bugs exist in complex software no matter how hard you try, including software written in Rust such as the Rust compiler and standard library. Did you expect the highly competent people working on these projects to just one-shot everything? | | |
| ▲ | thesz 28 minutes ago | parent | next [-] | | From the article: "The findings reveal three recurring patterns of misplaced trust in unsafe code: trusting code not to panic, trusting user-provided implementations to be correct, and trusting the underlying system." The call to "panic" is an effect and can be checked at compile time. Haskell does this with MonadError class, marking up code that may throw. Rust has Turing complete type system for years and still does not have effects of any kind. To be fair, "trusting user provided implementation" is also has something to do with effects. Proper type system would require user-provided implementation to use only certain effects and nothing else. So two of of found shortcomings are directly related to the type system and, specifically, to the choice of original Rust implementors to not to distinguish between pure and effectful computations. Do Rust implementors highly competent? Yes, of course! They work on Turing complete type system. Will they eliminate two of three recurring patterns? I strongly doubts so. It is a design decision and these are especially hard to change. Changing it will also break all Rust source code produced to that point of time. Is it a scandalous situation? For me, it is. The situation is so dire, actually, that "highly competent people working" has to be defended with trivially true fact that "bugs exist in complex software no matter how hard you try," instead of addressing core problem. | |
| ▲ | cowlevel 21 minutes ago | parent | prev | next [-] | | Actually, software that probably has no bugs does exist, starting with the seL4 microkernel. Such software is usually pretty simplistic because that goes a long way towards proving an absence of bugs. More practically, however, Rust has proven to have more memory safety bugs than GC languages. If you want memory safety you should go GC. If you want consistent performance or tight control over memory, and pretty good memory safety, then you should go Rust. | |
| ▲ | teiferer an hour ago | parent | prev [-] | | You may have noticed the wave of "rewritten in rust" software in recent years, whose only (?) selling point was that this makes the software somehow automatically safe and if not bug free then at least significantly lower on bugs. This finding is just yet another nail in the coffin of that myth. | | |
| ▲ | irealis an hour ago | parent | next [-] | | I have never understood this argument. Rust, by default, is safer. This is trivially true. But it also is not perfect. This entire argument against Rust hinges on it somehow invalidating all of the improvements it does have, all the safety it does guarantee, just because it also has a few bugs. It's neither a myth that Rust is safer, nor is it a nail in the coffin for it. Otherwise you must make the argument that just because unsafe exists, and is used, it is automatically as bad as let's say C - a language that has proven time and time again how trivial it is to make mistakes with. Something that just doesn't happen in Rust. | | |
| ▲ | nopurpose 11 minutes ago | parent | next [-] | | In addition to that, many of reported soundness issues rely on adversarial implementations for stdlib' traits by user's types, like Borrow::borrow or as_ref returning different value between back to back calls. You would do that if you try to break compiler, but not when writing regular programs. | |
| ▲ | Galanwe an hour ago | parent | prev | next [-] | | The annoyance comes from the fact that your kind of _reasonable_ discourse on safety is always thrown when there is a bug, but never when there is a claim that rust is 100% by construction. I.e. It's always a defensive argument, but I never see someone step up to mention it on overly optimistic claims. | | |
| ▲ | simonask 43 minutes ago | parent [-] | | I can't be defending the statements of whoever says this, but I do think that the reasonable way to understand it is that the fundamental model that the Rust compiler and standard library attempt to implement is safe and correct by construction, rather than as a statement about the concrete implementation. Among software developers, we shouldn't need to provide a "assuming there are no bugs" caveat with every statement about how something works, because we know that bugs are essentially unavoidable at scale. We can say that Rust makes it much easier to avoid bugs, but nobody serious has ever claimed that they are 100% eliminated. It's not like C or C++ compilers are bug free either, despite implementing much weaker models. |
| |
| ▲ | pseudony an hour ago | parent | prev [-] | | Funny. I would think a GCed language was safer - maybe Haskell being the safest of all (outside even more niche languages) for its ability to encode so much in the type system. Yet, for some curious reason, a substantial group of snake oil salesmen insist that only this one particularly cut puzzle piece (rust) fits the bill. | | |
| ▲ | simonask 38 minutes ago | parent | next [-] | | Who, exactly? It has been known for decades that GC was one way to achieve memory safety. That was one of the main selling point of Java back in the 90s - no more segfaults. Rust was an absolute breakthrough, because it provides a second way to gain memory safety, this time without the significant drawbacks of a garbage collector, bringing memory safety to systems programming. That's materially different. The only other previous attempt at this was Ada, which for a variety of reasons never quite fit the bill despite its obvious strengths. | | |
| ▲ | dcminter 9 minutes ago | parent [-] | | > Who, exactly? I'm on your side of this, but there are Rust zealots out there and they're incredibly annoying. (Edit to add:) As a longtime Java guy (after a decade or so as a C++ guy) what drew me to Rust wasn't actually memory safety; that was table-stakes for me and Go might have lured me in if that was all I wanted. The low-cost (cognitive complexity is a cost) of memory safety was a factor, but it was the expressive type system that sealed the deal. |
| |
| ▲ | JuniperMesos 23 minutes ago | parent | prev [-] | | Also you gotta write the garbage collector itself in some language that isn't itself garbage collected - usually C or C++, before Rust existed. Anyway, it's basically slander to say that people advocating Rust are snake oil salesman. Rust is a genuine improvement to systems languages over the existing options (primarily C/C++) in multiple respects, not just memory safety; it is not a panacea that solves every problem or eliminates every possible bug, and no one involved with the project ever claimed that it did. |
|
| |
| ▲ | dcminter 11 minutes ago | parent | prev | next [-] | | Yes, that's a myth. I, like many others, still think that a language that requires a bug in the compiler for a certain category of error to be exhibited is preferable to one that has no such guarantees. I like C but it's not crazy to consider rewriting a tool in Rust for memory safety reasons. | |
| ▲ | no-name-here 20 minutes ago | parent | prev | next [-] | | > automatically safe > bug free In most discussions involving Rust I hear those “counter” criticisms, but is it a common claim by Rust proponents that Rust makes software bug free, etc? I feel like I don’t really encounter the original claim that others are arguing against. | |
| ▲ | Certhas an hour ago | parent | prev [-] | | Because? |
|
|
|
|
|
| ▲ | hingler36 3 hours ago | parent | prev | next [-] |
| I've always felt that parts of the Rust standard library to be weirdly un-rusty. Like indexing a vector panicking instead of returning a Some(T) in case the index is out of bounds. It's easy to avoid if you put the effort in of course, but it did always feel weird that one of the first data structures you learn has this behavior right out of the gate. |
| |
| ▲ | namjh 6 minutes ago | parent | next [-] | | That's just a design decision orthogonal to memory safety: For "fallible" variant indexing, you have `the_slice.get(index)`: https://doc.rust-lang.org/stable/std/primitive.slice.html#me... Although its method signature is kind of cursed due to generics, it matches your requirement that allows graceful OOB handling. If the default indexing behavior were like `.get()` it would be too cumbersome to express repeated use of indexing, or multi-dimensional array indexing, etc. Also there is get_unchecked which is unsafe. Trivially you'll trigger OOB access when misused. | |
| ▲ | Chai-T-Rex 3 hours ago | parent | prev | next [-] | | That's not really a memory safety bug, due to the fact that it panics rather than doing something unsafe. The `variable[i]` notation is intended to function like in other languages, where it returns an unwrapped value. It would be ugly, noisy, unergonomic, and annoying to have to properly handle `Some` (like in `variable[i].unwrap()`) every single time you ever used it. Imagine doing `c[i].unwrap()[j].unwrap() += a[i].unwrap()[k].unwrap() * b[k].unwrap()[j].unwrap();`. If you really want the result to be wrapped in `Some`, you can use `variable.get(i)`. | | |
| ▲ | cube00 2 hours ago | parent | next [-] | | > That's not really a memory safety bug, due to the fact that it panics rather than doing something unsafe. The ? operator can be nicer to read then all those .unwraps() as an alternative and doesn't panic. I guess the concern with panics is you're not forced to think about how you want to handle them like you do when you get a `Result` or `Option` returned. As a result it could be used as a DDOS against your app if it's network accessible. | | |
| ▲ | aslilac 2 hours ago | parent | next [-] | | gotta remember tho that ? didn’t exist in 1.0, and now we can’t break existing code | | |
| ▲ | cowlevel 17 minutes ago | parent [-] | | Is that Rust's guarantee? I know that Go avoids breaking changes but I thought they were normal in Rust. |
| |
| ▲ | adwn 2 hours ago | parent | prev [-] | | Applying the ? operator to an Option<_> doesn't work inside a function returning io::Result<_>, for example, and turning the None from an out-of-bounds vector access into some io::Result::Err usually wouldn't make sense. I would go so far as to say that an out-of-bounds index is always a logic error (to be handled by panicking), not an input error (to be handled with Result). And if the out-of-bounds access was caused by invalid input, then logic error consists of not checking the input before indexing. | | |
| ▲ | ablob 4 minutes ago | parent [-] | | The former issue arises from post-hoc thinking. Of course it's hard to retrofit into the language, but I see no reason why there shouldn't be facilities for the ? operator to turn a None into an appropriate error type. The latter is a subjective matter. There are reasons to never want a panic (hello there, Linux kernel) and reasons to do more sophisticated stuff (core dump, cleanup or notifications) before crashing. And maybe one thread shouldn't lead to process death. I think most of these decisions should be kept to the user's discretion. |
|
| |
| ▲ | rendaw 2 hours ago | parent | prev [-] | | GP didn't say it was a memory safety bug. |
| |
| ▲ | suresk 3 hours ago | parent | prev | next [-] | | I think it is a reasonable tradeoff, especially since `get(i)` is available to do what you want. An array index oob error is usually a logic issue, not some recoverable error, and in a lot of cases you're doing it where it is impossible to be wrong, so it is a lot of error-checking (and giving up things like x[0] += 1) to have the Index trait return an Option instead of a direct reference. | |
| ▲ | cowlevel 18 minutes ago | parent | prev | next [-] | | Wrapping everything in optional does lead to optional fatigue, and people just unwrap each optional they get since they know the index is inbounds, missing the one instance where it really is possible to get None. Maybe optional fatigue is Rusty but it's not a good style in any language | |
| ▲ | saagarjha 2 hours ago | parent | prev [-] | | > Like indexing a vector panicking instead of returning a Some(T) in case the index is out of bounds. If you are doing this then you've made a mistake in your code. Rust is consistent in catching these early. | | |
| ▲ | uecker 2 hours ago | parent [-] | | There is also the idea that the language is so good because such things are caught at compile time via the type system. (But this is generally not done for the same reason it is not done in C.) | | |
| ▲ | pjmlp an hour ago | parent [-] | | As shown in PL/I or NEWP, C could have done it both ways, checked and unchecked when really required, e.g. PREFIX(SUBSCRIPTRANGE), PREFIX(NOSUBSCRIPTRANGE). Likewise regarding the storage for the length, if one is willing to go the way C went down a few years later, declare a pointer and make the array storage be pointer based, e.g. BASED(MY_ARRAY_PTR). | | |
| ▲ | uecker an hour ago | parent [-] | | Can you explain what you mean by "make the array storage be pointer based"? |
|
|
|
|
|
| ▲ | Panzerschrek 2 hours ago | parent | prev | next [-] |
| > But many operations in Rust can panic, from out-of-bounds indexing to calls into user-provided code, and by default a panic unwinds That's why I think having unwinding in Rust is a mistake. It gives a false sense of safety, but it's very hard to write unwinding-correct code. So, it would be better to handle panics as immediate process termination instead. |
| |
| ▲ | Xirdus an hour ago | parent [-] | | But that would make Rust unsuitable for writing anything that wants to keep the program running even if something does panic - say, test harness, or a plugin system. | | |
| ▲ | masklinn an hour ago | parent [-] | | OTOH that panics are ok and easy to reach for and unwind is why you have to handle them and keep the program running. I don’t think that an unwind-less rust would have fit within the weirdness budget 10 years ago, today it would definitely be something to consider. Although it would have an unwind effect to contend with. | | |
| ▲ | Xirdus 26 minutes ago | parent [-] | | It would be entirely unsuitable for the kind of project I work on in my day job, which requires running user-provided code (trusted code, but one that may contain many bugs) in 100 different worker threads, and if one of them crashes the other 99 must keep running. Without unwinding and recovery, we'd have to completely redesign the whole application, probably to multiprocess architecture with all its downsides. |
|
|
|
|
| ▲ | eptcyka 41 minutes ago | parent | prev | next [-] |
| An effect system could gelp us reason about code by making calls to panic-capable functions marked as such. Maybe we could mark some types as panic unsafe? |
| |
| ▲ | cowlevel 15 minutes ago | parent [-] | | That's literally what returning optional does. Effect systems usually fail because effects are very different from each other and can't be generalised. |
|
|
| ▲ | nektro 3 hours ago | parent | prev | next [-] |
| > Pattern 2: Trusting user-provided implementations to be correct reminds me of the tales from https://oxide.computer/blog/iddqd-unsafe |
|
| ▲ | nektro 4 hours ago | parent | prev | next [-] |
| > Notes from Microsoft Security's Frontier Offensive Research & Generative Exploitation Lab. |
|
| ▲ | shevy-java 12 minutes ago | parent | prev | next [-] |
| The best AI language has not used AI to find all bugs yet? I don't quite like AI, but I think from an objective point of
view one can reason that AI led to the discovery of more bugs,
if we deduct the non-bugs from those reports. |
|
| ▲ | senderista 3 hours ago | parent | prev | next [-] |
| I sure wish they'd rewrite the Azure Linux VM Agent in Rust. That thing normally burns 10-15% of a core, and also blocks VM restart for tens of minutes, god knows why. |
| |
| ▲ | loeg 3 hours ago | parent | next [-] | | I've seen certain MSFT engineers get a lock ordering warning from taking mutexes in a wrong order and "fix" it by using a spin loop + try lock instead. | | |
| ▲ | senderista 2 hours ago | parent [-] | | I work on a commercial database marketed as "lock-free" and I can't tell you how many CAS spin loops I've seen with no backoff or iteration bounds. | | |
| ▲ | masklinn an hour ago | parent [-] | | Does the system provide ready made backoff and / or bounded CAS loop primitives? Because if you have to write the entire thing in full every time I can see why it’s get skipped. |
|
| |
| ▲ | arccy an hour ago | parent | prev [-] | | no amount of rust will fix the problems in azure that lead to https://news.ycombinator.com/item?id=47616242 |
|
|
| ▲ | gspr 43 minutes ago | parent | prev | next [-] |
| Very interesting finds for sure. But taking the "trusting user-provided code" as an example: isn't this just a case of malicious code being malicious? I understand the point that the malicious act comes through memory unsafety, and it's a big gotcha that safe Rust (i.e. Rust without `unsafe`) can trigger memory unsafe behavior. But the code doing it is still acting maliciously (by having an `AsRef` implementation that returns different slices at different times). And the code being abused (the one that does the unsafe indexing) does have `unsafe` – so this is a case of the user writing unsafe Rust that interacts with malicious safe Rust. I don't know if it's ever been in Rust's threat model to handle malicious code. |
|
| ▲ | petterroea 20 minutes ago | parent | prev | next [-] |
| Is it just me or is it painful that they format their product name like that in the blog text. |
|
| ▲ | not_a_bot_4sho 3 hours ago | parent | prev [-] |
| A good use for AI |
| |
| ▲ | rurban 3 hours ago | parent [-] | | No, a good use to fix their specs. Rust is unsafe by spec, not just by implementation. For memory and type safety. concurrency safety is impossible to get with their blocking IO approach. |
|