Stack Overflow Prevention
Contents
Introduction
In order to detect stack overflows in a running embedded system the Segger compiler is able to generate code to check for stack overflows in every function. This can be activated using the command line switch -mstack-overflow-check (cc1 interface). For secure systems, a stack overflow must be detected before any memory is destroyed by the overflowing stack, therefore a check is required on any changed of the stack pointer and before any large stack growth.
Compiler generated code
If stack overflow checking is activated in the compiler different code is the generated as follows:
- Functions that don't touch the stack are not changed.
- In functions that uses a local stack frame but don't use R3 as a function parameter: The set up of the stack frame (usually a sub sp, #size) is replaced by loading the required size into register R3, then calling the function __SeggerChkStkR3().
- In functions that uses a local stack frame and uses R3 as a function parameter: Same as 2., but using register R4 instead of R3 and calling the function __SeggerChkStkR4(). This means, that R4 must be pushed at function entry.
- In functions that don't use a local stack frame but need to save register on the stack, the function __SeggerChkStk0() (with no parameter) is called after pushing the registers.
- Functions that need dynamic stack allocation (for example if alloca() or variable sized arrays are used) will also call __SeggerChkStkR3(). Because this may happen in the middle of a function, the register allocator will be instructed to make sure, R3 can be used as argument.
The called functions then can check for a stack overflow using a stack limit that is stored in a global variable. These functions are called after registers to be saved are pushed on the stack. Therefore the stack limit must be calculated such that there is always space for:
- All general purpose registers that may be pushed at function entry (R0 - R11, LR). There are some optimization that leads to pushing R0 - R3.
- All callee saved floating point / vector registers (D8 - D15)
- Registers save on interrupt entry (8 words)
- 3 (spare) words for alignment and emergency spill splots
Stack checking code generation can be disabled for single functions with __attribute__((no_stack_overflow_check)), which may be required for internal RTOS functions.
Generated code examples
Original code:
ReadReqEP:
push {lr}
sub sp, #12
and r12, r0, #7
...
is changed to
ReadReqEP:
push {lr}
movs r3, #12
bl __SeggerChkStkR3
and r12, r0, #7
...
Original code:
SEGGER_CRC_Calc:
push {r4, r5, r6, r7, lr}
sub sp, #64
mov lr, r2
...
is changed to
SEGGER_CRC_Calc:
push {r4, r5, r6, r7, lr}
movs r4, #64
bl __SeggerChkStkR4
mov lr, r2
...
Original code:
SEGGER_CRC_CalcBitByBit:
push {r4, r5, lr}
cbz r1, .LBB0_4
...
is changed to
SEGGER_CRC_CalcBitByBit:
push {r4, r5, lr}
bl __SeggerChkStk0
cbz r1, .LBB0_4
...
The stack check functions
The functions called by the compiler generated code must check the remaining stack size and must not return in case of a stack overflow. For efficiency the functions do not follow the standard calling convention. Therefore the functions must not modify any registers except R12 and the register containing the size argument (if any). The functions also must adjust the stack pointer before they return.
A sample implementation for a Cortex-M CPU with 2 stack pointers is like this:
__SeggerChkStkR3:
mrs r12, IPSR
lsls r12, #23
ite eq
ldreq r12, =StackLimitUser
ldrne r12, =StackLimitSys
sub r3, sp, r3
ldr r12, [r12]
cmp r3, r12
itt hs
movhs sp, r3
bxhs lr
b StackOverflow
__SeggerChkStkR4:
mrs r12, IPSR
lsls r12, #23
ite eq
ldreq r12, =StackLimitUser
ldrne r12, =StackLimitSys
sub r4, sp, r4
ldr r12, [r12]
cmp r4, r12
itt hs
movhs sp, r4
bxhs lr
b StackOverflow
__SeggerChkStk0:
mrs r12, IPSR
lsls r12, #23
ite eq
ldreq r12, =StackLimitUser
ldrne r12, =StackLimitSys
ldr r12, [r12]
cmp sp, r12
it hs
bxhs lr
b StackOverflow
Stack limit
The startup code or the RTOS is responsible for setting the StackLimit global variable and keeping it up to date.