Readit News logoReadit News
tekacs · 2 months ago
To pre-empt the folks who'll come in here and laugh about how Rust should be preventing memory corruption... I'll just directly quote from the mailing list:

  Rust Binder contains the following unsafe operation:
  
   // SAFETY: A `NodeDeath` is never inserted into the death list
   // of any node other than its owner, so it is either in this
   // death list or in no death list.
   unsafe { node_inner.death_list.remove(self) };
  
  This operation is unsafe because when touching the prev/next pointers of
  a list element, we have to ensure that no other thread is also touching
  them in parallel. If the node is present in the list that `remove` is
  called on, then that is fine because we have exclusive access to that
  list. If the node is not in any list, then it's also ok. But if it's
  present in a different list that may be accessed in parallel, then that
  may be a data race on the prev/next pointers.
  
  And unfortunately that is exactly what is happening here. In
  Node::release, we:
  
   1. Take the lock.
   2. Move all items to a local list on the stack.
   3. Drop the lock.
   4. Iterate the local list on the stack.
  
  Combined with threads using the unsafe remove method on the original
  list, this leads to memory corruption of the prev/next pointers. This
  leads to crashes like this one:

themafia · 2 months ago
So the prediction that incautious and unverified unsafe {} blocks would cause CVEs seems entirely accurate.
mustache_kimono · 2 months ago
> So the prediction that incautious and unverified unsafe {} blocks would cause CVEs seems entirely accurate.

This is one/the first CVE caused by a mistake made using unsafe Rust. But it was revealed along with 159 new kernel CVEs found in C code.[0]

It may just be me, but it seems wildly myopic to draw conclusions about Rust, or even, unsafe Rust from one CVE. More CVEs will absolutely happen. But even true Rust haters have to recognize that tide of CVEs in kernel C code runs something like 19+ CVEs per day? What kind of case can you make that "incautious and unverified unsafe {} blocks" is worse than that?

[0]: https://social.kernel.org/notice/B1JLrtkxEBazCPQHDM

K0nserv · 2 months ago
Isn’t it obvious that the primary source of CVEs in Rust programs would be the portions of the program where the human is charge of correctness instead of the compiler?

The relevant question is whether it results in fewer and less severe CVEs than code written in C. So far the answer seems to be a resounding yes

woodruffw · 2 months ago
"Cause" seems unsubstantiated: I think to justify "cause," we'd need strong evidence that the equivalent bug (or worse) wouldn't have happened in C.

Or another way to put it: clearly this is bad, and unsafe blocks deserve significant scrutiny. But it's unclear how this would have been made better by the code being entirely unsafe, rather than a particular source of unsafety being incorrect.

n2d4 · 2 months ago
Sure, but that's not really that interesting or controversial.

The more useful question is, how many CVEs were prevented because unsafe {} blocks receive more caution and scrutiny?

crote · 2 months ago
Obviously. If you use a language which inherently makes memory safety bugs in regular code impossible, all memory safety bugs will be contained to the "trust me, I know what I'm doing - no need to check this" bypass sections. Similarly, all drownings happen in the presence of water.

The important thing to remember is that in this context C code is one giant unsafe {} block, and you're more likely to drown in the sea than in a puddle.

Phelinofist · 2 months ago
I know nothing about Rust. But why is unsafe needed? Kinda sounds a lock would make this safe?
aw1621107 · 2 months ago
> I know nothing about Rust. But why is unsafe needed?

The short of it is that for fundamental computer science reasons the ability to always reject unsafe programs comes at the cost of sometimes being unable to verify that an actually-safe program is safe. You can deal with this either by accepting this tradeoff as it is and accepting that some actually-safe programs will be impossible to write, or you can add an escape hatch that the compiler is unable to check but allows you to write those unverifiable programs. Rust chose the latter approach.

> Kinda sounds a lock would make this safe?

There was a lock, but it looks like it didn't cover everything it needed to.

torginus · 2 months ago
Sorry, but this is like saying 'when I am not wrong, I am right 100% of the time'.

The devs didn't write unsafe Rust to experience the thrills of living dangerously, they wrote it because the primitives were impossible to express in safe Rust.

If I were to write a program in C++ that has a thread-safe doubly linked list in it, I'd be able to bet on that linked list will have safety bugs, not because C++ is an unsafe language, but because multi-threading is hard. In fact, I believe most memory safety errors today occur in the presence of multi-threading.

Rust doesn't offer me any way of making sure my code is safe in this case, I have to do the due diligence of trying my best and still accept that bugs might happen because this is a hard problem.

