首页 > 解决方案 > 将金丝雀用于 bss 或数据部分以检测溢出/粉碎有用吗?

问题描述

在我们基于 GCC 的 C 嵌入式系统中,我们使用-ffunction-sections-fdata-sections选项来允许链接器在链接最终可执行文件时删除未使用(未引用)的部分。多年来,这运作良好。

在同一系统中,大多数数据结构和缓冲区都是静态分配的(通常作为static文件范围内的变量)。

当然,我们有错误,有时是令人讨厌的错误,我们想快速排除缓冲区溢出的可能性。

我们的一个想法是在每个 bss-section 和 data-section 之间放置金丝雀——每个都只呈现一个符号(因为-fdata-sections)。就像编译器在激活 Stack-Smashing 和 StackProtection 时对函数堆栈所做的那样。可以通过“不时”读取金丝雀地址来从主机检查这些金丝雀。

似乎修改链接器脚本(手动放置该部分并在其间添加一个金丝雀字)似乎是可行的,但这有意义吗?

在野外有项目或文章吗?使用我的关键字我找不到任何东西。

标签: cgcclinkerembeddedbare-metal

解决方案


Canaries 对堆栈最有用,因为它的扩展和折叠超出了程序员的直接控制。你在 data/bss 上的东西不是那样的。它们要么是静态变量,要么如果它们是缓冲区,它们应该保持在它们的固定大小内,应该用算法就地的防御性编程来检查,而不是非正统的技巧。

此外,堆栈金丝雀专门用于基于 RAM 的、类似 PC 的系统中,这些系统不知道有什么更好的方法。在嵌入式系统中,它们不是很有意义。您可以做一些有用的事情:

  • 内存映射堆栈,使其增长到写入将产生硬件异常的内存区域。例如,如果您的 MCU 能够将可执行内存与数据内存分开,并且如果您尝试在数据区域中执行代码或写入可执行区域,则会产生异常。
  • 确保程序中处理缓冲区的所有内容都执行错误检查并且不会越界写入。静态分析工具通常可以很好地发现越界错误。甚至一些编译器也可以做到这一点。
  • 使用静态断言添加大量防御性编程。在编译时检查结构、缓冲区等的大小,它是免费的。
  • 运行时防御性编程。例如if(x==good) {...} else if(x == bad) {... }缺少一个else. 并且switch(x) case A: { ... }缺少一个default. “但理论上是去不了那里的!” 不,但在实践中,当您得到由错误(很可能)、闪存数据保留(100% 可能)或 EMI 对 RAM 的影响(不太可能)导致的失控代码时。
  • 等等

推荐阅读