我想将一些金丝雀分散在C程序的数据布局中的战略位置(使用GCC,用于嵌入式),以检查是否有一些指针遇到了疯狂。
如下所示:
const volatile uint32_t canary_0 = CANARY_DEF;
static uint32_t some_global_array_0[SOME_SIZE_0] = {0};
const volatile uint32_t canary_1 = CANARY_DEF;
static uint32_t some_global_array_1[SOME_SIZE_1] = {0};
...
当然我知道上面的示例不起作用,只是为了这个想法。我想在安全关键的嵌入式软件中使用类似的东西作为回退的附加点,当所有其他(例如通常的数组索引绑定断言等)无法正常运行时。
我读过this post on the GCC mailing list(我害怕认为" craft"将被解释为"飞机" ...),建议使用结构。问题是,在更大规模的情况下,感觉它会严重破坏程序结构,为此失去更多(开发人员拔掉头发并因此而制造错误),而不是通过实施这样的故障保护机制获胜。
现在我最好的选择是使用链接器脚本(无论如何都需要实现堆栈canaries)来将RAM分成不同的段,在中间散布金丝雀。然而,这感觉有些脆弱,需要维护。 (或者可能不是这样?目前我对LD的链接器脚本知之甚少,只有基本知识就足够了,比如将代码flash CRC,堆栈canaries等添加到用于micro的默认脚本中。现在我甚至缺乏如何制作灵活布局的知识,这种布局将适应分段中分配的数据量
出于我的目的,考虑线程和搞乱某些操作系统的结构并不重要(因为在出现这种情况时实际上能够回退),因为我们设计的单线程软件架构没有第三方组件。如果主线程无法绕过最终到达后备代码,则看门狗会将其杀死。让这些人参与答案是可以的,但请不要发布一个答案,说明这样的事情因为RTOS搞砸而无法工作。
答案 0 :(得分:4)
实际上,确保两个数据块(几乎达到对齐和填充)连续的唯一可靠方法是将它们放在单个聚合中(例如数组或struct
)。否则,允许编译器进行优化。
您可以考虑
volatile struct {
uint32_t my1_canary_0 = CANARY_DEF;
uint32_t my1_array_0[SOME_SIZE_0] = {0};
} my1;
#define canary0 my1.my1_canary_0
#define array0 my1.my1_array_0
所以我们正在使用结构,但上面的两个宏是"隐藏"他们!
但是,我不确定将整个struct
声明为volatile
是个好主意
(也许只有金丝雀可能是不稳定的)。它可能会显着降低您的代码速度。
重点是你在哪里以及如何检查金丝雀?你可以手动完成(但这很乏味),添加了很多
assert (canary0 == CANARY_DEF);
在许多地方(或您自己的assert
- 就像检查一样)。出于此类目的,您还可以考虑使用MELT自定义GCC
(例如,添加一个优化传递,它将在访问array0
的每个块中添加上述代码的等价物。当然,这需要几周的工作。
正如我评论的那样,最近GCC的-fsanitize=
选项也应该有所帮助。