The difference between Rust and C++ in this case, is that the bad parts of Rust are cordoned off with glowing red lines, while the bad parts of C++ are not.

This might help me in minimizing the attack surface in the future, but I suspect Rust's practical benefits will end up less impactful than advertised, even when the language is full realized and at its best, because most memory safety issues occur in code that cannot be expressed in safe Rust and doing it in a safe Rust way is not feasible for some technical reason.

samdoesnothing · 2 months ago
If rust is so inflexible that it requires the use of unsafe to solve problems, that's still rust's fault. You have to consider both safe rust behaviour as well as necessary unsafe code.
woodruffw · 2 months ago
This is sort of the exact opposite of reality: the point of safe Rust is that it's safe so long as Rust's invariants are preserved, which all other safe Rust preserves by construction. So you only need to audit unsafe Rust code to ensure the safety of a Rust codebase.

(The nuance being that sometimes there's a lot of unsafe Rust, because some domains - like kernel programming - necessitate it. But this is still a better state of affairs than having no code be correct by construction, which is the reality with C.)

thayne · 2 months ago
That's like saying that if c is so inflexible it requires the use of inline assembly to solve problems, it's C's fault if inline assembly causes undefined behavior.
bigstrat2003 · 2 months ago
> If rust is so inflexible that it requires the use of unsafe to solve problems...

Thankfully, it doesn't. There are very few situations which require unsafe code, though a kernel is going to run into a lot of those by virtue of what it does. But the vast majority of the time, you can write Rust programs without ever once reaching for unsafe.

kstrauser · 2 months ago
What's the alternative that preserves safe-by-default while still allowing unlimited flexibility to accidentally break things? I mean, Rust allows inline assembly because there are situations where you absolutely must execute specific opcodes, but darned if I want that to be the common case.
lynndotpy · 2 months ago
Yes. When writing unsafe, you have to assume you can never trust anything coming from safe rust. But you are also provided far fewer rakes to step on when writing unsafe, and you (ideally) are writing far fewer lines of unsafe code in a Rust project than you would for equivalent C.

Rust is written in Rust, and we still want to be able to e.g. call C code from Rust. (It used to be the case that external C code was not always marked unsafe, but this was fixed recently).

quotemstr · 2 months ago
samdoesnothing is making a legitimate point about needing to consider prevalence of unsafe inna Rust program. That he's being downvoted to hell is everything wrong with HN.
n2d4 · 2 months ago
I recommend you read Greg Koah-Hartman's thread instead of this article: https://social.kernel.org/notice/B1JLrtkxEBazCPQHDM

    > Rust is is not a "silver bullet" that can solve all security problems, but it sure helps out a lot and will cut out huge swatches of Linux kernel vulnerabilities as it gets used more widely in our codebase.
    
    > That being said, we just assigned our first CVE for some Rust code in the kernel: https://lore.kernel.org/all/2025121614-CVE-2025-68260-558d@gregkh/ where the offending issue just causes a crash, not the ability to take advantage of the memory corruption, a much better thing overall.

    > Note the other 159 kernel CVEs issued today for fixes in the C portion of the codebase, so as always, everyone should be upgrading to newer kernels to remain secure overall.

Dead Comment

jackrabbit1997 · 2 months ago
> > That being said, we just assigned our first CVE for some Rust code in the kernel: https://lore.kernel.org/all/2025121614-CVE-2025-68260-558d@g... where the offending issue just causes a crash, not the ability to take advantage of the memory corruption, a much better thing overall.

That indicates that Greg Koah-Hartman has a very poor understanding of Rust and the _unsafe_ keyword. The bug can, in fact, exhibit undefined behavior and memory corruption.

His lack of understanding is unfortunate, to put it very mildly.

n2d4 · 2 months ago
What are some compiler flags that would compile the code such that an attacker could take advantage? And what would the attack be?

Or is this just a theoretical argument, "it is hypothetically possible to create a technically-spec-compliant Rust compiler that would compile this into dangerous machine code"? If so it should still be fixed of course, but if I'm patching my Linux kernel I'd rather know what the practical impact is.

drob518 · 2 months ago
Anybody who thought the simple action of rewriting things in Rust would eliminate all bugs was hopelessly naive. Particularly since Rust allows unsafe operations. That doesn’t mean Rust provides no value over C, just that the value is short of total elimination of bugs. Which was never advertised as the value to begin with.
tensor · 2 months ago
What? I think people think "rust without unsafe" eliminates certain classes of bugs. Are we really going to imply that people don't understand that "unsafe" labeled code is ... uh.. possibly unsafe? I don't believe that these mythical "naive" people exist who think code explicitly labelled unsafe is still safe.
hnlmorg · 2 months ago
I think the problem lies with the fact that you cannot write kernel code without relying on unsafe blocks of code.

So arguably both camps are correct. Those who advocate Rust rewrites, and those who are against it too.

stouset · 2 months ago
These mythical people are here in the comment section with us. All of them appear to be Rust detractors that are incapable of grasping that one line of unsafe code amongst nine lines of provably-safe code is somehow no better than ten lines of unsafe C.
bangaladore · 2 months ago
I think part of the problem is people start thinking that unsafe code with a SAFETY comment nearby is probably safe.

Then the safety comment can easily bias the reader into believing that the author has fully understood the problem and all edge cases.

Ferret7446 · 2 months ago
You know one of the best ways to introduce a ton of bugs? Rewriting existing battletested code. Cloudflare is another recent learner of this lesson with their rust rewrite.
zamalek · 2 months ago
> Anybody who thought the simple action of rewriting things in Rust would eliminate all bugs was hopelessly naive

All bugs is typically a strawman typically only used by detractors. The correct claim is: safe Rust eliminates certain classes of bugs. I'd wager the design of std eliminates more (e.g. the different string types), but that doesn't really apply to the kernel.

samdoesnothing · 2 months ago
> All bugs is typically a strawman typically only used by detractors. The correct claim is: safe Rust eliminates certain classes of bugs. I'd wager the design of std eliminates more (e.g. the different string types), but that doesn't really apply to the kernel.

Which is either 1) not true as evidenced by this bug or 2) a tautology whereby Rust eliminates all bugs that it eliminates.

