← Back to context

Comment by skullone

16 hours ago

except nearly everyone uses unsafe rust

No they really don't. 95% of rust is safe rust[1].

Also unsafe rust doesn't remove bounds checks. arr[idx] is bounds checked in every context.

You can opt out of array bounds checking by writing unsafe { arr.get_unchecked(idx) } . But thats incredibly rare in practice.

[1] https://cs.stanford.edu/~aozdemir/blog/unsafe-rust-syntax/

  • > 95% of rust is safe rust.

    Based on the raw number of assorted crates, which has no bearing on kernel code. The more relevant question is, can a performant, cross-architecture, kernel ring-buffer be written in safe Rust?

    • Hubris, an embedded RTOS-like used in production by Oxide, has ~4% unsafe code in the kernel last I checked. There’s a ring buffer implementation that has one unsafe, for unchecked indexing: https://github.com/oxidecomputer/hubris/blob/master/lib/ring... (this of course does not mean that it is the one ring buffer to rule them all, but it’s to demonstrate that yes, it is at least possible to have one with minimum unsafe.)

      It’s always a way lower number than folks assume. Even in spaces that have higher than average usage.

      1 reply →

    • I doubt it, but you can probably get pretty close.

      This is something a lot of people misunderstand about unsafe rust. The safe / unsafe distinction isn't at the crate level. You don't say "this entire module opts out of safety checks". Unsafe is a granular thing. The unsafe keyword doesn't turn off the borrow checker. It just lets you dereference pointers (and do a few other tricks).

      Systems code written in rust often has a few unsafe functions which interact with the actual hardware. But all the high level logic - which is usually most of the code by volume - can be written using safe, higher level abstractions.

      "Can all of io_uring be written in safe rust?" - probably not, no. But could you write the vast majority of io_uring in safe rust? Almost certainly. This bug is a great example. In this case, the problematic function was this one:

          static void io_zcrx_return_niov_freelist(struct net_iov *niov)
          {
              struct io_zcrx_area *area = io_zcrx_iov_to_area(niov);
      
              spin_lock_bh(&area->freelist_lock);
              area->freelist[area->free_count++] = net_iov_idx(niov);
              spin_unlock_bh(&area->freelist_lock);
          }
      

      At a glance, this function absolutely could have been written in safe rust. And even if it was unsafe, array lookups in rust are still bounds checked.

"unsafe Rust" is not a binary; you don't opt into it for every single line of code. Given that the entire premise behind the idea that using C instead of Rust is fine is that people should be able to pay close attention and not make mistakes like this, having the number of places you need to look be a tiny fraction of the overall code that's explicitly marked as unsafe is a massive difference from C where literally every line of the code could be hiding stuff like this.

> except nearly everyone uses unsafe rust

Really? Why? I've not used Rust outside of some fairly small efforts, but I've never found a reason to reach for unsafe. So why is "nearly everyone" else using it?

  • Let's say you want to call win32 (or Mac) OS functions, all of a sudden you're doing all kinds of wonky pointer stuff because that's how these operating systems have been architected. Doing unsafe stuff is pretty inevitable if you want to do anything non-hello-world-ish.

    • > Doing unsafe stuff is pretty inevitable if you want to do anything non-hello-world-ish.

      So the vast majority of Rust projects involve writing at least one unsafe block? Is that really your claim?

      6 replies →

    • A tiny fraction of programs need to use win32 or Mac OS functions beyond the standard library or other safe wrappers for said functions.

      1 reply →

    • So what? Just because you used the keyword `unsafe` to call an unsafe API does not mean that you are going to use unsafe pointer access to write to a vector.