Hacker News new | ask | show | jobs
by lilyball 658 days ago
If interactions through safe abstractions leak unsafety into your code, that's a bug in the abstractions.

Edit: who voted me down? This is literally the definition of writing a safe abstraction around unsafe code in Rust. If the user of your safe abstraction can trigger a segfault or UB or otherwise do something that is supposed to require unsafe, then your safe abstraction is buggy.

2 comments

Memory safety and UB sure you could argue for that. But take for example something like FD reuse. In Rust's std library there's abstractions to prevent that and encourage ownership semantics even though there's no segfault or UB that will happen as a result. So clearly "safety" is a spectrum here. And it should be pretty obvious from the existence of unit tests and various property testing frameworks & sanitizers that bugs exist that aren't memory safety / UB related can't be completely prevented through Rust's abstractions.
Safety may be a spectrum, but `unsafe` in Rust is very explicit about what it allows/enables. They never say "if you only write safe Rust you won't encounter logic bugs".
All unsafe does is mean you’re only allowed to call unsafe functions from unsafe blocks or unsafe functions. Idiomatically memory safety is guaranteed if you stick to safe functions that themselves don’t have any issues with memory safety (by being safe or by calling an unsafe function that is correctly implemented).*

For example, retrieving the raw fd from a BorrowFd is unsafe even though there’s no memory corruption issues.

I was just replying to OP that claimed that safety issues in an abstraction are a bug in the abstraction. While that’s a good general rule of thumb, I was highlighting how there are safety issues that are difficult to guarantee within some abstractions.

* and adding on this, technically even using only safe code there’s a few examples how you can create memory unsafety. The canonical example is opening /proc/mem as a file and overwriting memory out from under Rust’s ownership model. This just goes to show how difficult it is to provide a completely safe abstraction even when you limit yourself to memory safety so treating every safety issue a buggy abstraction is too strong. It’s an important opportunity to review but it could just be intractable and you have to establish other conventions to solve the issue

> All unsafe does is mean you’re only allowed to call unsafe functions from unsafe blocks or unsafe functions.

That's the mechanism by which the safety invariant is enforced, not a description of the safety invariant itself; it's the "how", not the "why". The safety invariant itself can be roughly summarized as "memory corruption can't originate in safe code".

> retrieving the raw fd from a BorrowFd is unsafe

No it isn't. https://doc.rust-lang.org/std/os/fd/struct.BorrowedFd.html#m...

> opening /proc/mem as a file

I think the Ferrocene people are working on a more formal definition of memory safety in Rust that excludes things like this, since of course no program can defend itself against it.

There are some cases, especially when embedding other runtimes with different invariants, where Rust's safety model isn't quite expressive enough and so people are forced to provide unsound APIs for the sake of practicality. https://docs.rs/pyo3/latest/pyo3/marker/index.html#drawbacks is an example. None of those have been cited here, though, and it's not clear to me that firmware inherently imposes this kind of challenge; as far as I can tell, it's perfectly possible to write sound Rust APIs for firmware.

> If interactions through safe abstractions leak unsafety into your code, that's a bug in the abstractions.

This isn't much different than saying that C safe language if you write it perfectly.

> definition of writing a safe abstraction

That definition is not guaranteeing safety, because that is usually not possible. It just about limiting risky areas.

> This isn't much different than saying that C safe language if you write it perfectly.

The difference is in C, the entire language is unsafe, whereas in Rust only the bits marked `unsafe` are unsafe. Most Rust code does not need to use `unsafe` at all, and by extension most Rust developers don't need to touch `unsafe`. And for those developers who do use `unsafe`, instead of having to prove every single line of the program is safe like you do in C, you only have to prove that the tiny subset of the program contained within `unsafe` is safe.

No, not quite. If the abstractions leak then it's Rust's fault. If you write buggy C then it's your fault.
> If the abstractions leak then it's Rust's fault

It is the fault of the one who wrote the abstraction.

Which is one of the Rust people yes