lelanthran · 2 months ago
> All bugs is typically a strawman typically only used by detractors.

That's obviously not true; we've had years and years of "if it compiles it works" derailing C and C++ forums all over the internet.

themafia · 2 months ago
> That doesn’t mean Rust provides no value over C

The real question is "does it provide this greater value for _less_ effort?"

The answer seems to be: "No."

drob518 · 2 months ago
No, the real question is whether it provides that greater value for a reasonably acceptable, commensurate effort. Looking for greater value with less effort is looking for a free lunch, and we all know TANSTAAFL.
timeon · 2 months ago
> The answer seems to be: "No."

It is actually "Yes."

samdoesnothing · 2 months ago
> Anybody who thought the simple action of rewriting things in Rust would eliminate all bugs was hopelessly naive.

Classic Motte and Bailey. Rust is often said "if it compiles it runs". When that is obviously not the case, Rust evangelicals claim nobody actually means that and that Rust just eliminates memory bugs. And when that isn't even true, they try to mischaracterize it as "all bugs" when, no, people are expecting it to eliminate all memory bugs because that's what Rust people claim.

anon-3988 · 2 months ago
> Classic Motte and Bailey. Rust is often said "if it compiles it runs".

That claims is overly broad, but its a huge, huge part of it. There's no amount of computer science or verification that can prevent a human from writing the wrong software or specification (let plus_a_b = a - b or why did you give me an orange when I wanted an apple). Unsafe Rust is so markedly different than safe default Rust. This is akin to claiming that C is buggy or broken because people write broken inline ASM. If C can't deal with broken inline ASM, then why bother with C?

AnIrishDuck · 2 months ago
> Classic Motte and Bailey.

For this to be a "classic motte and bailey" you will need to point us to instances where _the original poster_ suggested these (the "bailey", which you characterize as "rust eliminates all bugs") things.

It instead appears that you are attributing _other comments_ to the OP. This is not a fair argumentation technique, and could easily be turned against you to make any of your comments into a "classic motte and bailey".

Joel_LeBlanc · a month ago
It's great to see discussions around the intricacies of digital assets, especially when it comes to security like the CVE vulnerabilities in the Linux kernel. I've been in the digital asset space for a while, and I always emphasize the importance of thorough due diligence—it's a bit of a balancing act knowing that bugs can arise. For evaluating apps specifically, I’ve found the DREA (Digital Real Estate Analyzer) tool really helpful for assessing potential risks and opportunities. It’s all about being prepared while recognizing that the digital landscape can be unpredictable.
phendrenad2 · 2 months ago
I feel like everyone involved in the Linux Kernel Rust world is ironically woefully unaware of how Rust actually works, and what it's actually capable of. I suspect that Rust gurus agree with me, but don't want to say anything because it would hurt Rust adoption in places where it actually is helpful (encryption algorithms...)

Kernels - and especially the Linux kernel - are high-performance systems that require lots of shared mutable state. Every driver is a glorified while loop waiting for an IRQ so it can copy a chunk of data from one shared mutable buffer to another shared mutable buffer. So there will need to be some level of unsafe in the code.

