fw/syscall: run syscalls on a dedicated stack on ARMv7-M (FIRM-4306) - #2083
Merged
Merged
Conversation
jplexer
approved these changes
Sep 16, 2026
arch_thread_regions_set() encodes an unused per-thread region slot as RBAR=0 / RASR=0. On ARMv7-M the context switch streams the four slots through MPU_RBAR..MPU_RASR_A3, and an RBAR write with VALID clear does not change RNR: it updates the region RNR already points at, i.e. the one the previous slot just programmed. For App and Worker the empty slot 3 therefore lands on region 6 and its RASR=0 disables the task stack guard. The FreeRTOS port ORed VALID|region into every slot, which is why v4.36 firmware still faults on the guard while v4.37+ lets an app stack overflow walk into the kernel heap unnoticed. Keep VALID|region on empty slots (RASR stays 0, so the region is simply disabled). ARMv8-M selects the region through RNR and the aliases and is unaffected. Observed on qemu_flint: with the App thread running, region 6 read back as RBAR=0x6 RASR=0 while the thread's saved MPU words held the guard. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
be4d98d gave App/Worker syscalls their own 2 KiB privileged stack but only on ARMv8-M, because the relocation bounded that stack with PSPLIM. ARMv7-M apps kept running the whole privileged call chain on their 2 KiB task stack, and asterix reboots with StackOverflow / HardFault when a text-heavy watchface renders a glyph that misses the cache: text layout -> text_resources -> sys_resource_load_range -> resource_storage -> pfs_open -> FTL -> flash consumes ~1.3 KB on top of whatever the app already used, and the overflow happens privileged, so the kernel cannot recover and resets. Enable the relocation everywhere. The kernel side already handles the missing PSPLIM; on ARMv7-M the syscall stacks get a 32 B no-access MPU region below them instead, programmed through the free fourth per-task slot (MemoryRegion_Task4). The fault handler treats a hit in that region like the other stack guards, so a genuine kernel-side over-budget still reboots with StackOverflow, while an app that runs out of its own stack now faults unprivileged and is killed. Verified on qemu_flint with a stress app that burns N bytes of stack and then draws text in a custom font: with the guard alone, 512 B of extra app usage reboots the emulator (the device failure mode); with this change the same load succeeds (app peak 1968/2016 B, syscall stack peak 608 B) and 768 B only kills the app. WeatherGraph from the app store runs unchanged. Fixes FIRM-4306 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
Add a "Syscall near limit" case that burns the App task's stack down to 160 B and then loads a system font the app has not touched, a syscall whose privileged chain (resource_storage, filesystem, flash) needs a few hundred bytes. With syscalls relocated to their own stack the call completes and the app reports "SURVIVED (expected)"; on a kernel that still runs syscalls on the task stack it overflows privileged and the watch reboots, which is the FIRM-4306 failure mode. Tests can now declare that surviving is the expected outcome, so the "MPU MISS" wording stays reserved for accesses that should have faulted. On qemu_flint: upstream/main "survives" only because the wiped guard lets the overflow run into the kernel heap; with the guard restored alone the emulator reboots; with the dedicated syscall stack the test passes and the guard stays untouched. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Signed-off-by: Gerard Marull-Paretas <gerard@teslabs.com>
gmarull
force-pushed
the
syscall-stack-armv7m
branch
from
September 16, 2026 14:17
1cdd8e5 to
718586d
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Two fixes for App-task stack overflows on ARMv7-M (asterix, flint), found while analysing the FIRM-4306 coredumps, plus a new case in the MPU violation demo that exercises them.
kernel: keep empty MPU slots from wiping the previous region— since the native kernel (v4.37.0+),arch_thread_regions_set()writesRBAR=0for unused per-thread slots. With VALID clear that write lands on the region RNR already points at, so the empty slot 3 disables region 6 = the App/Worker stack guard. On v4.37+ ARMv7-M builds an app stack overflow no longer faults at all; it walks into the kernel heap. The FreeRTOS port ORedVALID|regioninto every slot, which is why v4.36 devices still fault on the guard. Worth backporting on its own.fw/syscall: run syscalls on the dedicated stack on ARMv7-M too— be4d98d's privileged syscall stack was ARMv8-M only (PSPLIM). FIRM-4306's coredumps show the App task's 2 KiB stack exhausted inside the privileged chaingraphics_draw_text → text_resources (glyph miss on an app font) → sys_resource_load_range → resource_storage → pfs_open → FTL → flash(~1.3 KB kernel-side, ~0.5 KB app), so the overflow is privileged and the watch reboots (StackOverflow: Task #3, orHardFault: LR 0x..1bffddwhen the exception frame itself hits the guard — that "LR" is SRAM residue read out of the never-written guard). This enables the relocation on ARMv7-M with a 32 B no-access MPU region under each syscall stack (freeMemoryRegion_Task4slot) instead of PSPLIM, and teaches the fault handler about it.apps/demo: probe the syscall stack from the MPU violation test— adds "Syscall near limit" (burn the app stack down to 160 B, then load an unused system font) and lets a test declare that surviving is the expected outcome.Validation (qemu_flint)
CONFIG_DEMO_APP_MPU_VIOLATION_TEST=y, all cases driven over the QEMU monitor, reboot detected via the kernel tick counter:Stress pbw: custom font, Up burns 64 B more app stack (VLA) before
graphics_draw_textwith a rotating string (glyph misses → pfs → FTL). Measured with QMPxp(the QEMU gdb stub reads through the MPU with the current privilege, so it lies in app context).WeatherGraph (the watchface from the ticket) runs unchanged.
pbl testpasses; asterix builds. Not yet run on hardware.Cost: 2 × 2080 B of kernel RAM. System apps run privileged and still use their own stack for everything (unchanged).
Test plan
StackOverflow/HardFaultrebootsstack_free_app_syscall_bytesheartbeat stays well above 0 on asterixFixes FIRM-4306
🤖 Generated with Claude Code