本文主要介绍如何在程序运行时进行堆栈溢出检测及检测到堆栈溢出之后的失效安全策略。 程序运行时堆栈溢出检测主要通过检查对应的堆栈指针(SP)是否超出指定的堆栈范围来实现,一般可以分为硬件检测和软件检测。 硬件堆栈溢出检测硬件堆栈溢出检测的原理:设置对应的硬件单元,如果在程序运行过程中,SP超出指定的堆栈范围,会触发对应的硬件错误。目前主要有如下两种方式: - 使用堆栈限制寄存器(Stack Limit registers)
- 使用内存保护单元(MPU:Memory Protection Unit)
使用堆栈限制寄存器(Stack Limit registers)ARMv8-M架构中包含了堆栈限制寄存器(Stack Limit registers),当SP的值小于(ARMv8-M架构中堆栈是向下生长的)对应Stack Limit registers的值时,会触发UsageFault或者HardFault: 使用内存保护单元(MPU:Memory Protection Unit)为了提高系统的安全性,越来越多的MCU集成了内存保护单元(MPU:Memory Protection Unit),可以通过设置MPU的属性,当SP访问超出指定的堆栈范围时,产生MemManageFault或者HardFault:
软件堆栈溢出检测软件堆栈溢出检测的原理:在程序开始之前,往堆栈防护区域填充特定的值(比如0xCD),然后在程序运行时去检查堆栈防护区域中之前填充的值是否被篡改:如果被篡改了,说明堆栈溢出。
RTOS任务堆栈溢出检测通常情况下,RTOS会提供任务堆栈溢出检测功能,只需要使能对应的宏定义,打开堆栈溢出检测功能。当检测到堆栈溢出时,RTOS会调用相应的堆栈溢出钩子函数(hook)。 注意:RTOS一般会计算各个任务堆栈的使用情况,所以在新建任务时,RTOS会把任务堆栈所有区域都填充特定的值,然后在运行过程中去检测和计算对应的堆栈使用情况。
在RTOS里面进行任务堆栈溢出检测一般需要如下操作(下面以Azure RTOS为例):
/* Determine whether or not stack checking is enabled.
By default, ThreadX stack checking is disabled.
When the following is defined, ThreadX thread stack checking is enabled.
If stack checking is enabled (TX_ENABLE_STACK_CHECKING is defined),
the TX_DISABLE_STACK_FILLING define is negated,
thereby forcing the stack fill which is necessary for the
stack checking logic. */
#define TX_ENABLE_STACK_CHECKING
void my_stack_error_handler(TX_THREAD *thread_ptr);
/* Register the "my_stack_error_handler" function with ThreadX
so that thread stack errors can be handled by the application. */
status = tx_thread_stack_error_notify(my_stack_error_handler);
系统堆栈溢出检测
系统堆栈溢出检查需要开发人员手动添加对应的代码:在程序开始之前,往堆栈防护区域填充特定的值(比如0xCDCDCDCD),然后在程序运行时去检查堆栈防护区域中之前填充的值是否被篡改。
#define STACK_FILL_PATTERN 0xCDCDCDCD
/* Linker generated symbols */
extern uint32_t CSTACK$$Base;
/* Fill the stack base with dedicated pattern */
*((uint32_t *) &CSTACK$$Base) = STACK_FILL_PATTERN;
/* Check system stack overflow */
/* If the filled pattern is changed, there is stack overflow */
if(STACK_FILL_PATTERN != *((uint32_t *) &CSTACK$$Base))
{
}
注意:软件堆栈溢出检查由于是通过软件代码进行检测,可能没有那么及时,也有可能在堆栈溢出的地方已经造成了其它的硬件错误(比如从堆栈POP出的PC指向了不可执行的地址)。 堆栈溢出之后的措施前面介绍了程序运行时进行堆栈溢出检测的方法。那么当检测到堆栈溢出之后程序应该怎么处理呢? 由于堆栈溢出可能会造成程序运行需要的重要数据的破坏,而这种破坏是未知的。所以当发生堆栈溢出时,程序通常是很难继续正常运行的,往往需要通过系统复位来重新回到正常的状态。但是需要注意的是,在系统复位之前,需要确保系统进入安全状态,避免造成更严重的损害。下面是发生堆栈溢出之后一般的处理策略: - 确保系统进入安全状态(比如关掉对应的执行器)
- 如果条件允许,Log堆栈溢出事件到非易失性存储器(Data Flash或者EEPROM),以便后续分析
- 系统复位
|