欢迎光临
我们一直在努力

166、【OS】【Nuttx】【栈溢出】运行时检测细节(一)

【声明】本博客所有内容均为个人业余时间创作,所述技术案例均来自公开开源项目(如Github,Apache基金会),不涉及任何企业机密或未公开技术,如有侵权请联系删除

背景

上篇 blog 【OS】【Nuttx】【栈溢出】R11 的特殊处理 补充了根据 R10 和 R11,可以推算出应该分配多大的栈空间,并详细分析了要向上补齐 12 字节的原因,其中 8 字节为栈指针 SP 的对齐要求,另外额外 4 字节是考虑一些特殊情况,比如异常 entry 入口,函数 prologue 等,SP 可能要再减 4 字节,下面继续

栈溢出检测

上篇 blog 里提到了,栈空间要多预留 12 字节,下面看一些栈检测的细节

在这里插入图片描述

首先 Per-Call Stack Checking 本质上是一种运行时栈溢出的检测机制,通过 gcc 的 -finstrument-functions,在每个函数入口/出口插入检查代码,检查代码会比较当前 SP 和 rBS(R10),如果 SP < rBS,说明栈溢出了,但前提是 rBS 必须已经被正确设置为安全栈的下限

OK,这第一个细节就是,谁负责设置 rBS,在什么时候设置?

首先是第一次发生时的函数调用,__start() 是 C 代码的入口(在 main 之前),__start() 需要在任何函数调用之前,就设置好 rBS,但如果 __start() 本身也被插桩(instrumented),编译器就会在它开头调用 __cyg_profile_func_enter,而这个函数内部会读取 rBS 来做栈检查,但此时 rBS 还没被初始化,就会产生未定义行为,甚至崩溃,所以这里禁止对 __start() 进行插桩

void __start(void) __attribute__((no_instrument_function));

这样 __start 就可以安全地执行,在无栈检查干扰的情况下初始化 rBS

在这里插入图片描述

然后是这里的

#ifdef CONFIG_ARMV7M_STACKCHECK
/* Set the stack limit before we attempt to call any functions */
__asm__ volatile ("sub r10, sp, %0" : : "r" (CONFIG_IDLETHREAD_STACKSIZE – 64) : );
#endif

这行汇编代码等价于

r10 = sp – (CONFIG_IDLETHREAD_STACKSIZE – 64);

这个点在之前blog 【OS】【Nuttx】【启动】启动函数分析 分析过,下面再详细说下,其目标是让 rBS 指向栈的安全下限:

  • SP 是当前 idle task 的栈顶(高地址)
  • CONFIG_IDLETHREAD_STACKSIZE 是 idle task 的总栈大小
  • 栈底(最低合法地址) = SP – CONFIG_IDLETHREAD_STACKSIZE
  • 但 rBS 被设置为 SP – (CONFIG_IDLETHREAD_STACKSIZE – 64),也就是栈底 + 64

可以看到,rBS 并不指向真正的栈底,而是比栈底高 64 字节的位置

在这里插入图片描述

这 64 字节就是故意留出的安全余量(safety margin),原因如下

  • 防止刚好溢出:如果 rBS 等于真实栈底,那么当 SP 刚好到栈底时,此时检查认为还没溢出,但下一个 push 就会破坏掉非栈内存,提前 64 字节报警,可以相对留出反应时间
  • 补偿异常/中断开销:即使 idle task 本身不深度调用,中断仍可能在 idle 上下文中发生,并使用部分栈空间,而 64 字节可容纳基本 ISR 压栈,比如 8 个寄存器 ≈ 32 字节,并考虑对齐
  • 调试友好:栈溢出时,还有 64 字节空间可以写 fault 信息,或触发断点,而不是直接崩溃

注意,这里的 64 字节是额外加的,和前面提到的 +12 字节不冲突:

  • +12字节:用于计算新任务栈大小时的余量
  • -64字节:用于设置初始 rBS 位置,提供运行时缓冲

所以综上,实际可用的栈空间,会比配置值 CONFIG_IDLETHREAD_STACKSIZE 少 64 字节


OK,本篇先到这里,如有疑问,欢迎评论区留言讨论,祝各位功力大涨,技术更上一层楼!!!更多内容见下篇 blog

赞(0)
未经允许不得转载:171主机测评 » 166、【OS】【Nuttx】【栈溢出】运行时检测细节(一)
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址