Cortex M-33 PSPLIM register is one word off the real limit

Hello to the community.

Recently, I’ve run into the stack overflow issue while debugging my application. Pretty standard case, nothing special. But while fixing it, I met the neat Cortex-M33 feature called “Stack Pointer Limit Registers”. If any instruction like “push” attempts to access the memory below the given limit, it rises a “UsageFault”/“HardFault” exception (depends on the SHCSR register value) and sets the “STKOF” bit in the “UFSR” register. In the exception handler one may get the “PSP” register value using the “mrs” instruction and figure out which task has failed exactly.

A task which failed in my case was created using the “xTaskCreateStatic” call with the following arguments: ulStackDepth = 2304, puxStackBuffer = 0x2000185C. So, I was expecting the PSPLIM register to have the 0x2000185C value, but instead it was configured to 0x20001858, one word off the real end of the stack.

That potentially makes it possible to corrupt the one word of the “out-of-stack” memory without causing the exception. For instance, let’s consider the following edge case:

Step 1:
SP = 0x2000185C;
LR = 0x00011035;
RAM at 0x20001858 = 0x00000000;
RAM at 0x2000185C = 0xA5A5A5A5;
RAM at 0x20001860 = 0xA5A5A5A5;

Step 2:
The processor executes the “push LR” instruction. No exception happens.

Step 3:
SP = 0x20001858;
LR = 0x00011035;
PSPLIM = 0x20001858;
RAM at 0x20001858 = 0x00011035;
RAM at 0x2000185C = 0xA5A5A5A5;
RAM at 0x20001860 = 0xA5A5A5A5;

At this point the program may execute however long until it tries to “push” something one more time. And at that moment the exception does rise.

Please tell me, is it really an intended behavior? Because I feel like the PSPLIM should point to the last accessible word in the stack.

Thanks in advance for the answers and comments.

You are right about the intended behavior.

Unfortunately, the stack pointer limit registers always point to a location with 8-byte alignment. The port code attempted to write 0x2000185C to the register, but the register only took 0x20001858.

Can you experiment with the values for puxStackBuffer and ulStackDepth and verify everything works with 8-byte alignment?

Oh, you’re completely right!

I have completely missed the *LIM registers’ layout in the documentation, where 2 least significant bits are marked as “reserved”. And after I aligned the stack by 8 bytes (0x20001860, if being more specific), the “UsageFault” exception did fire.

I will keep that in mind in the future. Though, this limitation makes this feature a little bit less “fancy”, than it could be.

Thanks for spending your time to answer. Have a good luck!

The ARM documentation says that the stack pointer at function boundaries is supposed to be 8 byte aligned, so (by implication) stacks are presumed to be 8 byte aligned.

I think the code does make sure the stack pointer starts 8 byte aligned, but seems to assume the lower bound started 8 byte aligned. It might make sense for the ARM ports that have the SPLIM register to round up the lower limit to avoid the problem.

@richard-damon, could you please post a link to the document where it is stated? Is it ARM Procedure Call Standard?

I would need to search for it, but I think it is in the ABI documentation. I seem to remember that it wasn’t required at first, but for later Cortex-M processors, due to the way some instructions (I think load/store multiple) worked they needed 8-byte alignment, so the ABI was updated to 8 byte alignment of procedure boundaries.

Here is one link:

Documentation – Arm Developer)

Ok, I’ve got it.
Thank you for the clues!

This is a good idea. Alternatively, add a new configASSERT() on the alignment of puxStackBuffer passed to xTaskCreateStatic(). That would make developers aware of the issue at startup so they can resolve cleanly. Could be implemented in pxPortInitialiseStack() since the issue is port specific.

2 Likes