概述
在高质量、高安全性的产品中,软件占据了越来越重的比例,从需求分析阶段,到软件研制,各级测试过程中,都需要尽量避免软件问题的发生。一方面从设计上规避,一方面对软件产品进行充分的测试,尽可能早的发现隐藏在软件中的缺陷,避免将软件问题在大批量生成之后,造成严重的后果和损失。
软件测试是有效发现软件缺陷的重要方法之一,嵌入式软件测试按照是否执行被测系统,将测试类型分为静态测试和动态测试:
静态测试不需要编译执行源程序,对源程序进行词法语法、编程规范、数据流、控制流、度量等分析,获取程序的结构和特性,利用形式化方法验证、证明程序是否符合安全规则,能够较为全面的获取程序的特征。
动态测试需要通过获取程序的动态信息来分析软件的缺陷,如分析程序的内存状态、覆盖率和执行结果,更有利于理解程序的动态行为特征。
很多代码缺陷产生于程序运行过程中,具有隐蔽性和不可预见性,比如数组越界,动态存储分配,内存溢出,指针非法引用,类型不一致的隐式转换等,这些错误都是在编译阶段不能被编译器检查发现的错误。在大规模,复杂度极高的软件中,完全依赖于人工检查方式可能会因为人为等主观原因造成问题被遗漏无法发现。将现有常见多发问题形成缺陷模式库植入工具中,由工具自动化检测代码缺陷,能够高效提升测试效率,避免问题重复发生,减轻测试人员工作量。
缺陷模式规则分类
2.1规则分类
2.1.1按照错误类别分类
为了进一步研究软件错误发生的机理,分布情况,针对C语言典型缺陷模式库中的规则,按照规则检查类型对其进行分类。
(1)数值检查:除零错误、数组越界。
(2)指针使用检查:空指针解引用。
(3)数据流检查:变量定义未使用就再次赋值。
(4)控制流检查:ifelseif分支语句末尾缺少else分支。
(5)初始化检查:使用前未初始化变量。
(6)类型转换检查:数据类型不一致引入的隐式类型转换。
(7)操作符使用不当检查:关系表达式、逻辑表达式、条件判断语句中的控制表达式禁止为赋值表达式“=”、使用连续的比较运算,可能导致代码执行逻辑与预期不符。
2.1.2按照严重程度分类
根据故障出现可能引起的错误严重程度,对软件或者系统造成影响的严重程度,给每条规则定义了优先级高、中、低的属性。
(1)重要:除零错误;数组越界;空指针解引用。
(2)中等:使用前未初始化变量;数据类型不一致引入的隐式类型转换;关系表达式、逻辑表达式、条件判断语句中的控制表达式禁止为赋值表达式“=”;使用连续的比较运算,可能导致代码执行逻辑与预期不符。
(3)轻微:ifelseif分支语句末尾缺少else分支;变量定义未使用就再次赋值。
2.2规则解析
2.2.1数值检查
错误发生在不断运行过程中,称为运行时错误,一旦程序中出现该类型错误,会导致计算得到的实际结果与预期结果完成不同,甚至引发计算机复位。数值检查作为运行时错误检查的重要检查内容,会出现在某些特定的运行条件下,即便是经过严格测试的程序,仍有可能存在非预期的浅通路,引发软件不安全问题。
(1)数组越界
通过数组的下标来得到数组内指定索引的元素,称作对数组的访问。如果一个数组定义为n个元素,它占用一块连续的内存空间,对n个元素(下标为0到n-1)的访问都合法,如果对这n个之外的元素(如下标n)进行访问,访问到的是其它变量,是非法的,称为“越界”。数组越界在运行时的表现是不确定的,可能不会造成严重后果,也有可能导致程序崩溃。因此在使用数组时一定要检查访问是否越界,以保证程序的正确性。代码示例见表1。
表1数组越界代码示例
(2)除零错误
出现除零操作时会导致计算结果为一个极大值,超过数据类型能够表示的最大范围,就会发生溢出,计算机程序对于溢出的防护处理可能是计算机复位。因此代码中将整数和浮点数作为分母时都应该进行保护,防止除零后数据溢出的异常情况发生。代码示例见表2。
表2除零错误代码示例
2.2.2空指针解引用
空指针解引用是一种常见的动态内存错误。指针变量可以指向堆地址、静态变量和空地址单元,当引用指向空地址单元的指针变量时,就会产生空指针引用故障,导致不可预见的错误,系统崩溃或者异常复位。因此,在解引用指针前,应先判断是否为NULL,如果是NULL则不要解引用。代码示例见表3。
表3空指针解引用代码示例
2.2.3变量定义未使用就再次赋值
这条规则属于数据流分析规则,主要