(Zero cost) stack overflow protection #34
Description
Today, the layout of RAM looks like this (assuming no heap and a single RAM region):
With this layout when a stack overflow occurs static
variables end up being overwritten /
corrupted silently.
This scenario can be avoided by simply changing the memory layout to look like this:
In this new scenario a stack overflow will hit the lower RAM boundary. Trying to write beyond the
RAM boundaries raises a HardFault exception. Thus, in theory, the HardFault exception handler
could be used as a stack overflow handler.
In systems where heap memory, which by default starts where the static
region ends and grows
upwards, exists a similar reordering of the regions can be applied.
Implementation
To my knowledge this can't be implement using only linker scripts. With a linker script you can
instruct the linker where to start a memory region but you can't specify the end address of the
region.
The related bits of the linker script we use are shown below:
PROVIDE(_stack_start = ORIGIN(RAM) + LENGTH(RAM));
SECTIONS
{
/* .. */
.bss : ALIGN(4)
{
_sbss = .;
*(.bss .bss.*);
. = ALIGN(4);
_ebss = .;
} > RAM
.data : ALIGN(4)
{
_sidata = LOADADDR(.data);
_sdata = .;
*(.data .data.*);
. = ALIGN(4);
_edata = .;
} > RAM AT > FLASH
/* The heap starts right after the .bss + .data section ends */
_sheap = _edata;
/* .. */
}
A C implementation of this region reordering uses a two step linking process. See
this StackOverflow answer for details.
Other options for stack overflow protection
Assuming that we can't change the memory layout. We could:
-
Use the MPU (Memory Protection Unit) to mark the upper boundary of the
static
region as
read-only. In this scenario when a stack overflow occurs a MemManage exception is raised. This has
an initialization cost but no runtime cost. The downside is that not all microcontrollers have a
MPU. -
Implement stack probes for the Cortex-M targets. I believe enabling stack probes carries a runtime
cost (per function call?) but I'm not sure.