There's a fallacy that if 95% of the code is safe, and 5% is unsafe, then that code is only 5% as likely to contain memory errors as a comparable C program. But, to reiterate what another commenter said, and something I've predicted for a long time, the tendency for the "unsafe block" to become instrumented by the "safe block" will always exist. People will loosen the API contract between the "safe" and "unsafe" sides until an error in the "safe" side kicks off an error in the "unsafe" side.

bronson · 2 months ago
> Every driver is a glorified while loop waiting for an IRQ

This is so obviously false that I suspect there's the reason you don't see any Rust gurus agreeing with you.

Drivers do lots of resource and memory management, far more than just spinning on IRQs.

infamouscow · 2 months ago
I should probably ask what experience do you have writing hardware drivers for the Linux kernel, but it's pretty obvious the answer is: none. I actually burst out laughing reading your comment, it's ridiculous.

My anecdotal experience interviewing big tech engineers that used Rust reflects GP's hunch about this astonishing experience gap. Just this year, 4/4 candidates I interviewed couldn't give me the correct answer for what two bytes in base 2 represented in base 10. Not a single candidate asked me about the endianness of the system.

Now that Rust in the kernel doesn't have an "experimental" escape hatch, these motte-and-bailey arguments aren't going to work. Ultimately, I think this is a good thing for Rust in the kernel. Once all of the idiots and buffoons have been sufficiently derided and ousted from public discourse (deservedly so), we can finally begin having serious and productive technical discussions about how to make C and Rust interoperate in the kernel.

phendrenad2 · 2 months ago
I'll accept praising with faint disagreement any day.
bangaladore · 2 months ago
Correct me if I'm wrong, but this comment is atleast partially incorrect right?

> Since it was in an unsafe block, the error for sure was way easier to find within the codebase than in C. Everything that's not unsafe can be ruled out as a reason for race conditions and the usual memory handling mistakes - that's already a huge win.

The benefit of Rust is you can isolate the possible code that causes an XYZ to an unsafe block*. But that doesn't necessarily mean the error shown is directly related to the unsafe block. Like C++, triggering undefined behavior can in theory cause the program to do anything, including fail spectacularly within seemingly unrelated safe code.

* Excluding cases where safe things are actually possibly unsafe (like some incorrectly marked FFI)

malcolmgreaves · 2 months ago
The point at which you _could_ start to have undefined behavior is within an `unsafe` block or function. So even if the "failure" occurred in some "safe" part of the code, the conditions to make that failure would start in the unsafe code.

When debugging, we care about where the assumptions we had were violated. Not where we observe a bad effect of these violated assumptions.

I think you get here yourself when you say:

> triggering undefined behavior can in theory cause the program to do anything, including fail spectacularly within seemingly unrelated safe code

The bug isn't where it failed spectacularly. It's where the C++ code triggered undefined behavior.

Put another way: if the undefined behavior _didn't_ cause a crash / corrupted data, the bug _still_ exists. We just haven't observed any bad effects from it.

landr0id · 2 months ago
From my experience UB in Rust can manifest a bit differently than in C or C++, but still generally has enough smoke in the right area.

I believe their point was that they only needed to audit only the unsafe blocks to find the actual root cause of the bug once they had an idea of the problematic area.

tialaramex · 2 months ago
I guess the problem here is that you and the writer have different understandings of what "the error" means.

The author is thinking about "the error" as some source code that's incorrect. "Your error was not bringing gloves and a hat to the snowball fight" but you're thinking "the error" is some diagnostic result that shows there was a problem. "My error is that I'm covered in freezing snow".

Does that help?

lousken · 2 months ago
Why do they allow unsafe parts in linux kernel in the first place? Why rewriting C code into unsafe rust?
K0nserv · 2 months ago
It's important to note that the `unsafe` keyword is poorly named. What it does is unlock a few more capabilities at the cost of upholding the invariants the spec requires. It should really be called "assured" or something. The programmer is taking the wheel from the compiler and promising to drive safely.

As for why there is unsafe in the kernel? There are things, especially in a kernel, that cannot be expressed in safe Rust.

Still, having smaller sections of unsafe is a boon because you isolate these locations of elevated power, meaning they are auditable and obvious. Rust also excels at wrapping unsafe in safe abstractions that are impossible to misuse. A common comparison point is that in C your entire program is effectively unsafe, whereas in Rust it's a subset.

