Comment by steveklabnik

6 hours ago

> probably be a linker

I don't think that's any different either. The core job of linking isn't particularly unsafe.

(Unless, similarly, you're doing the hot reloading stuff)

I've noticed that people equate "low level stuff" with unsafe, regardless of whether it's contextually justified.

  • I think it's an understandable prior. Historically, "low level stuff" was near-exclusively (see my comment below about OCaml...) written in unsafe languages. Even if that wasn't always literally required, it sometimes was, and so thinking this is the case was a reasonable thing to think.

    It is only relatively recently that we have gained more realistic options in these spaces, and so not fully understanding the implications, or preferring the historically normal choices, is understandable.

  • I'll play devil's advocate. I think emitting machine code intended to run is unsafe because you could emit unsafe machine code, which could run. It's the whole system that is either safe or not, not the individual components. If your system gets hacked by a buffer overflow in the end, nobody cares whether it was the linker that overflowed or the code emitted by the linker.

    • "Safe" has a very specific definition in Rust. It's not identical to the broader definition used in technical English. You can easily have safe rust code with behaviors any reasonable layperson would call unsafe, like crashing a plane. The original article, comment, and replies were using the word in the Rust sense from my reading, not the English meaning.

      14 replies →

    • > It's the whole system that is either safe or not, not the individual components.

      This is a core perspective disagreement. While this is true:

      > If your system gets hacked by a buffer overflow in the end, nobody cares whether it was the linker that overflowed or the code emitted by the linker.

      That does not mean that increasing the amount of safety in the individual components isn't helpful, because it helps minimize the above outcome, even if it will never be zero.

    • Safety is a feature of a system - yes. It's also a property of what it's against. A computer could be safe against being hacked but still be dangerously easy to drop on someones toe and break it.

      Safety [against something] is also a feature of components - a system made up of only safe components [against a thing] is safe [against the same thing... I'm going to stop this qualification now for brevity]. A system containing unsafe components may or may not be safe but at least you know what components usage you need to look at carefully.

      If your linker is safe, linking code will never result in the thing it is safe against. Ever. This is a useful property even if running the linked thing is not safe because it means:

      1. When things go wrong in strange ways, you have strict bounds guiding you in figuring out what went wrong.

      2. You can build reliable systems that do part of the job, and only have to sandbox the other half of the job. Compiling in a CI system will (if the compiler was entirely safe) be safe. You can do it with secrets present against malicious code. Running tests will have to be sandboxed (assuming running tests isn't safe). This could for instance enable safely sharing significantly more artifacts for incremental builds in CI.

      Unfortunately very few compilers are really safe against anything (though I do wonder how I could break my toe on one). Rustc for instance has a giant C++ half called llvm that isn't really hardened at all. We get away with this by just not trusting the compiler when run against potentially malicious code.

Perhaps the parent meant dynamic linker.

  • It wouldn't be the linker that has to be unsafe, it'd be the "and now execute!" jump. And that could be abstracted as memfd+execveat, which are fairly normal operations.

    OP's argument is roughly "doings things with computers has to be unsafe to be useful", which is.. uninteresting.