How to protect from stack overflow on embedded targets?

How can I make -fstack-check work on stm32f4 or something like that?

Are there others ways to protect from overflows?

If I recall correctly it does work. I think it raises a Storage_Error. It might go straight to the last chance handler instead of unwinding.

Another option is to statically check the stack size. AdaCore has a proprietary tool for this (GNATstack), but you can get the data from GCC and do something yourself. I have someone elses stack analysis tool here that I made a few tweaks to to get it working with the current GCC version: prunt_board_3_software/firmware at master · Prunt3D/prunt_board_3_software · GitHub

Cool! Can you rewrite it in Ada as standalone Alire crate?

A better starting point for that would be to pull mucheckstack out of Muen. I encountered some friction with getting it working, which is why I don’t use it in the above project.

Let me explain why -fstack-check doesn’t work for me. Event on tiny function it wants 4Kb stack, like this:

        @ args = 0, pretend = 0, frame = 40
        @ frame_needed = 0, uses_anonymous_args = 0
        push    {r4, r5, r6, r7, r8, r9, lr}
        vpush.64        {d8}
        sub     sp, sp, #44
        vmov.f32        s16, s0
        sub     r0, sp, #4384  <---------
        subs    r0, r0, #8
        bl      __gnat_stack_check
Tested code
procedure Compute (Value : in out Float) is
   Data : array (1 .. 3) of Float := (Value, others => <>);
begin
   for J in 2 .. Data'Last loop
      Data (J) := Data (J - 1) * Value;
   end loop;
   for Item of reverse Data loop
      Value := Value + Item;
   end loop;
end Compute;

Do I really need 4Kb spare space for 40 byte frame? :roll_eyes:

The extra 4kb is so that an exception can be raised with a full backtrace. It’s controlled by STACK_CHECK_PROTECT. Note that this does not accumulate for every frame, it’s just one extra bit of space on top of the stack.

I pulled mucheckstack out of Muen and packaged it with Alire. I think the issue with it for my project was that it didn’t look give separate numbers for each task: GitHub - liampwll/mucheckstack · GitHub

It looks like it shouldn’t take much work to make it task-aware now that I’m looking closer at it. I’ll work on that at some point. Adding most of the other features from GNATstack should be trivial too.

Probably largely missing something here but just curious, why can you not override the default stack size on the task that calls Compute if that’s too high?

I could specify task My_Task with Storage_Size => 4384;, but I don’t want to spend memory on unused stack just to make __gnat_stack_check happy, if the task really needs just 1024 bytes or so. Having 10-20 tasks, for example, I will loose 30-60Kb of RAM for nothing. Total stm32f4 memory is 192Kb.

Thank you! I’ve compiled it locally and tried with the example and it works.

Just curious, is 4384 a somehow hardcoded value for __gnat_stack_check?

If I am reading the above code right, initially the stack pointer gets bumped by 44 bytes:
sub sp, sp, #44
then r0 is then set by the value of bumping sp a further 4384 bytes:
sub r0, sp, #4384
which is presumably then used by __gnat_stack_check to check for valid bounds? (i.e. its first argument)

Do you know if the source code for __gnat_stack_check can be accessed somewhere, by any chance?

Thanks!

It is. It comes from the STACK_CHECK_PROTECT macro in GCC. Although I don’t know off the top of my head where the extra bit above 4096 is coming from. Refer to: Stack Checking (GNU Compiler Collection (GCC) Internals)

It’s in System.Stack_Checking.Operations.

Thanks very much for that. :folded_hands: