Skip to content

Fix STM32 boards crashing instead of running out of memory - #11610

Merged
tannewt merged 2 commits into
adafruit:mainfrom
mikeysklar:stm-heap-below-stack
Oct 16, 2026
Merged

tannewt merged 2 commits into
adafruit:mainfrom
mikeysklar:stm-heap-below-stack

Conversation

@mikeysklar

Copy link
Copy Markdown
Collaborator

What

Keep the heap out of the stack's RAM on 18 STM32 boards.

Why

A big allocation could land on the stack and crash the board instead of raising MemoryError.

The largest free block on the Feather STM32F405 shrinks by about 24 KB.

Code that fit only by overlapping the stack now raises MemoryError.

Tested
Board Feather STM32F405 Express
Host Ubuntu 24.04.5
Firmware 10.3.1, main 11.0.0-alpha.1-86, this PR
Not tested H7, F7, L4, thunderpack
Builds feather_stm32f405_express, nucleo_h743zi_2, nucleo_f746zg
Probe, 10 trials each, power cycle between 10.3.1 main this PR
Fill largest block, recurse to stack limit 10/10 STACK_OVERFLOW 10/10 STACK_OVERFLOW 10/10 clean
Same, 32 KB block 1/1 STACK_OVERFLOW 10/10 clean
Probe code and output
import gc
gc.collect()
n = 90112
b = None
while b is None:
    try:
        b = bytearray(b'\xa5') * n
    except MemoryError:
        n -= 1024
def r(d):
    return r(d + 1)
try:
    r(0)
except RuntimeError:
    pass
pat = bytearray(b'\xa5') * 1024
bad = 0
for i in range(0, n, 1024):
    if b[i:i + 1024] != pat[:len(b[i:i + 1024])]:
        bad += 1
print('RESULT', 'CORRUPT' if bad else 'CLEAN', n, bad)
main:    board resets; after reconnect, supervisor.runtime.safe_mode_reason is supervisor.SafeModeReason.STACK_OVERFLOW
this PR: RESULT CLEAN 63488 0

AI assistance: written with Claude Code. I reviewed the change and the trial results.

🤖 Generated with Claude Code

Since 1cc4e6f (adafruit#9962) port_heap_get_top() returns _ld_heap_end, the
top of RAM. That is right for common_tcm.ld, where the stack lives in
DTCM, but common_default.ld and common_nvm.ld put the stack at the top
of RAM too. On those boards (Feather STM32F405 and 17 others) the TLSF
pool covered the whole stack, 25 KB on the F405, so a large heap block
could be handed out on top of it. A list growing to 32 KB in a
fragmented heap then hard-faulted, or overwrote the stack canary and
reset into safe mode with STACK_OVERFLOW, instead of raising
MemoryError.

Stop the heap at port_stack_get_limit() when the stack top falls inside
the heap's RAM, as atmel-samd, nordic and raspberrypi do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

@tannewt tannewt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not change the linker script?

Comment thread ports/stm/supervisor/port.c Outdated
Move the fix from port_heap_get_top() into common_default.ld and
common_nvm.ld, as suggested in review. _ld_heap_end now stops at the
same address port_stack_get_limit() returns.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@mikeysklar
mikeysklar requested a review from tannewt October 15, 2026 17:47

@tannewt tannewt left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you!

@tannewt
tannewt merged commit 5d85c57 into adafruit:main Oct 16, 2026
39 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants