网教网

搜索
查看: 110|回复: 0

运行时错误(Runtime Error)是什么?

[复制链接]

3

主题

3

帖子

9

积分

新手上路

Rank: 1

积分
9
发表于 2023-4-9 19:17:28 | 显示全部楼层 |阅读模式
运行时错误(Run-time Error)是一种跟程序运行状态相关的缺陷。这类缺陷不能通过直接禁用相关特性来屏蔽,而是需要通过分析变量的数值状态来发现可能的异常。
这里我们将浮点型比较和缓冲区溢出这两类缺陷进行比较,说明一下“运行时”的含义。其中缓冲区溢出属于运行时错误,而浮点型比较则不属于。
例1:浮点型比较错误

float a;
...
if(a == 0)
{
  ...
}
例 1 中第3行代码使用浮点型变量 a 与 0 直接相比,由于浮点型自身特点,所以无论 a 如何取值,第3行的比较都会导致程序异常。因此检测该类缺陷时仅需在相关表达中检测变量类型是否为浮点型,如果发现则直接产生告警。例2:缓冲区溢出
int a[10];
for(int i = 0; i <= 10; i++)
{
  a = 0;
  ...
}例 2 中第4行代码对数组 a 的第 i 个元素进行了赋值。该语句是否发生错误,无法简单地通过分析变量类型得出,而是需要综合分析数组 a 的长度与变量 i 在运行时可能的取值。
根据第1和第2行,我们可知数组a长度为10,i可能的取值区间为 0-10,即 i 的取值可能大于 a 的长度。因此第4行存在错误,需要产生告警。
通过对例1和例2中缺陷判定的过程进行比较,我们可以发现运行时错误的产生依赖于变量运行时的取值,检测这类缺陷需要进行较为复杂的数值或状态分析。


相关指南要求

因为运行时错误会严重影响程序的可靠执行,所以一些国际通用的安全编程指南都对这类问题进行了要求,其中MISRA C 编程指南的Dir 4.1中,要求要尽量减少运行时错误;MISRA C++ 编程指南也要求尽量减少运行时错误,并且列出了可供选择的技术方式:包括:静态分析、动态分析和编写专有检查代码。
为了更详细地说明运行时错误这类缺陷,MISRA C 和MISRA C++指南都对这类问题进行了分类。其中MISRA C将运行时错误分为6类;MSIRA C++分为5类,具体内容与MISRA C 的前5类一致。
以下是MISRA C指南的详细分类说明:
运算错误(arithmetic errors)

运算错误主要指在表达式求值时发生的错误,包括:溢出、下溢、除零或由于移位导致丢失有效位等。对于无符号整数,不会发生严格意义上的整数溢出,而是会以确定的方式产生截断之后的数值。而截断值可能会与开发者预期不一致。
针对这类错误,在使用算术表达式时,需要仔细检查运算值的范围。
指针计算(pointer arithmetic)

指针计算主要由指针类型变量加减等计算操作导致。如果计算结果超过指针指向对象的范围,就会使得指针将不再指向原有对象。此时如果使用该指针,则会导致未知的异常行为。
数组边界错误(array bound errors)

数组越界是由数组索引变量取值超过数组范围导致的。当索引取值超过数组定义的范围时,如果使用该索引值对数组进行操作,则会导致对数组以外的位置进行读写,从而发生未知的异常行为。
函数参数(function parameters)

函数参数错误是指使用 C 或 C++库函数时,由于传入参数数值或状态不满足使用要求,从而导致的异常错误。
例如,字符串拷贝函数 strncpy 时需要三个函数:目的字符串指针destination、源字符串指针source和操作长度num。该函数会将从destination指针指向地址开始,向source指针指向地址拷贝num个字符。如果source指向缓冲区长度小于num,则会导致source指向缓冲区以外的内存被意外写入数据,从而产生异常错误。
指针解引用(pointer dereferencing)

指针解引用问题发生在使用指向NULL的指针时。
当对指向NULL的指针进行解引用操作并执行读写操作时,本质上是对地址为0的内存数据进行读写。此时,如果运行操作系统带有分页保护机制,则会导致段错误(Segmentation Fault);如果运行操作系统没有分页保护机制(通常为嵌入式操作系统),则会导致未定义的错误。
动态内存(arithmetic errors)

虽然在MISRA C 和 C++ 指南中要求禁用动态分配的内存,但是MISRA C 指南考虑到可能部分开发者依旧会使用这一特性,因此也将该特性可能会导致的问题归为运行时失败。
在程序真实运行时,操作系统不能保证每次动态分配内存都能成功。
如果内存动态分配部分或彻底失败,而后续代码在没有检查返回地址有效性的前提下,直接使用了返回地址,该类问题就会发生。此时返回地址并不指向开发人员预期的缓冲区,直接使用该地址就会导致未知错误的发生。
针对这类问题,指南要求编写代码检查内存分配是否成功,并根据返回结果编写异常处理逻辑。
如何减少


由于运行时失败这类缺陷与变量运行时的数值或状态密切相关,因此检测该类缺陷需要复杂的推理和分析。所以传统的人工走查和简单的模式匹配工具都很难有效地发现这类缺陷。同时C和C++语言编译器为了保障生成代码的速度,其内置的基础检测都比较简单,因此也无法有效识别这类缺陷。
针对运行时失败这类缺陷,MISRA C 和 C++指南提出了两种应对方式:第一,在每次使用相关变量前加入检测代码,确保对应变量的取值或状态位于合理的范围内。第二,在软件开发过程引入专业的运行时错误检测工具,例如源代码静态分析工具。这些工具将利用自身特有技术对软件代码进行分析,检测出潜在的运行时错误。第一种应对方式需要加入额外代码。这使得该应对方式无法保证对每个变量操作都能添加合理的检测代码,且这些额外引入的检测代码会拖慢程序的运行效率,甚至引入额外的缺陷。第二种应对方式不需要修改原有代码,不会对原有程序带来额外的影响。因此较好的方法是以第二种应对方式为主,同时在在程序的个别关键位置使用第一种方式。

【缺陷捕手】微信公众号开启了编程规范解读系列。将对包括MISRA CMISRA C++AutoSar C++等经典编程规范进行逐条解读。
欢迎大家关注,一起学习软件缺陷知识,搞定 C/C++ 安全编程
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表