在外国功能安全的外国人世界中的编纂者

文章作者:Chris Tapp

在各个领域,功能安全对开发人员提出了新的要求。功能安全的代码必须包括防御代码来防御…

在各个领域,功能安全对开发人员提出了新的要求。功能安全代码必须包括防御代码,以防御可能由各种原因导致的意外事件。例如,由于编码错误或宇宙射线事件导致的内存损坏可能导致根据代码的逻辑执行的代码路径。高级语言,特别是C和C ++,包括令人惊讶的特征,其行为未被代码遵守的语言规范规定。这种未定义的行为可能导致意外和潜在的灾难性结果,这在功能安全的应用中是不可接受的。由于这些原因,标准要求应用防御性编码,该代码是可测试的,即可以抵消足够的代码覆盖,并且该应用程序代码可追溯到要求,以确保系统完全且唯一地实现它们。

代码还必须实现高水平的代码覆盖范围,并且在某些部门 - 特别是汽车 - 设计需要复杂的外部诊断,校准和开发工具是常见的。出现的问题是,防御性编码和外部数据访问等实践不是编译器识别的世界的一部分。例如,C或C ++都不会对内存损坏进行任何津贴,因此除非没有这样的损坏,否则旨在可以访问它的代码,否则在优化代码时可以简单地忽略它。因此,如果不“优化”,则防御代码必须在语法上和语义可达。

未定义行为的实例也会导致意外。建议避免使用它们很容易,但要识别它们却往往很难。即使存在它们,也不能保证编译后的可执行代码的行为将符合开发人员的意图。调试工具使用的对数据的“后门”访问是该语言没有考虑到的另一种情况,因此可能会产生意想不到的后果。

编译器优化可能对所有这些区域产生重大影响,因为它们都不是编译供应商的汇款的一部分。优化可能导致显然的声音防御码被消除,其中它与“缺陷”相关联 - 该存在,其中存在于无法通过任何可能的输入值测试和验证的路径上。甚至更令人惊讶地,在构造系统可执行文件时,可能会在单元测试期间显示的防御代码。仅仅因为在单元测试期间已经实现了防御码的覆盖,因此并不能保证它存在于完成的系统中。

在这种功能安全的奇怪之地中,编译器可能是一个元素。这就是为什么对象代码验证(OCV)代表任何系统的最佳做法,其中有与失败相关的任何系统 - 而且确实对于只有最佳实践足够好的系统。

在汇编之前和之后

验证和验证措施通过功能安全,安全性和编码标准等IEC 61508,ISO 26262,IEC 62304,MISRA C和C ++所扮演的验证和验证实践在基于需求的测试期间展示了大量的重点来表明应用程序源代码的许多。

经验告诉我们,如果代码能够正确执行,那么该领域的失败概率就会大大降低。然而,由于这种值得称赞的努力的重点是高级源代码(无论哪种语言),这种方法对编译器创建精确重现开发人员意图的目标代码的能力寄予了极大的信心。在最关键的应用中,这种隐含的假设是不合理的。

它是不可避免的,对象代码的控制和数据流不是导出的源代码的精确镜像,因此证明所有源代码路径都可以可靠地锻炼,不证明对象代码的相同事物。鉴于对象代码和汇编程序之间存在1:1的关系,源和汇编代码之间的比较是讲述的。考虑图1中所示的示例,其中右侧的汇编器代码已从左侧的源代码生成(使用TI编译器禁用优化)。


图1:右侧的汇编程序代码已从左侧的源代码生成,显示源和汇编代码之间的说明比较。(来源:LDRA)

如稍后的说明,当编译此源代码时,结果汇编程序代码的流程图与源的流程码与源相比不同,因为C或C ++编译器之后的规则允许它们以任何方式修改代码,提供二进制文件表现得“好像它是一样的。”

在大多数情况下,这一原则完全可以接受 - 但有异常。编译器优化基本上是数学变换,应用于代码的内部表示。例如,如果假设不保持,则这些转换“出错” - 例如,代码库包括未定义行为的实例的情况。

只有在航空航天行业中使用的DO-178C才会侧重于开发人员意图和可执行行为之间的危险不一致的可能性 - 即使,也不难以找到解决方案的倡导者,以清楚的潜力留下未被发现的不一致。然而,这种方法是原谅的,事实仍然是,源代码和对象代码之间的差异可能会在任何关键应用中具有毁灭性的后果。

开发人员意向与可执行行为

尽管源代码流和目标代码流之间有明显的区别,但它们不是主要的关注点。编译器通常是高度可靠的应用程序,尽管与其他任何软件一样可能存在bug,但编译器的实现通常会满足其设计需求。问题是,这些设计需求并不总是反映功能安全系统的需求。

简而言之,可以假定编译器在功能上忠实于其创建者的目标。但这可能并不是完全需要或期望的,如下面的图2所示,其中有一个使用CLANG编译器编译得到的示例。


图2显示了CLANG编译器的编译(源代码:LDRA)

很明显,在汇编程序代码中尚未在“错误”函数上的防守呼叫。