speed_spread · 2 months ago
You need unsafe Rust for FFI - interfacing with the rest of the kernel which is still C, uses raw pointers, has no generics, doesn't track ownership, etc. One day there might enough Rust in the kernel to have pure-Rust subsystems APIs which would no longer require unsafe blocks to use. This would reverse the requirements as C would be a second class citizen with these APIs (not that C would notice or care). How far Rust is to get pushed remains to be seen but it might a long time to get there.
tialaramex · 2 months ago
Rust is very nice for encapsulation. C isn't great at that work, and of course it can't express the idea that whatever we've encapsulated is now safe to use this way, in C everything looks equally safe/ unsafe.

Dead Comment

Dead Comment

uecker · 2 months ago
Somebody (tsoding?) called "unsafe" in Rust a "blame shifting device". If a bug was in an "unsafe" block, it is not Rust's fault, but solely the responsibility of the programmer, while every bug in a C program is obviously the language's fault alone.
K0nserv · 2 months ago
Everything is solely the responsibility of the programmer. The strength of Rust as a language is that it helps the programmer check themselves before they wreck themselves. The critique of C would be that it provides far too little support to the programmer, although it was reasonable at the time it was was invented.

Unsafe is the one escape hatch where Rust is more like C, but pragmatically it's an important escape hatch.

uecker · 2 months ago
Yes, but the arguments why we need to replace C code with Rust was not that it is better by "helping the programmer check themselves" but that we need to switch to memory-safe languages because it "removes a whole class of error". (of course, nobody has ever said this, this must be my imagination)

Finally, there are also a lot of ways to improve memory safety in C which are not nowhere exhausted even in the kernel. As long as this is not even the case, I find the argument that there is "too little support for the programmer" quite hollow.

aw1621107 · 2 months ago
Direct link to the mailing list entry at [0]. The fix for 6.19-rc1 is commit 3e0ae02ba831 [1]. The patch is pretty small (added some extra context since the function it's from is short):

        pub(crate) fn release(&self) {
            let mut guard = self.owner.inner.lock();
            while let Some(work) = self.inner.access_mut(&mut guard).oneway_todo.pop_front() {
                drop(guard);
                work.into_arc().cancel();
                guard = self.owner.inner.lock();
            }

    -       let death_list = core::mem::take(&mut self.inner.access_mut(&mut guard).death_list);
    -       drop(guard);
    -       for death in death_list {
    +       while let Some(death) = self.inner.access_mut(&mut guard).death_list.pop_front() {
    +           drop(guard);
                death.into_arc().set_dead();
    +           guard = self.owner.inner.lock();
            }
        }
And here is the unsafe block mentioned in the commit message with some more context [3]:

    fn set_cleared(self: &DArc<Self>, abort: bool) -> bool {
        // <snip>

        // Remove death notification from node.
        if needs_removal {
            let mut owner_inner = self.node.owner.inner.lock();
            let node_inner = self.node.inner.access_mut(&mut owner_inner);
            // SAFETY: A `NodeDeath` is never inserted into the death list of any node other than
            // its owner, so it is either in this death list or in no death list.
            unsafe { node_inner.death_list.remove(self) };
        }
        needs_queueing
    }
[0]: https://lore.kernel.org/linux-cve-announce/2025121614-CVE-20...

[1]: https://github.com/torvalds/linux/commit/3e0ae02ba831da2b707...

[2]: https://github.com/torvalds/linux/blob/3e0ae02ba831da2b70790...

[3]: https://github.com/torvalds/linux/blob/3e0ae02ba831da2b70790...

anon-3988 · 2 months ago
The interesting part to me is that this bug does not necessarily happen in an unsafe block. The fix happens in an unsafe block, I think the API should change to avoid this. Perhaps by forcing users to pass a lambda to do stuff instead of having to manually lock and drop?
aw1621107 · 2 months ago
The `unsafe` block was present because `List::remove` is marked `unsafe` [0]:

    /// Removes the provided item from this list and returns it.
    ///
    /// This returns `None` if the item is not in the list. (Note that by the safety requirements,
    /// this means that the item is not in any list.)
    ///
    /// # Safety
    ///
    /// `item` must not be in a different linked list (with the same id).
    pub unsafe fn remove(&mut self, item: &T) -> Option<ListArc<T, ID>> {
I think it'd be tricky at best to make this particular API safe since doing so requires reasoning across arbitrary other List instances. At the very least I don't think locks would help here, since temporary exclusive access to a list won't stop you from adding the same element to multiple lists.

[0]: https://github.com/torvalds/linux/blob/3e0ae02ba831da2b70790...

mlindner · 2 months ago
Yeah this is a bad fix. It should be impossible to cause incorrect things to happen from safe code, especially from safe code calling safe code.