cortex-m icon indicating copy to clipboard operation
cortex-m copied to clipboard

Enable stack overflow protection by default

Open jonas-schievink opened this issue 5 years ago • 8 comments

Not providing this is unsound, so we should make sure this is on by default before 1.0. This was already implemented in the past, but was removed with the switch to LLD (if I'm not mistaken).

@japaric has been working on flip-lld, a small wrapper that invokes LLD twice to place the stack below the static data sections. An older description of this approach is described in this blog article.

(unfortunately, experiments with doing it with one linker invocation have so far not worked out, but according to https://github.com/rust-embedded/cortex-m-rt/issues/34#issuecomment-336704755 there's another way to do it with a single linker invocation, might be worth checking out)

jonas-schievink avatar Feb 06 '20 00:02 jonas-schievink

@jonas-schievink I do not agree with the "unsound" part of your intro but it would certainly be nice to have it.

therealprof avatar Feb 06 '20 08:02 therealprof

This may not work well on Cortex-M0 microcontrollers when you have to relocate vector table to the start of the RAM. For example, when you have a bootloader at the beginning of the FLASH with its own interrupt vector table and you want to have your own one.

Disasm avatar Feb 06 '20 08:02 Disasm

@therealprof "unsound" just means that it's possible to cause undefined behavior in safe code, which is the case here (just grow the stack enough). While there are exceptions to this that aren't really modeled by Rust's type system (such as writing to /dev/mem, or DMAing all over RAM), creating local variables isn't one of them.

@Disasm Ah, you mean when using a vendor-specific feature to map SRAM to address 0. I'm hoping that we'll add features for controlling the memory layout more precisely before 1.0, and it doesn't seem like this situation would be worse than what we have now (you'd just smash the vector table instead of application data, which is hopefully more noticeable).

jonas-schievink avatar Feb 06 '20 11:02 jonas-schievink

@jonas-schievink

you'd just smash the vector table instead of application data, which is hopefully more noticeable

This sounds like a code execution vulnerability out of the box. Quite easy to exploit.

Disasm avatar Feb 06 '20 12:02 Disasm

That's true, but the same is the case today from what I can tell (only you'd also have to overwrite .data and .bss in RAM). Since the M0 also doesn't have an MPU that could be used to mark the region read/execute-only, it seems like the only real fix would be to use a vendor-specific protection feature for SRAM or the stack (not sure what's offered here), or to add stack checking code to every function call (which needs rustc support).

jonas-schievink avatar Feb 06 '20 13:02 jonas-schievink

Anyway, I think it's a good idea to have this stack protection feature, but I'd also like to have a possibility for opt-out.

Disasm avatar Feb 06 '20 13:02 Disasm

For reference / posteriority: the trick I have tried is "end address" aligning sections in the linker script. The trick looks like this:

SECTIONS
{
  .bss ORIGIN(RAM) + LENGTH(RAM) - SIZEOF(.bss) :
  /*   ^^^^^^^^^^^^^^^^^^^^^^^^^                */
  /*   desired end address                      */
  {
    . = ALIGN(4);
    *(.bss .bss.*);
    . = ALIGN(4);
  } > RAM

  .data ADDR(.bss) - SIZEOF(.data) :
  /*    ^^^^^^^^^^^^^^^^^^^^^^^^^ */
  /*    start address             */
  {
    . = ALIGN(4);
    *(.data .data.*);
    . = ALIGN(4);
  } > RAM

  /* .. */
}

The expression after the section name (.bss) and before the : is the start address (Virtual Memory Address, to be precise) of the section. The trick is applying the SIZEOF operator, which returns (or should return) the size of the section, on the section itself to decide its start address.

A complete, working example can be found here: https://github.com/japaric/xenon/blob/02e36e97777a8ebc53ccf202bb3cf278e1662de9/firmware/xenon/link.x#L25

For whatever reason, that example breaks down (you get weird linker errors) once the .rodata has a non-zero size. That could be a LLD bug; I don't know. The example doesn't link when you use GNU LD.

japaric avatar Feb 06 '20 21:02 japaric

https://app.bountysource.com/issues/110784472-enable-stack-overflow-protection-by-default

pwnorbitals avatar Aug 26 '22 09:08 pwnorbitals