state对象只有在初始化并且在' S0 '和' S1 '情况下才会被修改,因此编译器可以推断给' state '的唯一值是' S0 '和' S1 '。’编译器得出结论说‘default’是不需要的,因为‘state’永远不会包含任何其他值,假设没有损坏——事实上,编译器正是做出了这样的假设。

编译器还决定,因为实际对象(13和23)的值未在数字上下文中使用,它将简单地使用0和1之间的值来在状态之间切换,然后使用独占“或”来更新国家价值。二进制文件遵守“仿佛”义务,代码快速紧凑。在其职权范围内,编译器做得很好。

此行为对使用链接器内存映射文件进行间接访问对象的“校准”工具具有含义,并通过调试器直接存储器访问。同样,这种考虑因素不是编译器汇率的一部分,因此在优化和/或代码生成期间不考虑。

现在假设代码保持不变,但其上下文在呈现给编译器的代码中略有变化,如图3所示。


图3:代码保持不变,但其上下文呈现给编译器的代码略有变化。(来源:LDRA)

现在有一个附加函数,它将状态变量的值返回为整数。这次在提交给编译器的代码中绝对值13和23事项。即便如此,这些值也不在更新函数(其保持不变)内操作,并且只在我们的新“f”函数中显而易见。

简而言之,编译器继续(正确地)判断13和23的值应该在什么地方使用——它们绝不可能应用于所有可能使用它们的情况。

如果更改新功能以返回指向我们的状态变量的指针,则汇编程序代码会大幅度更改。因为现在alias的潜力通过指针访问,所以编译器不能再推断出状态对象发生的情况。如下面的图4所示,不能得出结论,13和23的值是不重要的,因此它们现在在汇编器内明确表示。


图4:如果更改新功能以返回指向我们状态变量的指针,则汇编程序代码会大幅度更改。它不能得出结论,13和23的值是不重要的,因此它们现在在汇编程序(源:LDRA)内明确表示。

对源代码单元测试的影响

现在考虑一个假想单元测试工具的上下文中的示例。由于需要一个工具来访问被测试的代码,状态变量的值被操纵,因此默认值没有“优化掉”。这种方法在测试工具中是完全合理的,因为测试工具与源代码的其余部分没有关联,并且需要使所有内容都可以访问,但是作为一个副作用,它可以掩盖编译器对防御代码的合法省略。

编译器识别出通过指针,同样地将任意值写入状态变量,不能得出结论,13和23的值是不重要的。因此,它们现在在汇编程序内明确表示。在这种情况下,不能得出结论,S0和S1表示状态变量的唯一值,这意味着默认路径可能是可行的。如图5所示,状态变量的操纵实现其目标,并且在汇编程序中显而易见地对错误功能的调用。


图5:状态变量的操作实现了其目标,并且对汇编程序中的呼叫函数现在显而易见。(来源:LDRA)

但是,这种操作不会出现在产品中提供的代码中,因此对error()的调用并不真正存在于整个系统中。

对象代码验证的重要性

为了说明目标代码验证如何帮助解决这个难题,请再次考虑第一个示例代码片段,如图6所示:


图6:这说明了对象代码验证如何帮助解决错误调用不存在于完整系统中的问题。(来源:LDRA)

可以说明该C代码以通过单个呼叫实现100%的源代码覆盖:

f_while4(0,3);

代码可以单行重新格式化为单个操作,并在流程图上表示为“基本块”节点的集合,每个是一系列直线代码。基本块之间的关系在图7中表示在节点之间的指示边缘。


图7:这显示了使用节点之间有向边的基本块之间的关系。(来源:LDRA)

当代码被编译后,结果如下所示(图8)。流图的蓝色元素表示未被调用f_while4(0,3)执行的代码。

通过利用对象代码和汇编程序代码之间的一对一关系,该机制公开了对象代码的哪些部分是未开发的,提示测试员设计额外的测试并实现完整的汇编代码覆盖范围 - 因此实现了对象代码验证。


图8:这显示了代码编译时的结果。流图的蓝色元素表示呼叫f_while4(0,3)尚未锻炼的代码。(来源:LDRA)

显然,对象代码验证没有任何能力,以防止编译器遵循其设计规则,并无意地绕过开发人员的最佳意图。但它可以,并且这样做可以带来任何这样的不匹配来注意令人不智能的。

现在考虑在前面的“呼叫错误”示例的上下文中的那个原则。当然,完成系统中的源代码与单位测试级别的经过验证的源代码相同,因此比较将揭示任何内容。但是,对目标代码验证对已完成系统的应用在提供基本行为被表示为打算的开发人员的保证方面是非常宝贵的。

任何世界上最好的做法

如果编译器与单元测试相比,在测试线束中不同地处理代码,那么是源代码单元测试覆盖价值?答案是一个合格的“是”。许多系统已经证明了这种文物的证据,并证明了服务安全可靠。但对于所有部门的最关键的系统,如果开发过程是为了承受最详细的审查并坚持最佳实践,那么源级别单元测试覆盖必须由OCV补充。假设它满足其设计标准是合理的,但这些标准不包括功能安全考虑因素。目标代码验证目前代表了功能安全世界最有保证的方法,其中编译行为符合标准,但最多可能产生显着的负面影响。

- Chris Tapp是LDRA的现场应用工程师

发表评论