cembeddedbufferstack-overflowcortex-m

Local variable allocation crashes the stack in embedded C on cortexm4 and operating system


Details: embedded C, cortex M4 with operating system (RTX Kernel), compiled with ARM Compiler V5.06 update6 and Microlib.

Inside a periodic task there are some functons call, one is a debug function:

/**
 * @details
 */
void DEBUG_Utilities_IndirectTraceString
(
  const char *  moduleName,
  const char *  string
)
{
  Debug_Message_t * dbgMsg;
  char              tempString[16];
  ...

Context switch, registers are pushed in the stack and the stack pointer gets updated (0x2000FE80 points now to0x2000FE58 as expected, 40 bytes for r0-r8 and LR ).

The local char array tempString is created and the first element is allocated at 0x2000FE58,second one at 0x2000FE59 etc, thus crashing the pushed registers.

Why?

I'd expect the array to grow downards be allocated below the stack pointer, not above. Any hint would be gladly appreciated.

Further clarification becouse I used the wrong wording: I am not concerned with the fact that array elements are indexed sequentially upwards, this is what I expect. I am concerned with the fact the first element of the array is allocated at the stack pointer after the push instead of allocating 20bytes of ram to the two variables (array and the custom type).

Also, I am not debugging the stack with the debug function. What happens is that the tempString array is populated and destroys the content of the pushed r0-r3.This is why I inspected the code, the assembly and noticed that the tempString is located at the wrong address.

Here is the assembly as per disassembler:

DEBUG_Utilities_IndirectTraceString
i.DEBUG_Utilities_IndirectTraceString
$Thumb
{
 08006F44   PUSH.W         {R0-R8, LR}
 08006F48   MOV            R4, R0
 08006F4A   MOV            R5, R1
dbgMsg = DebugTask_CreateMessage();

before the push, this code is called:

 08002C28   ADD            R1, PC, #0x016C               ; 0x08002D98
 08002C2A   ADD            R0, PC, #0x0150               ; 0x08002D7C
 08002C2C   BL             DEBUG_Utilities_IndirectTraceString  ;0x08006F44

05/08/2024: adding image to clarify what I see pre and post push.

Here the registers and memory dump pre and post push.

Pre and post push registers allocation


Solution

  • I think you have misunderstood what is happening here. The stack frame already includes the local variables when it is established on entry to the function. The accesses you are seeing are to the memory allocated to the string, not the pushed registers. That is, the stack pointer includes the memory for the string, it is not separate

    In any event in a context switch the scheduler stores the context of a preempted task and switches to the task stack of the preempting task. The previous context is stored on the stack of the preempted task, not the stack you are observing.

    Here your stack frame will comprise of:

    So before any caller register save, that is 24 bytes, then the remaining 16 bytes is the caller saving caller registers that it will modify (4 registers saved). It does not blindly store all registers, so it is variable.

    Note that the precise details may vary somewhat, but the above accounts for your observation without the misapprehension that it somehow involves context storage.