4

我已经为安全 SIL 4 系统实现了具有大量状态转换的复杂状态机。这个实现的主干是使用函数指针完成的。当一切顺利时,V&V 反对在 SIL 4 系统中使用函数指针。参考规则 9 NASA .Misra C 2004 然而并没有说不能使用函数指针。

有没有其他方法可以在没有任何函数指针的情况下实现复杂的状态机?

4

1 回答 1

13

首先,NASA 的文件不是经典。首先询问哪些法律/指令/标准/要求/文件强制您遵循 NASA 文件。如果它没有在任何地方强制执行(这似乎很有可能,即使在 NASA 本身也是如此),你没有义务遵循它,你可以忽略整个事情。

如果不能将废话视为废话,您可以在安全标准碰壁时使用通常的程序:解决方案始终是详细记录规定的规则如何没有意义,并用他们自己的方法打他们的脸.

因此,与其放弃函数指针,不如通过下面描述的方法确保以安全的方式使用它们。


由于所有与安全相关的设计都归结为风险评估,因此您将始终拥有:

错误 -> 原因 -> 危险 -> 安全措施

使用 NASA 文档中给定的(糟糕的)基本原理,您可以通过以下方式证明“避免函数指针”的安全措施是合理的:

  1. 执行错误的代码 -> 损坏的函数指针 -> 失控代码/非法操作码

  2. 堆栈溢出 -> 函数指针递归 -> 内存损坏

  3. 困惑的程序员 -> 函数指针语法 -> 意外的程序功能

这都是相当模糊和可疑的风险评估,但这就是美国宇航局文件归结为的内容。

对于上面列出的 3 个危险,我建议不要使用“避免函数指针”,而是使用以下安全措施:

  1. 防御性编程和断言。
  2. 防御性编程和断言。教育程序员。
  3. 使用类型定义。教育程序员。

防御性编程和断言

  • 对于状态机,确保状态的数量(枚举中的项)与处理程序的数量(函数指针数组中的项)相对应。简单地静态断言枚举中的最后一项(称为STATES_N或类似的)反对sizeof(func_pointer_array)/sizeof(*func_pointer_array).
  • 函数指针数组必须在带有错误检测/CRC 的 ROM 中分配。其他安全要求将指向此需求,最简单的解决方法是使用带有“ECC 闪存”的微控制器。回到 ECC 之前的日子,您必须改为在整个 ROM 上计算 CRC,这既复杂又乏味,但也可以完成。您还可以创建表的相同 ROM 镜像以防止闪存损坏。
  • 唯一允许您的状态机代码调用的函数指针是从这个安全函数指针数组中获得的。鉴于您的状态机调用看起来像函数指针数组STATE_MACHINE[i]();在哪里STATE_MACHINE,那么只需添加一个运行时检查i以确保它始终有效。
  • 对于程序中的其他函数指针,例如回调函数,请确保将它们初始化为指向 ROM 中的有效函数。尽可能使用const指针(指针本身是只读的)。如果您需要在运行时重新分配它们,请在调用它们之前确保它们指向一个有效的函数。

上述类型的状态机是惯用的并且非常安全,可能比代码中其他地方的普通函数调用安全得多。您当然必须确保以安全合理的方式完成状态转换,但这与函数指针无关。

避免递归

这主要是关于教育程序员不要使用它,函数指针或没有函数指针(似乎这可以防止丰田错误)。

它既不难发现也不难避免递归,因此半体面的代码审查形式应该足以防止它。没有经验丰富的嵌入式系统程序员,无论对安全至关重要的系统经验如何,都不会认可包含递归的代码。

您可以/应该制定内部设计规则,规定所有与安全相关的代码必须由具有n年安全关键程序设计经验的资深 C 程序员审查和批准。

此外,您还应该使用静态分析器工具检查递归(即使它们无法通过函数指针检测递归)。如果您有一个符合任何 MISRA-C 版本的静态分析器,则包括在内。

关于无意识的递归,可以通过上面提到的防御性编程方法来避免。

Confusig 函数指针语法

诚然,C 中的函数指针语法可能非常令人困惑,请看

int (*(*func)[5])(void);

或其他一些荒谬的例子。它可以通过始终强制执行typedef函数指针类型来解决。

(参考:Les Hatton, Safer C , p184 “从与安全相关的角度来看,简单的答案是它们不应该被允许在 typedef 机制之外。”)

有两种不同的方式可以对它们进行 typedef,我更喜欢这个:

typedef int func_t (void);
func_t* fptr;

因为这不会将指针隐藏在 typedef 后面,这通常是不好的做法。但是,如果您对替代方案感到更自在

typedef int (*func_t) (void);
func_t fptr;

那也可以练习。

于 2016-01-22T15:55:04.230 回答