[arm][cortex-m4] Add hardware stack guard support - #11740
Conversation
|
👋 感谢您对 RT-Thread 的贡献!Thank you for your contribution to RT-Thread! 为确保代码符合 RT-Thread 的编码规范,请在你的仓库中执行以下步骤运行代码格式化工作流(如果格式化CI运行失败)。 🛠 操作步骤 | Steps
完成后,提交将自动更新至 如有问题欢迎联系我们,再次感谢您的贡献!💐 |
📌 Code Review Assignment🏷️ Tag: bsp_stm32Reviewers: @Liang1795 @hamburger-os @wdfk-prog Changed Files (Click to expand)
📊 Current Review Status (Last Updated: 2026-08-25 14:35 CST)
📝 Review Instructions
|
d1ff4f6 to
07b426d
Compare
There was a problem hiding this comment.
Pull request overview
This PR adds Cortex-M4 support for RT-Thread’s RT_USING_HW_STACK_GUARD (hardware stack overflow protection) by introducing an MPU backend aligned with existing Cortex-M7 / Cortex-M33 implementations, and wiring MPU table switching into the PendSV context switch path.
本次 PR 为 Cortex-M4 补齐 RT_USING_HW_STACK_GUARD(硬件栈保护)能力:新增与 Cortex-M7/M33 对齐的 MPU 后端,并在 PendSV 上下文切换路径中切换线程对应的 MPU 表。
Changes / 变更点:
- Add Cortex-M4 MPU abstraction + implementation (
mpu.c/.h,mputype.h) to support memory protection and stack guard.
新增 Cortex-M4 的 MPU 抽象与实现(mpu.c/.h,mputype.h),用于内存保护与栈保护。 - Add
rt_hw_stack_guard_init()for Cortex-M4 and integrate MPU table switching intocontext_gcc.SPendSV path.
Cortex-M4 增加rt_hw_stack_guard_init(),并在context_gcc.S的 PendSV 路径中调用rt_hw_mpu_table_switch()。 - Provide an STM32F407 BSP reference static MPU region definition (Flash RX) guarded by
RT_USING_MEM_PROTECTION.
提供 STM32F407 BSP 参考实现:在RT_USING_MEM_PROTECTION条件下定义静态 MPU 区域(Flash 只读可执行)。
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| libcpu/arm/cortex-m4/SConscript | Excludes mpu.c when neither mem protection nor stack guard is enabled |
| libcpu/arm/cortex-m4/mputype.h | Adds Cortex-M4 rt_mem_attr_t and dynamic region count definitions |
| libcpu/arm/cortex-m4/mpu.h | Adds Cortex-M4 MPU permission/type macros and MPU APIs |
| libcpu/arm/cortex-m4/mpu.c | Implements MPU init/region ops/table switch + MemManage handler for Cortex-M4 |
| libcpu/arm/cortex-m4/cpuport.c | Adds rt_hw_stack_guard_init() and adjusts formatting in several areas |
| libcpu/arm/cortex-m4/context_gcc.S | Includes rtconfig.h and switches MPU table during PendSV context restore |
| bsp/stm32/stm32f407-fk407m2-zgt6/board/board.h | Adds NUM_STATIC_REGIONS definition under mem protection |
| bsp/stm32/stm32f407-fk407m2-zgt6/board/board.c | Adds static_regions[] (Flash RX) under mem protection |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| __asm int __rt_ffs(int value) | ||
| { | ||
| CMP r0, #0x00 | ||
| BEQ exit | ||
| CMP r0, #0x00 BEQ exit | ||
|
|
||
| RBIT r0, r0 | ||
| CLZ r0, r0 | ||
| ADDS r0, r0, #0x01 | ||
| RBIT r0, |
There was a problem hiding this comment.
已修复。Fixed.
The __rt_ffs inline assembly has been restored to the correct ARMCC syntax (same as the Cortex-M7 implementation), and wrapped with // clang-format off / // clang-format on to prevent the automatic clang-format workflow from corrupting it again.
已将 __rt_ffs 内联汇编恢复为正确的 ARMCC 语法(与 Cortex-M7 实现一致),并用 // clang-format off / // clang-format on 包裹,防止格式化工作流再次破坏。
|
存在一个ci格式化问题,可以用评论区的ci机器人格式化下 |
4feec6f to
094bf55
Compare
| if rtconfig.PLATFORM in ['iccarm']: | ||
| src += Glob('*_iar.S') | ||
|
|
||
| if not GetDepend('RT_USING_MEM_PROTECTION') and not GetDepend('RT_USING_HW_STACK_GUARD'): |
There was a problem hiding this comment.
当前只有 context_gcc.S 实现了 rt_hw_mpu_table_switch(),但 RT_USING_MEM_PROTECTION 和 RT_USING_HW_STACK_GUARD 在其他工具链下仍可开启,mpu.c 也会被 ARMCC/ArmClang/IAR 构建。这会导致 unsupported configuration 编译失败,或者更危险地成功构建但任务切换时没有更新 MPU。
建议把这段代码加上,约束一下
using_mpu = (
GetDepend('RT_USING_MEM_PROTECTION') or
GetDepend('RT_USING_HW_STACK_GUARD')
)
if using_mpu:
if rtconfig.PLATFORM != 'gcc':
raise RuntimeError(
'Cortex-M4 memory protection currently supports GCC only'
)
else:
SrcRemove(src, 'mpu.c')
There was a problem hiding this comment.
已采纳并修复。Fixed.
你的建议非常正确,非 GCC 工具链下开启 MPU 会导致 mpu.c 被编译但任务切换时不会更新 MPU 表。我已按你的建议加上约束,并做了一处小调整:
using_mpu = (
GetDepend('RT_USING_MEM_PROTECTION') or
GetDepend('RT_USING_HW_STACK_GUARD')
)
if using_mpu:
if rtconfig.PLATFORM not in ['gcc', 'llvm-arm']:
raise RuntimeError(
'Cortex-M4 memory protection currently supports GCC only'
)
else:
SrcRemove(src, 'mpu.c')
调整说明:M4 的 SConscript 中 ['gcc', 'llvm-arm'] 都会编译 *_gcc.S(其中包含 rt_hw_mpu_table_switch 实现),所以约束条件用了 not in ['gcc', 'llvm-arm'] 而不是 != 'gcc',避免误伤 llvm-arm 工具链。
已验证:gcc 下开启/关闭 MPU 两种场景均正常编译。
094bf55 to
7c057d0
Compare
Enable RT_USING_HW_STACK_GUARD on Cortex-M4, which was previously only available on Cortex-M7 and Cortex-M33. Core porting (libcpu/arm/cortex-m4/): - Add mpu.c/mpu.h/mputype.h aligned with the Cortex-M7 MPU layer. The only difference is the default memory type attribute: Cortex-M4 has no L1 cache, so the cacheability bits are reduced to three cases. - Add rt_hw_stack_guard_init() to cpuport.c. - Add missing #include <rtconfig.h> to context_gcc.S and invoke rt_hw_mpu_table_switch() in the PendSV context switch path. - Update SConscript to exclude mpu.c when memory protection is disabled. BSP reference (bsp/stm32/stm32f407-fk407m2-zgt6/): - board.h: include rtthread.h and define NUM_STATIC_REGIONS. - board.c: define static_regions[] marking the Flash region read-only, guarded by RT_USING_MEM_PROTECTION so it is not enabled by default.
|
我在 STM32F407 上实际集成并验证 Cortex-M4 1.
|
1 similar comment
|
我在 STM32F407 上实际集成并验证 Cortex-M4 1.
|
#include <rtthread.h>
#include <mprotect.h>
#if defined(RT_USING_MEM_PROTECTION) && defined(RT_USING_HW_STACK_GUARD)
static volatile rt_uint32_t g_stack_guard_sink;
/**
* @brief MPU memory fault callback used by stack guard test.
*/
static void stack_guard_fault_hook(rt_mem_exception_info_t *info)
{
rt_kprintf("\n");
rt_kprintf("========== STACK GUARD FAULT ==========\n");
if (info == RT_NULL)
{
rt_kprintf("info: NULL\n");
return;
}
rt_kprintf("thread : %s\n",
info->thread != RT_NULL ?
info->thread->parent.name : "NULL");
rt_kprintf("fault address: %p\n", info->addr);
rt_kprintf("region start : %p\n", info->region.start);
rt_kprintf("region size : %u\n",
(unsigned int)info->region.size);
rt_kprintf("MMFSR : 0x%02x\n",
(unsigned int)info->mmfsr);
rt_kprintf("DACCVIOL : %d\n",
!!(info->mmfsr & (1U << 1)));
rt_kprintf("MSTKERR : %d\n",
!!(info->mmfsr & (1U << 4)));
rt_kprintf("MMARVALID : %d\n",
!!(info->mmfsr & (1U << 7)));
rt_kprintf("=======================================\n");
}
/**
* @brief Deliberately access one of the stack guard regions.
*
* parameter:
* 0 -> bottom guard
* 1 -> top guard
*/
static void stack_guard_probe_thread(void *parameter)
{
rt_thread_t self;
rt_uint8_t *stack_begin;
rt_uint8_t *stack_end;
volatile rt_uint8_t *fault_addr;
self = rt_thread_self();
stack_begin = (rt_uint8_t *)self->stack_addr;
stack_end = stack_begin + self->stack_size;
rt_kprintf("\n");
rt_kprintf("========== STACK GUARD PROBE ==========\n");
rt_kprintf("thread : %s\n", self->parent.name);
#ifdef RT_USING_HW_STACK_GUARD
rt_kprintf("stack_buf : %p\n", self->stack_buf);
#endif
rt_kprintf("stack_addr : %p\n", self->stack_addr);
rt_kprintf("stack_size : %u\n",
(unsigned int)self->stack_size);
rt_kprintf("stack_end : %p\n", stack_end);
if ((rt_ubase_t)parameter == 0U)
{
/*
* Cortex-M stack grows downward.
*
* stack_addr points to the first usable byte after
* the bottom guard region.
*
* Therefore stack_addr - 1 is guaranteed to be
* inside the bottom guard region.
*/
fault_addr = stack_begin - 1;
rt_kprintf("probe : bottom guard\n");
}
else
{
/*
* stack_addr + stack_size is the first byte of
* the top guard region.
*/
fault_addr = stack_end;
rt_kprintf("probe : top guard\n");
}
rt_kprintf("fault addr : %p\n", fault_addr);
rt_kprintf("MPU CTRL : 0x%08x\n",
(unsigned int)MPU->CTRL);
rt_kprintf("SHCSR : 0x%08x\n",
(unsigned int)SCB->SHCSR);
rt_kprintf("about to access protected address...\n");
/*
* Give console output a chance to finish before faulting.
*/
rt_thread_mdelay(100);
/*
* Expected:
*
* This write MUST trigger MemManage_Handler.
*
* This statement must never complete.
*/
*fault_addr = 0x5A;
/*
* Reaching this point means stack guard protection failed.
*/
rt_kprintf("ERROR: guard write succeeded!\n");
while (1)
{
rt_thread_mdelay(1000);
}
}
/**
* @brief Consume stack recursively until the bottom stack guard is hit.
*/
__attribute__((noinline))
static void stack_guard_consume_stack(rt_uint32_t depth)
{
volatile rt_uint8_t frame[128];
rt_size_t i;
/*
* Force actual memory accesses so the compiler cannot optimize
* the local stack object away.
*/
for (i = 0; i < sizeof(frame); i += 16)
{
frame[i] = (rt_uint8_t)depth;
}
g_stack_guard_sink += frame[0];
/*
* Large enough depth to exceed the test thread stack.
*/
if (depth < 1000U)
{
stack_guard_consume_stack(depth + 1U);
}
/*
* Prevent tail-call optimization.
*/
g_stack_guard_sink += frame[depth % sizeof(frame)];
}
/**
* @brief Real stack overflow test thread.
*/
static void stack_guard_overflow_thread(void *parameter)
{
rt_thread_t self = rt_thread_self();
RT_UNUSED(parameter);
rt_kprintf("\n");
rt_kprintf("========== REAL STACK OVERFLOW ==========\n");
rt_kprintf("thread : %s\n", self->parent.name);
rt_kprintf("stack_addr : %p\n", self->stack_addr);
rt_kprintf("stack_size : %u\n",
(unsigned int)self->stack_size);
rt_thread_mdelay(100);
stack_guard_consume_stack(0);
rt_kprintf("ERROR: stack overflow was not detected!\n");
}
/**
* Usage:
*
* stack_guard_test bottom
* stack_guard_test top
* stack_guard_test overflow
*/
static int stack_guard_test(int argc, char **argv)
{
rt_thread_t thread;
if (argc != 2)
{
rt_kprintf("usage:\n");
rt_kprintf(" stack_guard_test bottom\n");
rt_kprintf(" stack_guard_test top\n");
rt_kprintf(" stack_guard_test overflow\n");
return -RT_ERROR;
}
rt_hw_mpu_exception_set_hook(stack_guard_fault_hook);
if (rt_strcmp(argv[1], "bottom") == 0)
{
thread = rt_thread_create("sg_bottom",
stack_guard_probe_thread,
(void *)0,
1024,
10,
10);
}
else if (rt_strcmp(argv[1], "top") == 0)
{
thread = rt_thread_create("sg_top",
stack_guard_probe_thread,
(void *)1,
1024,
10,
10);
}
else if (rt_strcmp(argv[1], "overflow") == 0)
{
thread = rt_thread_create("sg_over",
stack_guard_overflow_thread,
RT_NULL,
1024,
10,
10);
}
else
{
rt_kprintf("unknown test: %s\n", argv[1]);
return -RT_ERROR;
}
if (thread == RT_NULL)
{
rt_kprintf("create stack guard test thread failed\n");
return -RT_ERROR;
}
rt_thread_startup(thread);
return RT_EOK;
}
MSH_CMD_EXPORT(stack_guard_test, test MPU hardware stack guard);
#endif |
Description / 描述
为 Cortex-M4 补齐
RT_USING_HW_STACK_GUARD硬件栈保护支持。此前该能力仅 Cortex-M7 和 Cortex-M33 可用,M4(尤其是大量 STM32F4 BSP)缺失。Add hardware stack guard support for Cortex-M4, which was previously only available on Cortex-M7 and Cortex-M33.
Why / 为什么
栈溢出是嵌入式系统最高频、最隐蔽的故障之一。Cortex-M4 同样具备 MPU,但 RT-Thread 的 mprotect 框架尚未在 M4 上落地
rt_hw_stack_guard_init。Stack overflow is one of the most common and hardest-to-debug faults in embedded systems. Cortex-M4 has an MPU, but the mprotect framework has not yet provided
rt_hw_stack_guard_initon M4.Modified Files / 修改文件
核心移植 core porting (
libcpu/arm/cortex-m4/):mpu.c/mpu.h/mputype.h(新增):对齐 Cortex-M7 的 MPU 抽象层。唯一差异在默认内存类型属性——M4 无 L1 cache,cacheability 位简化为三种情况。cpuport.c:新增rt_hw_stack_guard_init()。context_gcc.S:补上缺失的#include <rtconfig.h>,并在 PendSV 上下文切换路径中调用rt_hw_mpu_table_switch()。SConscript:未开启内存保护时排除mpu.c。BSP 参考实现 (
bsp/stm32/stm32f407-fk407m2-zgt6/board/):board.h:includertthread.h,定义NUM_STATIC_REGIONS(#ifdef保护)。board.c:定义static_regions[](Flash 区域只读,#ifdef保护,不默认开启)。Verification / 验证
RT_USING_HW_STACK_GUARD两种配置均通过;frdm-k64f无回归)。