介绍
MISRA C 是由汽车产业软件可靠性协会(Motor Industry Software Reliability Association)提出的 C 语言编程标准,可提高嵌入式系统软件的安全性和可靠性。这些指南由汽车制造商、零部件供应商和工程咨询公司合作的汽车工业软件可靠性协会 (MISRA) 发布。MISRA C 指南最初仅针对汽车行业,发展过程中,其他产业也逐渐开始使用 MISRA C,包括航空航天和国防、工业自动化、电信、国防、医疗设备、铁路等领域。
1998 年,英国汽车工业软件可靠性协会制定了一套 127 条指南,用于在安全关键系统中使用 C。
标准那么有哪些编程标准可用呢?随着人们对这个话题的兴趣越来越浓厚,C编程标准的发布:
名字 | 程序设计语言 | 描述 |
---|---|---|
secureC | C | C 安全编码规则(ISO/IEC 17961:2013)。一套关于用 C 编程语言创建安全代码的规则。 |
CERT C | C | 卡内基梅隆大学软件工程研究所计算机应急小组 (CERT) 部门制定的安全编码标准,为 C 编程语言的安全编码提供了规则和建议(统称为指南)。 |
MISRA-C | C | 汽车工业软件可靠性协会(MISRA)的标准,使开发人员能够利用大多数 ISO C 编程语言的功能,同时帮助他们降低安全关键型汽车、医疗、工业或军事/航空应用的风险。 |
Embedded C Coding Standard | C | 此 Barr Group 编码标准旨在帮助最大程度地减少固件中的错误,它详细说明了一组使用特定 C 语言概念(如数据类型、函数、预处理器宏和变量)的指南。 |
JPL | C | 喷气推进实验室机构编码标准。基于 MISRA-2004 和一篇题为“The Power of 10: Rules for Developing Safety-Critical Code”(IEEE Computer,2006 年 6 月,第 93-95 页)的文章中列出的规则,该标准收集了用于提高用 C 编程语言编写的代码的安全性和可靠性的最佳可用见解,并对其进行扩展以解决与使用多线程软件相关的风险。 |
MISRA C 的演变
-
MISRA C:1998 – 第一版(汽车行业的原始指南)
-
MISRA C:2004 – 第二版(考虑了用户反馈和跨行业应用)
-
MISRA C:2012 – 第三版(包含对 C99 语言功能的支持,改进的强类型模型,分析关键字)
-
MISRA C:2012 (Feb 2019) – 第三版第一次修订(纳入了额外的安全准则),纳入了第 1 次修正案(AMD1)和技术勘误 1(TC1) – 也称为 MISRA C:2019
-
MISRA C:2023 (Apr 2023) – 第三版第二次修订(包含对 C11 和 C18 语言功能的支持),纳入了第 2 次(AMD2)、第 3 次(AMD3)和第 4 次(AMD4)修正案,以及技术勘误 2(TC2)。
MISRA C:2012 还包含以下补充内容: -
MISRA C:2012 - 附录 1 显示了 MISRA C:2004 和 MISRA C:2012 之间的双向规则映射。
-
MISRA C:2012 - 附录 2 将 MISRA C:2012 映射到 ISO/IEC TS 17961:2013“C Secure”规则。
-
MISRA C:2012 - 附录 3 将 MISRA C:2012 映射到 CERT C 规则。
准则介绍
MISRA C:2012基本内容
每项 MISRA C 准则(guidelines)都被分为 “规则(Rule)“或 “指示(Directive)”:
指示是指无法提供必要的完整描述来进行合规性检查的准则。为了能够执行检查,需要额外的信息,如设计文档或需求规格中可能提供的信息。静态分析工具可以帮助检查是否符合指示,但不同的工具对不符合指示的解释可能大相径庭。
规则是对要求进行完整描述的准则。检查源代码是否符合规则应该是可能的,而不需要任何其它信息。特别是,静态分析工具应该能够检查规则的合规性,但必须遵守第 6.5 节中描述的限制。
注意后续的准则、规则、指示名词
准则分类
每条准则被分为:
- 强制(mandatory):不允许违反
- 必要(required):只有在有明确限制、要求和预防措施的偏离情况下才能违反
- 建议(advisory):在合理可行的范围内遵循建议
他们的重要程度相同,区别只是是否允许偏离标准
偏离标准
允许为了某些特殊情况偏离标准,比如将 int 类型值强制转为指针来实现访问内存地址空间映射的 I/O 端口:
// 内存中的0x0002地址内数据映射了某一I/O端口数据
#define PORT (*(volatile unsigned char *)0x0002)
// 修改该位置数据就相当于修改了该I/O端口数据
PORT = 0x10u;
需要有专门的方式记录这种不得不违反 MISRA C 的地方。当然最好不要有这种违反的地方。
可判定性(Decidability)
规则(Rule)分为可判定的(decidable)和不可判定的(undecidable):
- 可判定的: 可由静态分析工具明确给出是否符合 MISRA C 的结论(是或否)
- 不可判定的: 不能给出明确结论,比如有些需要在编译、链接阶段或运行时才能分析出
分析的作用域(Scope)
规则(Rule)的分析的作用域分为单一翻译单元(Single Translation Unit)和系统(System)
也就是根据各种变量、函数的作用域来确定规则分析时的作用域。
extern void f(uint16_t *p);
uint16_t y;
void g(void)
{uint16_t x; /* x is not given a value */f(&x); /* f might modify the object pointed to by its parameter */y = x; /* x may or may not be unset */
}
多来源项目
项目中的代码来源于多个公司(组织):
- 标准库来源于编译器
- 底层驱动来源于设备厂家
- 操作系统和上层驱动来源于特定供应厂家
- 应用代码来自于其他厂家
特别是标准库和底层代码为了高性能会用到很多汇编以及偏离准则部分,这部分不需要符合 MISRA C 规范。
其他代码尽可能符合 MISRA C,如果推动第三方厂家配合较为困难,至少头文件(接口)要符合 MISRA C。
自动生成的代码
项目中自动生成的代码也需要遵守 MISRA C
准则格式
Ident | Requirement text | |
---|---|---|
Source ref | ||
Category | Category | |
Aanalysis | Decidability,Scope | |
Applies to | Cxx |
- Ident: 准则的唯一序号
- Requirement text: 准则文本
- Source ref: 参考来源
- Category: 准则分类
- Decidability: 可判定性decidability,指示(Directive)无该选项
- Scope: 分析的作用域scope,指示(Directive)无该选项
- Cxx: C 标准(C90、C99)
引用来源
ISO C
MISRA C 引用了 C90 和 C99,注意以下的一些行为:
Unspecified
: 未明确行为是指在 C 语言标准中没有明确规定其具体行为的情况。这意味着编译器可以根据实现的特定规则来定义其行为,但在不同的编译器或平台上可能会有不同的结果。如x=f(&a)+g(&a)
,f 与 g 的执行顺序是未明确的
,而且其执行顺序可能影响到 x 的最终结果。Undefined
: 未定义行为是指当程序违反了 C 语言标准规范,导致编译器无法确定其具体行为时发生的情况。例如,对指针进行未初始化的解引用、数组越界访问、除以零等操作都属于未定义行为。最重要的是编译器没有责任去检查这些错误,导致这些问题无法在编译阶段暴露。Implementation-defined"
: 实现定义行为是指 C 语言标准规定了多个可能的行为,但具体的行为由编译器或平台的实现决定。这意味着在不同的编译器或平台上,同一段代码可能会有不同的行为,但这些行为都是符合标准的。应尽量避免该行为来保证代码在不同编译器上的一致性和可移植性。Locale
: 本地化行为,和 C 语言的本地化相关,比如字符的使用习惯、日期格式等,这里不涉及。