|
|
Subscribe / Log in / New account

Proper Community Engagement

Proper Community Engagement

Posted Aug 31, 2024 2:25 UTC (Sat) by roc (subscriber, #30627)
In reply to: Proper Community Engagement by rc00
Parent article: Rust-for-Linux developer Wedson Almeida Filho drops out

> Why write Unsafe Rust (which also means opting out of the borrow checker)

That's not the right way to think about it. "unsafe" lets you deference raw pointers, which means you can bypass the borrow checker by using raw pointers instead of references, but you can use references in unsafe Rust and they still get borrow-checked. This matters because it means that entering an unsafe block because e.g. you want to call a C function does NOT mean that you are suddenly also susceptible to lifetime bugs.

> Unsafe Rust is inevitable in the Linux kernel

It is inevitable that unsafe Rust is present in the kernel, but it matters a great deal *how much* is present and where it is present. If you have unsafe Rust in the Rust wrappers around kernel APIs but you can write a lot of drivers with zero unsafe Rust (which is actually the goal), then that is likely to be a big win. You write the wrappers, you invest effort in reviewing that code and testing it, and then all your driver code (which is much more code than the wrappers, in the long run if not the short run) benefits from the safe Rust guarantees.

OTOH if every driver has to use copious amounts of "unsafe" then Rust in the kernel will have failed.

> instead of something like Zig that has around 80% of the memory safety that you get from Rust's borrow checker feature set but with 0% of the downside of the loss of productivity in fighting the borrow checker?

In release builds (i.e. builds with acceptable performance) you get no use-after-free checking with Zig, and those are most of the exploited memory safety issues these days. Plus of course Rust is giving you more safety than just memory safety.

> I believe both Zig and Odin also do not have undefined behavior where Unsafe Rust and C do

UAF is for sure undefined behaviour in Zig. There is no way to define it!

> This strikes me as Zig 1.0 if the current trajectory holds true.

Nope, no UAF protection in production code with Zig.


to post comments

Proper Community Engagement

Posted Aug 31, 2024 3:10 UTC (Sat) by rc00 (guest, #164740) [Link] (8 responses)

> OTOH if every driver has to use copious amounts of "unsafe" then Rust in the kernel will have failed.

I've already placed my wager. đŸ˜‰

> In release builds (i.e. builds with acceptable performance) you get no use-after-free checking with Zig, and those are most of the exploited memory safety issues these days. Plus of course Rust is giving you more safety than just memory safety.
> UAF is for sure undefined behaviour in Zig. There is no way to define it!
> Nope, no UAF protection in production code with Zig.

Let's debug more of your programming language knowledge:

* Zig's standard library offers a general-purpose allocator that can be used to prevent double-free, use-after-free, and can also detect memory leaks.

* Zig has a build mode named ReleaseSafe that is not only fit for production code but considered the main mode for releases. With ReleaseSafe, memory and safety checks are still enabled.

Let's try to avoid spreading misinformation.

Additional links and resources:
* https://zig.news/kristoff/how-to-release-your-zig-applica...
* https://medium.com/@shyamsundarb/memory-safety-in-c-vs-ru...
* https://ziglang.org/documentation/master/#Build-Mode

Proper Community Engagement

Posted Aug 31, 2024 3:17 UTC (Sat) by intelfx (subscriber, #130118) [Link] (2 responses)

And that allocator is going to be used if (for some inexplicable reason) Linux kernel gets to contain Zig code?

> Let’s try <…>

Let’s try to get our facts and logic straight before directing condescension at others, shall we?

Proper Community Engagement

Posted Aug 31, 2024 3:37 UTC (Sat) by rc00 (guest, #164740) [Link] (1 responses)

> And that allocator is going to be used if (for some inexplicable reason) Linux kernel gets to contain Zig code?

It's a general-purpose heap allocator. For all intents and purposes, it can be thought of as the default allocator. You can certainly use others for specialized cases but during development and debugging, why not use the general-purpose one out of good habit? How is this any different from kmalloc? (These are rhetorical questions. See the last sentence of this reply.)

> Let’s try to get our facts and logic straight before directing condescension at others, shall we?

Can you approach this thread with correct information? It's quite the take that you want to apply a negative connotation to "condescension" but yet you state that toxic comments that suggest brigading are "funny."

No, I'm not going to interact with you beyond this dribble.

Proper Community Engagement

Posted Aug 31, 2024 3:41 UTC (Sat) by Cyberax (✭ supporter ✭, #52523) [Link]

> It's a general-purpose heap allocator. For all intents and purposes, it can be thought of as the default allocator.

JFYI, Linux has a variety of ways pointers are allocated, kmalloc is only one of them. One of the problems with Rust adoption was that Rust's standard library was not ready for custom allocators and fallible allocations.

Proper Community Engagement

Posted Aug 31, 2024 4:05 UTC (Sat) by roc (subscriber, #30627) [Link] (3 responses)

> * Zig's standard library offers a general-purpose allocator that can be used to prevent double-free, use-after-free, and can also detect memory leaks.

Which leaks virtual memory (to catch heap UAF) and is not suitable for production use.

> Zig has a build mode named ReleaseSafe that is not only fit for production code but considered the main mode for releases. With ReleaseSafe, memory and safety checks are still enabled.

ReleaseSafe does not affect the choice of allocator and therefore does not detect heap UAF unless you opt into the general-purpose allocator, which is not suitable for production use.

And FWIW Zig has *nothing at all* to detect stack UAF.

Proper Community Engagement

Posted Aug 31, 2024 5:05 UTC (Sat) by rc00 (guest, #164740) [Link] (2 responses)

Your knowledge is either dated or you insist on spreading misinformation. I have provided numerous links and references. Please make use of them.

> Which leaks virtual memory (to catch heap UAF) and is not suitable for production use.

The general-purpose allocator is configured with never_unmap set to false by default. Only when set to true is there a possibility that every allocation can be leaked. Again, this allocator can be used for production release modes.

https://www.openmymind.net/learning_zig/heap_memory/

> ReleaseSafe does not affect the choice of allocator and therefore does not detect heap UAF unless you opt into the general-purpose allocator, which is not suitable for production use.

Please read the previous links. This was directly addressed.

> And FWIW Zig has *nothing at all* to detect stack UAF.

The language is still not 1.0 yet. Here is the issue where use-after-free stack allocations will be addressed:
https://github.com/ziglang/zig/issues/3180

I am sharing these links in good faith. Please take the appropriate time to process them.

Proper Community Engagement

Posted Aug 31, 2024 9:04 UTC (Sat) by roc (subscriber, #30627) [Link] (1 responses)

> I have provided numerous links and references. Please make use of them.

Yes, I checked all your links (and more) before I responded, and the information in those links is consistent with what I said. Your new link is also consistent with what I said.

> The general-zurpose allocator is configured with never_unmap set to false by default. Only when set to true is there a possibility that every allocation can be leaked. Again, this allocator can be used for production release modes.
> https://www.openmymind.net/learning_zig/heap_memory/

This doesn't support your claims. It doesn't mention never_unmap or describe exactly what GeneralPurposeAllocator guarantees or say what the performance impact is. In fact those things are not documented anywhere outside the source code, as far as I can tell; certainly nowhere you have linked to. So I find your attitude irksome.

But in the spirit of https://xkcd.com/386, I went ahead and looked at the source code: https://github.com/ziglang/zig/blob/master/lib/std/heap/g.... The situation is pretty complicated, but as far as I can tell, the following things are true in the default configuration with ReleaseSafe build mode:
-- GeneralPurposeAllocator reuses addresses of allocated objects in many cases and does allow UAF bugs in those cases
-- GeneralPurposeAllocator avoids reusing addresses of allocated objects in other cases, and therefore must suffer from serious fragmentation problems leading to performance degradation
-- GeneralPurposeAllocator lacks a lot of the optimizations essential to modern high-performance allocators and cannot be expected to be competitive with those allocators

In more detail: the source code comments say:
> //! ### `OptimizationMode.debug` and `OptimizationMode.release_safe`:
...
> //! * Do not re-use memory slots, so that memory safety is upheld. For small
> //! allocations, this is handled here; for larger ones it is handled in the
> //! backing allocator (by default `std.heap.page_allocator`).

It's not entirely clear from the comment what "memory slots" means here, but looking at the code it's more clear: https://github.com/ziglang/zig/blob/37df6ba86e3f4e0f5d6a2...
GPA passes through objects >= the page size through to the underlying page-level allocator (which defaults to td.heap.page_allocator). For smaller objects, it has a list of page-sized "buckets" per size class, with each bucket carved up into "slots" of that size class. To allocate an object, it finds a bucket for the right size class with empty space, and uses the *next slot in the bucket that has never been used*. I.e., even if some objects in the bucket have been freed, those slots won't get used. This is what inevitably leads to fragmentation and poor cache usage.

However, when freeing a sub-page-size object, if that means the bucket is *completely empty*, the entire bucket is freed: https://github.com/ziglang/zig/blob/37df6ba86e3f4e0f5d6a2...
With the default PageAllocator and GPA configuration, this memory is returned to the operating system. The next time GPA needs to allocate a page, PageAllocator will likely return memory at the same address. GPA will hand out references to that memory and so classic UAF bugs can then be triggered --- references through a pointer to the freed object will work but access memory in the freshly allocated object, perhaps of a different type.

Note that the comment "for larger ones it is handled in the backing allocator" is false for the default PageAllocator. There is no logic to avoid reusing virtual memory in that code. So virtual memory isn't leaked, but in exchange you get UAF bugs.

For allocations >= the page size, you enable UAF bugs immediately because PageAllocator can reuse addresses.

This Reddit thread agrees with me that "UAF will not be reliably detected": https://www.reddit.com/r/Zig/comments/1eysv2k/general_pur...

andrewrk did write: https://github.com/ziglang/zig/issues/3180#issuecomment-6...
> Use after free is now solved as far as safety is concerned with heap allocations, if you use the std lib page_allocator or GeneralPurposeAllocator.
And maybe you believed him, but unfortunately that's not true, or at least it's not true now.

> I am sharing these links in good faith. Please take the appropriate time to process them.

Oh, I've spent way too much time processing this.

> Here is the issue where use-after-free stack allocations will be addressed: https://github.com/ziglang/zig/issues/3180

Will it really, though? It is not easy at all to detect and prevent stack UAF bugs. For example, if you take the address of a stack value and store it in a heap object, is that allowed? It's often useful to be able to do that, so I guess Zig would want to allow it, but if you allow it, it can be very difficult to prove that the pointer is not used after the function has returned. If they do solve this, there will be some overhead and/or some existing useful Zig code that is no longer legal.

For now, safe Rust prevents UAF and all Zig has is partial, inefficient solutions and promises.

Proper Community Engagement

Posted Aug 31, 2024 13:34 UTC (Sat) by corbet (editor, #1) [Link]

This language-advocacy discussion has gone fairly far from the original topic and seems unlikely to resolve anything. This seems like a good time to let it go.

Proper Community Engagement

Posted Aug 31, 2024 5:32 UTC (Sat) by pbonzini (subscriber, #60935) [Link]

> I've already placed my wager

https://lwn.net/Articles/863459/ has two unsafe blocks, both at initialization time, and that was three years ago. It's a simple driver, sure, and some sources of unsafely are only apparent with e.g. devices that do DMA, but rest assured that the developers have done their homework.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds