状态机没有function指针
我已经实现了一个复杂的状态机,具有许多状态转换,用于安全SIL 4系统。 此实现的后骨是使用函数指针完成的。 当一切顺利航行时,V&V反对在SIL 4系统中使用function指针。 参考 – 规则9 NASA .Misra C 2004然而并没有说不能使用函数指针。
有没有其他方法可以在没有任何函数指针的情况下实现复杂的状态机?
首先,美国宇航局的文件不是正典。 首先询问强制您遵守NASA文件的法律/指令/标准/要求/文件。 如果它没有在任何地方执行(这似乎很可能,即使在NASA本身),你没有义务遵循它,你可以解雇整个事情。
没有把废话当作无稽之谈,你可以在按照安全标准打墙时使用通常的程序:解决方案总是要详细记录所述规则没有意义,并用他们自己的方法对他们进行打击。 。
因此,不要放弃函数指针,而是通过下面描述的方法确保以安全的方式使用它们。
由于所有与安全相关的设计都归结为风险评估,因此您始终可以:
错误 – >原因 – >危险 – >安全措施
根据美国国家航空航天局文件中给出的(差)理由,您可以certificate安全措施“避免使用函数指针”,例如:
执行了错误的代码 – >损坏的函数指针 – >失控代码/非法操作代码
堆栈溢出 – >函数指针递归 – >内存损坏
困惑的程序员 – >函数指针语法 – >非预期的程序function
这完全是模糊的,也是一个值得怀疑的风险评估,但这正是NASA文件归结为的。
对于上述3种列出的危险,我建议使用以下安全措施代替“避免function指针”:
- 防御性编程和断言。
- 防御性编程和断言。 教育程序员。
- 使用typedef。 教育程序员。
防御性编程和断言
- 对于状态机,请确保状态数(枚举中的项)对应于处理程序数(函数指针数组中的项)。 简单地静态断言枚举中的最后一项(称为
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
指针(指针本身是只读的)。 如果需要在运行时重新分配它们,请确保它们在调用它们之前指向有效的函数。
上述类型的状态机是惯用且非常安全的,可能比代码中其他地方的普通函数调用更安全。 您当然必须确保以安全合理的方式完成状态转换,但这不是涉及函数指针的事情。
避免递归
这主要是关于教育程序员不要使用它,函数指针或没有函数指针(似乎这会阻止丰田的bug)。
既不难发现也不避免递归,因此半正式的代码审查手续应该足以阻止它。 无论安全关键系统经验如何,没有经验丰富的嵌入式系统程序员都会批准包含递归的代码。
您可以/应该设置一个内部设计规则,声明所有与安全相关的代码必须由具有n年安全关键程序设计经验的资深C程序员审查和批准。
此外,您还应该使用静态分析器工具检查递归(即使它们无法通过函数指针检测递归)。 如果您的静态分析仪符合任何版本的MISRA-C,则包括此内容。
关于非预期的递归,使用上述防御性编程方法可以避免这种情况。
Confusig函数指针语法
C语言中的函数指针语法无疑是非常令人困惑的,只需看一下即可
int (*(*func)[5])(void);
或者其他一些荒谬的例子。 它可以通过始终为函数指针类型强制执行typedef
来解决。
(参考:Les Hatton, Safer C ,p184“从安全相关的角度来看,简单的答案是它们永远不应该被允许在typedef机制之外。”)
你可以通过两种不同的方式来定义它们,我更喜欢这样:
typedef int func_t (void); func_t* fptr;
因为这不会隐藏typedef后面的指针,这通常是不好的做法。 但如果你觉得替代方案更舒服
typedef int (*func_t) (void); func_t fptr;
那也没关系。