打印
[综合信息]

程序运行时堆栈溢出检测及失效安全策略

[复制链接]
35|2
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
tifmill|  楼主 | 2024-6-26 10:03 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

本文主要介绍如何在程序运行时进行堆栈溢出检测及检测到堆栈溢出之后的失效安全策略。

程序运行时堆栈溢出检测主要通过检查对应的堆栈指针(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


  • 定义并注册堆栈溢出钩子函数(hook)


  • 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),以便后续分析
  • 系统复位

使用特权

评论回复
沙发
LEDyyds| | 2024-6-27 16:21 | 只看该作者
处理措施可以

使用特权

评论回复
板凳
埃娃| | 2024-6-28 17:41 | 只看该作者
栈溢出后是下面的数据消失还是顶上的消失啊?

使用特权

评论回复
发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

39

主题

1201

帖子

0

粉丝