News for hackers. Stuff that matters.
Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by zozbot234 on Monday March 28, 2022 @07:41AM (#30829419)
Thing is, it's not even clear that their added elements are all that sensible. If you're always running trusted tasks on your embedded hardware, what do you need the MPU for? Even in principle, it can only save you a bounds check. And if you start running outside code, then the clear privilege separation in Tock between trusted "Capsules" and other "Tasks" starts looking plenty attractive. Even OP notes that this MPU business introduces severe overhead.
Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by bcantrill on Monday March 28, 2022 @12:12PM (#30832056)
Other folks have mentioned this, but it's important to understand the limitations of Rust with respect to safety. In particular: every stack operation is -- at some level -- an unsafe operation as it operates without a bounds check. This isn't Rust's fault per se; non-segmented architectures don't have an architecturally defined way to know the stack base. As a result, even an entirely safe Rust program can make an illegal access to memory that results in fatal program failure. That, of course, assumes memory protection; if you don't have memory protection (or, like many embedded operating systems, you don't make use of it), stack overflows will plow into adjacent memory.

But wait, it gets worse: stack overflows are often not due to infinite stack consumption (e.g., recursion) but rather simply going deep on an unusual code path. If stack consumption just goes slightly beyond the base of the stack and there is no memory protection, this is corrupt-and-run -- and you are left debugging a problem that looks every bit like a gnarly data race in an unsafe programming language. And this problem becomes especially acute when memory is scarce: you really don't want a tiny embedded system to be dedicating a bunch of its memory to stack space that will never ("never") be used, so you make the stacks as tight as possible -- making stack overflows in fact much more likely.

Indeed, even with the MPU, these problems were acute in the development of Hubris: we originally put the stack at the top of a task's data space, and its data at the bottom -- and we found that tasks that only slightly exceeded their stack (rather than running all of the way through its data and into the protection boundary) were corrupting themselves with difficult-to-debug failures. We flipped the order to assure that every stack overflow hit the protection boundary[0], which required us to be much more intentional about the stack versus data split -- but had the added benefit of allowing us to add debugging support for it.[1]

Stack overflows are still pesky (and still a leading cause of task death!), but without the MPU, each one of these stack overflows would be data corruption -- answering for us viscerally what we "need the MPU for."

[0] https://github.com/oxidecomputer/hubris/commit/d75e832931f67...

[1] https://github.com/oxidecomputer/humility#humility-stackmarg...

Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by zozbot234 on Monday March 28, 2022 @12:45PM (#30832478)
> This isn't Rust's fault per se; non-segmented architectures don't have an architecturally defined way to know the stack base.

Bounding stack usage is of course a whole-program concern, but that ought to be feasible when building for embedded, where external "plugin" components are unlikely and there's no inherent need for true separate compilation. (Arguably, in such cases even the need for an actual "stack" structure is actually quite limited; a smarter compiler might well replace many uses of the activation stack with references to static data, reserving it for cases where e.g. reentrancy is actively needed. AIUI, there has been some work along those lines for LLVM.)

Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by bcantrill on Monday March 28, 2022 @01:52PM (#30833414)
But to get back to your original question: you can see why the state of things today necessitates the use of the MPU, even for Rust?
Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by ctz on Monday March 28, 2022 @01:39PM (#30833242)
> non-segmented architectures don't have an architecturally defined way to know the stack base

ARMv8-M does have stack limit registers, which do precisely this. Being a much simpler mechanism than the MPU, they are quicker to switch than the entire MPU state when switching tasks.

Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by bcantrill on Monday March 28, 2022 @01:48PM (#30833355)
Yes, true -- it's more accurate to say "architectures traditionally don't have a way to know the stack base." Certainly, having an architecturally-defined way to know the base of the stack is/would be really really helpful, and would allow stack overflow to be at once less dangerous and much more debuggable!
Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by TickleSteve on Monday March 28, 2022 @09:21AM (#30830246)
Data-execution exploits mean that you may accidentally run code you weren't expecting to. If you have any kind of message parser taking input from an external system and examining it, you may be vulnerable to these types of exploits without realising it.

Having the ability to mark (for example) your stack as non-executable is a simple change that can remove a whole class of problems.

That is only the skimming the surface tho.

Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by zozbot234 on Monday March 28, 2022 @09:33AM (#30830358)
Yes, this is a concern in memory-unsafe languages like C. But Rust can build memory-safe primitives with only a modest amount of runtime checking wrt. inherently unsafe operations.

(Of course, having better formal models of how 'unsafe' code behaves might allow us to be a lot more rigorous in building safe primitives, such as by endowing some 'unsafe' calls with proof objects that might reify the outcome of a bounds check.)

Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by rcxdude on Monday March 28, 2022 @10:04AM (#30830589)
Defence in depth. Rust isn't completely immune to memory safety bugs, due to the need for unsafe code underlying many primitives in the language as well as soundness bugs in the compiler, it just makes them much rarer. It's well worth designing the system so it doesn't fall apart as soon as one such bug appears.
Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by TickleSteve on Monday March 28, 2022 @11:03AM (#30831190)
Rust helps, but isnt immune (e.g. stack overflows).

Also, when you're dealing with embedded code, you're very likely going to be using unsafe code (e.g. driver level). With no other protection, incorrectly programming a DMA transfer to trample over code-space is not unknown... and Rust cannot protect you from that.

(Having said that... an MPU window will also not necessarilly catch a bad DMA transfer either).

Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by zozbot234 on Monday March 28, 2022 @11:30AM (#30831560)
> Rust helps, but isnt immune (e.g. stack overflows).

This is a good point but isn't Hubris designed to use statically bounded amounts of memory anyway, like much embedded software? AIUI, this was a key reason for keeping their design focused on synchronized requests, avoiding the hard-to-predict buffering that's needed for supporting 'async' models.

Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by steveklabnik on Monday March 28, 2022 @01:06PM (#30832773)
Defense in depth matters. The blog post shows an example of Hubris correctly killing a task that's touching memory it's not supposed to. That "supposed to" was due to a configuration error, but configuration errors can and do happen, and maybe would not be as benign as this one was.
Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by bluGill on Monday March 28, 2022 @08:58AM (#30830054)
The real question is what is the correct risk tolerance, there are many degrees in here with trade offs. If I mostly trust the other programmers then I'm just concerned about mistakes. If I'm running code that might have been written by some evil cracker trying to do some evil then I need more protection.
Re:Oxide on My Wrist: Hubris on PineTime was the best worst ... by panick21_ on Monday March 28, 2022 @09:46AM (#30830423)
That is just one of many reasons and not the main one. Again, actually watch and listen to the associated talks/blogs/docs and so on.