军工,以及交通、医疗、金融乃至航空航天相关的软件,有一个共同的要求就是“高可靠性”,以下几个关键点需要落实:
-
严格按照相关规范编写代码
-
严格遵循代码审计流程
-
分支测试覆盖率高
-
需求吻合度高
-
以上各点均须量化评估
对覆盖率、吻合度的要求往往就是 100%,这类软件有一个普遍的特点就是单元测试以及自动化测试用例的代码量远超产品的代码量,而且各项文档要求齐备。
关于审计,需要引入工业级的分析软件来量化审计各项指标,这类工具一般都是本着宁可错杀一千也不放过一个的原则,会报出很多问题,但有相当一部分问题实际并不存在,所以排查这些问题也需要相当大的人力物力。可参见国家标准《信息安全技术 代码安全审计规范》(GB/T39412)。
关于编程规范,可以参见《航天型号软件 C 语言安全子集》(GJB 5369)一类的文档,这种文档都有严格的版权限制,这里不便细说,感兴趣的可自行搜索。
规范允许的编程方式一般是编程语言某个较小的子集,一切不受开发者掌控的特性,以及未经过长时间验证的特性均不可使用,没有经过验收的、不被信赖的第三方库是绝对不可使用的,甚至标准库也是难以符合高可靠性要求的。
下面以 MISRA 规范举几个例子,MISRA 是由英国汽车产业软件可靠性协会提出的 C/C++ 语言开发标准,在嵌入式开发领域有较高认可度,是行业级规范,也受版权限制,但在业界流传比较广,这里举几个比较典型的例子。军工级、航天级的规范只会比 MISRA 更严格。
如:
if (flag_1)
{action_1();
}
else if (flag_2)
{action_2();
} // Non-compliant
这段代码看起来没什么问题,但不符合 MISRA 规范,MISRA 要求所有的 if...else-if 必须以 else 分枝结尾,即使不需要 else 分枝,也应写上空的 else 分枝,并写上为什么不需要,未来会有什么变化等注释。
又如:
#include <stdlib.h>int32_t* fn(uint32_t n)
{return (int32_t*)malloc(n * sizeof(int32_t)); // Non-compliant
}
标准库中资源管理、中断、信号管理、进线程调度一类的函数是不可以使用的。
标准库采用的算法和策略不在使用者的控制之内,很多细节在语言标准中是由实现定义的,也有很多细节是标准没有考虑到位的,有高可靠性要求的嵌入式系统应避免使用相关函数。
又如:
int32_t * fn1( int32_t & x )
{ return &x; // Non-compliant
}
这段代码返回了参数 x 的地址,即使 x 是引用,也是不符合 MISRA 规范的,虽然在 C++ 语言标准中这不是问题,但规范不允许就是不允许,绝对服从规范是高可靠性软件的编程准则!
由于各种规范都有版权限制,正版文档都有不菲的价格,奇虎 360 质量工程部融汇各种规范,提出《360 安全规则集合》,适用于桌面、服务端及嵌入式软件系统,已在Github上开源,文档全部可以免费下载,请见参见:
360 安全规则集合https://github.com/Qihoo360/safe-rules高可靠性软件的规范往往都十分严格,显得不近人情,但这种规范的制定都有合理的理由,其背后也有惨痛的经验教训,理解规范并遵循规范是值得深入学习和实践的。