How do these Rust kernel modules handle out of bounds access in an array?

In a C module it would be undefined behaviour leading to kernel panic or weird bugs, in a Rust userspace binary it would be a panic that terminates the process with an error message, what happens here? Would it be a kernel panic as well? or is there a possibility of handling it?

The Rust for Linux implementation converts a Rust panic into a Linux kernel BUG macro call. I believe this will expand to an invalid CPU instruction (at least on popular architectures), and if you're a kernel thread you die immediately with the kernel reporting the state where this happened. Obviously in some cases this is fatal and the report might only be seen via say, a serial port hooked up to a device in a test lab or whatever.

So, it's not a kernel panic, but it's extremely bad, which seems appropriate because your code is definitely wrong. If you're not sure whether the index is correct you can use get() or get_mut() to have an Option, which will be None if your index was wrong (or of course you could ask about the length of the array since Rust remembers how long arrays are).

BUG() will panic the kernel.

https://elixir.bootlin.com/linux/latest/source/include/asm-g...

I guess that's quite drastic for a checked out of bound access, when there's no actual memory safety issue and the compiler can simply return an error from the function, or do something else less drastic.

In Rust code, if you're not able to locally reason that an array index is valid, it should be written with .get() and the None case handled appropriately.

It's impossible to claim there's "no actual memory safety issue" when a program's invariants have been broken: all bets are off at that point.

Why allow indexed access at all if the compiler is emitting a conditional check anyway?

Because it's convenient and familiar to most programmers. Not providing bounds-checked indexing makes some kinds of code very hard to write.

But note his problem also happens with integer division.

In Rust, a[x] on an array or vec is really a roughly a shortand for a.get(x).unwrap() (with a different error message)

Likewise, a / b on integers is a kind of a shortand for a.checked_div(b).unwrap()

The thing is, if the index ever is out of bounds, or if the denominator is zero, the program has a bug, 100% of time. And if you catch a bug using an assertion there is seldom anything better than interrupting the execution (the only thing I can think of is restarting the program or the subsystem). If you continue execution past a programming error, you may sometimes corrupt data structures or introduce bizarre, hard to debug situations.

Doing a pattern match on a.get(x) doesn't help because if it's ever None (and your program logic expects that x is in bounds) then you are kind of forced to bail.

The downside here is that we aren't catching this bug at compile time. And it's true that sometimes we can rewrite the program to not have an indexing operation, usually using iterators (eliding the bounds check will make the program run faster, too). But in general this is not possible, at least not without bringing formal methods. But that's what tests are for, to ensure the correctness of stuff type errors can't catch.

Now, there are some crates like https://github.com/dtolnay/no-panic or https://github.com/facebookexperimental/MIRAI that will check that your code is panic free. The first one is based on the fact that llvm optimizations can often remove dead code and thus remove the panic from a[x] or a / b - if it doesn't, then compilation fails. The second one employs formal methods to mathematically prove that there is no panic. I guess those techniques will eventually be ported to the kernel even if panics happen differently there (by hooking on the BUG mechanism or whatever)