一种检测内存边界溢出错误的方法与流程

文档序号:14774085发布日期:2018-06-23 02:32阅读:310来源:国知局
一种检测内存边界溢出错误的方法与流程

本发明涉及一种检测内存边界溢出错误的方法,属于计算机领域。



背景技术:

在日常生活中,各种各样的软件正以前所未有的速度融入到我们的日常生活中。而软件已经成为整个社会运行必不可少的一部分,银行,通信,教育,交通,乃至核工业等关键性行业都在利用软件来推动行业和社会的发展。然而,近年来我们的软件系统的大小和复杂度呈现出迅猛增长的态势,而且这种趋势在未来仍然不会停止。随着社会对软件依赖性的愈发严重,软件失效可能会给商业系统,甚至是安全关键系统带来巨大损失。因此,软件的可靠性正在成为软件行业发展中不可忽视的一个重要方面。

软件可靠性是指在特定环境以及特定时间段内软件保持运行不失效的概率,而软件可靠性工程是指利用一些工程上的技术来开发和维护软件系统,使得这些系统的可靠性能够进行量化的评价。按照错误产生的周期可以概括出提高软件系统可靠性的四个主要方面:通过规范开发进行错误预防和错误避免;通过验证和确认技术实现错误检测和移除;通过冗余设计使系统服务在错误发生情况下依然满足要求;通过软件可靠性模型等对错误存在性,未来失效发生可能性和失效影响等进行预测。其中软件验证技术一直是研究的重要方面,现有的软件验证技术主要包括如下几种,静态分析,模型检测,软件测试。

静态分析指的是通过类型推断,数据流分析和约束分析等找出可能存在的错误,优点是可以在系统开发早期发现错误,但同时也会带来大量的误报和漏报;模型检测通过对软件结构的分析,将系统转化成有限状态空间模型,并利用算法验证其完备性,优点是模型检测是可证明的,缺点是形式化验证会带来状态空间爆炸问题,验证需要时间较长且模型的行为和系统的实际行为之间会存在一定的差距;软件测试是指通过设计测试用例,并在此基础上运行程序,观察程序的行为和结果,优点是实现了测试用例管理和执行的自动化,缺点是测试用例无法实现全覆盖,错误会出现遗漏,另外验证性质的描述采用的是直接定义的方式,无法通过形式化表达自动生成,限制了验证的能力。

和前述四种主要在软件开发过程中应用的验证技术不同的是,运行时验证是一种在软件实际运行阶段进行性质分析验证的技术,作为一种新近提出的轻量级验证技术,能够实现对软件运行的实时监控,验证软件是否符合或违反给定的正确性属性,对提高软件的可靠性有重要的意义。运行时验证是运行时监控技术和形式化规约技术的结合体,其通过监控软件运行过程获取实时状态,避免了模型检测中形式化验证带来的状态空间爆炸和复杂度问题,并考虑到了实际运行过程的环境等影响因素,同时运行时验证采用形式化语言对待验证性质进行描述,例如线性时序逻辑,正则表达式,自动机等,使运行时验证对性质表达更加丰富和灵活。运行时验证在性质分析验证的基础上,往往还支持对程序运行流程的控制,当程序运行进入某种错误状态,可以引导程序进入安全状态或者直接终止程序的运行,避免程序的错误运行带来更大的损失或者严重的事故。目前运行时验证技术已经被广泛应用在硬件运行控制Web应用和Android应用的监控,网络等各个领域。

在运行时验证实现的关键技术中,主要包括两个部分,一个是待验证性质的描述和相应验证器的生成,二是监控器和验证器的系统集成。在性质描述方面,运行时验证采用了和模型检测相类似的技术,通过线性时序逻辑,参数化命题,正则表达式,自动机等逻辑语言进行描述。在验证器生成方面,验证器是指一类特殊的组件,可以读取一个有限事件序列,并给出一个特定的结论,验证器是依据用户所指定的性质自动生成,并随着对应的执行代码集成到待监控的系统当中,目前验证器主要采用基于自动机的验证算法和基于公式重写的验证算法生成。由于在性质描述和验证器生成方面,部分继承了模型检测中的内容,目前已有大量的成熟理论或算法可供参考,具有普遍的语言和算法通用性;而在监控器和验证器的系统集成方面,对应不同的开发平台,不同的目标语言,不同的性质描述规范会带来集成方法的不同,集成方法的设计反过来又会影响运行时验证本身性质的表达能力。因此,如何设计并实现运行时验证监控器和验证器的高效系统集成,成为运行时验证领域研究的一个重要和热点的方面,对于推动运行时验证在实际系统中的应用具有重要价值。



技术实现要素:

为解决上述问题,本发明提供一种检测内存边界溢出错误的方法。

本发明提供的检测内存边界溢出错误的方法包括如下步骤:

步骤一:编码器读取待检测的源代码并将其转变成中间代码;

步骤二:分析中间代码,采用基于对象的边界检测技术完成对象的对齐分配并实现边界表;

步骤三:在中间代码中插入检测函数,执行边界检查;

步骤四:通过分析优化,简化冗余操作,生成优化后的中间代码;

步骤五:将优化后的中间代码链接到二进制的库文件,生成可执行文件;

步骤六:运行可执行文件,检测函数会在指针解引用其所指向对象之前判断当前指针是否越出对象边界,当遇到有指针越界,缓冲区溢出等内存安全错误,程序会调用终止执行并报错。

在一种实施方式中,所述步骤二中采用基于对象的边界检测技术完成对象的对齐分配并实现边界表的具体过程为:

首先,以对齐方式分配对象:对象的对齐分配直接利用内存分配器对程序产生的对象进行边界控制,利用二进制的内存分配器来满足内存对象的填充与对齐存储;

然后,边界表的实现方法:在程序对象分配的时对对象的边界进行控制,通过填充使得对象所占内存为2的幂,在边界表中存储的是分配大小的对数的二进制表达(e=log2(size)),通过(size=1<<e;base=p&~(size-1);)操作还原出对象的分配大小和指向分配地址开始位置的指针。

在一种实施方式中,是使用连续数组来实现边界表。

在一种实施方式中,所述步骤三中在中间代码中插入检测函数,执行边界检查的具体操作如下:

使用p指针的值和分配大小e的对数的二进制值,其中的e是从边界表中获取的,以此保证如果q和p只在最低有效位有区别那么q指针就在边界内部。

在一种实施方式中,所述步骤四中具体优化步骤如下:

首先,由之前第三步已经在中间代码中为每个指针运算都添加了检查代码;

其次,对存在于循环体内的所有指针运算进行最大范围的值域分析。

在一种实施方式中,最大范围指的是在一个循环体内部的多个指向同一缓冲区的指针变量的最大指向范围。

在一种实施方式中,所述步骤六中检测函数通过检查指针当前的指针地址是否在该指针所指对象的内部,对象的地址是通过边界表查询得到。

在一种实施方式中,所述待检测的源代码为C或C++语言。

本发明提供的检测内存边界溢出错误的方法,使用一种规整的存储对象的方法,方便获取并存储对象的边界信息;实现更高效占据内存更小的边界表,存储对象分配边界大小的对数;通过判断指针所指向地址的对数的二进制的值来确定指针是否边界溢出;通过以更弱前提为核心的循环检查优化方法,提前发现边界溢出的指针,提高检测效率,能够快速的对软件进行验证;针对不同硬件的系统,如32位或64位的,可以调整对象分配的大小以达到最高的内存利用效率,减少内存浪费。本发明支持C语言和C++语言程序的内存溢出错误的高效检查,由于对象是以对齐方式存储的,这样既可以省去对边界值的查询时间,也可以省去存储常规对象地址的内存空间,能够解决所有的空间内存安全问题,从而有效的提高软件的可靠性。

附图说明

图1为本发明实施例一提供的检测内存边界溢出错误的方法流程示意图;

图2为对象的实际分配边界与对象大小的关系图。

具体实施方式

以下结合附图和具体实施例对本发明提出的一种检测内存边界溢出错误的方法作进一步详细说明。根据下面说明和权利要求书,本发明的优点和特征将更清楚。需说明的是,附图均采用非常简化的形式且均使用非精准的比例,仅用以方便、明晰地辅助说明本发明实施例的目的。

实施例一

在实际的对象内存分配中,使用大小固定的分配边界而不是对象的实际大小作为边界。这里,我们将这种方法用于高效的向下兼容边界检查。我们使用内存分配器(如二进制BD分配器)来让分配的边界足够的简化:由于所有的分配大小都是2的幂,因此单个字节足以存储二进制对数的分配大小。进一步来说,因为一个大小为S的分配的基地址可以通过清除任何指向已分配内存区域的指针的最低有效位(log2(s))计算出来,所以没必要去存储一些没必要的信息。这样我们就可以为边界信息表设计一个在空间上和时间上同时有效的数据结构。本方法中使用一个连续数组而不是十分耗费内存的数据结构(如展开树)。

本实施例一的内容如下:

步骤一:编码器读取待检测的源代码并将其转变成中间代码;所述待检测的源代码为C或C++语言。

步骤二:分析步骤一中生成的中间代码,完成对象的对齐分配并实现边界表。本步骤中采用的是基于对象的边界检测技术,基于对象的含义就是在边界表中记录每个对象的边界信息。基于对象的方法是对含有指针运算的程序语句上执行检测。它使用源指针在边界表中查询对象边界信息,并且检测目标指针是否还在边界内部。如果目标不是指向它应该指向的对象,就将这个指针标记为边界溢出指针,并且拒绝该指针的任何解引用请求。但是我们会允许这个非法指针进行进一步的指针运算,因为运算最终可能得到一个在边界内部的指针。

具体过程如下:

(1)以对齐方式分配对象:对象的对齐分配可以直接利用现有的内存分配器(如buddy分配器)对程序产生的对象进行边界控制。利用二进制的内存分配器来满足内存对象的填充与对齐存储,通俗来说就是将内存分成一个个大小相同的块(slot),每个块的大小(slot-size)都是2的幂。这就保证了下一步的边界表能够高效的存储边界信息。图2为对象的实际分配边界与对象大小的关系图;

(2)边界表的高效实现方法:使用非常简洁的方式来表示边界信息。在程序对象分配的时候就对对象的边界进行控制,通过填充使得对象所占内存为2的幂。这样就使得边界表中存储的是对象的分配边界而不是对象实际边界。原本存储对象的基地址和它的大小至少需要八个字节,而本实施例一中的边界表只需要一个字节就可以记录边界信息,从而可以大大降低内存的开销。我们在边界表中存储的是分配大小的对数的二进制表达(e=log2(size))。有了这条信息,我们可以用(size=1<<e;base=p&~(size-1);)操作还原出对象的分配大小和指向分配地址开始位置的指针。

本发明中使用连续数组来实现边界表,因为表内的每个条目都仅仅使用一个字节,所以边界表很小。由于上一步中对象都是以固定块大小进行存储的,并且每个块的大小slot-size都是2的幂,每个对象所占据的内存块都在边界表中有一个对象条目,因此表所占的空间是原本的1/slot size,并且我们可以调整slot-size大小来平衡内存浪费程度和表大小。

步骤三:在中间代码中插入检测函数,执行边界检查。由于本步骤三是基于对象的,并不需要关注每个指针,只需要关注每个指向当前对象的指针以及其算数运算是否仍在该对象的边界内部。

一般而言,检查一个关于p指针的算术运算(q=p+i)的结果q的边界需要检查q是否在上下边界内。本实施例提供了一个不需要计算对象的上下边界的优化的边界检查方法。这个方法只需要使用p指针的值和分配大小e的对数的二进制值,其中的e是从边界表中获取的。这种对分配大小和对齐的控制可以保证:如果q和p只在最低有效位有区别,那么q指针就在边界内部。

进一步来说,对于指针q其中sizeof(*q)&gt;1,我们也需要检查(char*)q+sizeof(*q)–1的值是否在边界内,以预防后续的绕过分配边界直接访问*q。宽松边界检查可以避免检查q指针指向一个内置类型这种情况。对这些对齐的类型的访问不会发生重叠,因为它们的值都是2的幂且都小于slot-size。

以一个指针操作(char*p=buf[i])为例:

第一步,右移源指针buf以获取与其对应的对象在边界表中的边界。

第二步,将分配大小e的对数从边界表载入到寄存器al中。

第三步,将指针运算的结果与源指针buf做异或运算。

第四步,通过al右移以去掉低位。

如果buf和p都在分配的边界内部,那他们只在log2e的最低有效位有区别。

步骤四:通过分析优化,简化不必要的冗余操作,生成优化后的中间代码。在边界检查中用到的典型优化方法包括:消除冗余检查,提取循环内部检查或者是仅仅提取出循环内的边界表查询操作。优化内部循环可以有效的提升性能。当一个循环内的所有访问都是同一个对象我们就尝试将边界表查询操作从循环内部提取出来。具体优化步骤如下:

a.由之前的几个步骤已经中间代码中为每个指针运算都添加了检查代码。

b.对存在于循环体内的所有指针运算进行最大范围的值域分析。最大范围指的是在一个循环体内部的多个指向同一缓冲区的指针变量的最大指向范围。但是这些指针变量的范围是不一样的,假设指针p的范围为a[0]~a[i-1],指针q的范围为a[1]~a[i+1],对这些指向同一缓冲区的数组指针的范围取并集,就可以得到最大范围a[0]~a[i+1],多个指针的最大范围分析和上述过程一样。例如在下面的一个循环示例中,如果L足够的大,不优化的代码的执行将会带来非常大的运行时开销。在C语言中a[i]就相当于一个指针,我们可以在循环开始前判断a[i]的最弱的边界溢出条件,在本例中a[i]的范围为a[1]到a[L]。如果a[i]连这个最弱的条件都不满足即说明其已经溢出,则终止执行。如果满足了最弱条件则执行循环体,在循环体内部判断是否存在内部溢出。

c.将同源对象的指针的最弱条件合并。如上面代码中的a[i]和a[i-1],a[i]的最弱条件范围为a[1]到a[L],a[i-1]的范围为a[0]到a[L-1],合并后的同源最弱条件则为a[0]到a[L]。

通过这样的进入循环体前对对象指针的范围进行分析,可以简化很多不必要的冗余操作。当静态分析能确定循环体内指针值的大概的边界,那么将整个检查从循环体内提取出来是非常有效的。但是只有当静态分析能确定这些边界在每一步执行中都能被访问到时才能将这些检查从循环体内提取出来。一个循环体的边界可以很容易确定但是循环可能会在到达上边界前就终止了。在循环跳出前溢出了对象边界,这种情况下将一个检测从循环内取出就会在运行时抛出一个警报。

步骤五:将优化后的中间代码链接到二进制的库文件,生成可执行文件。

步骤六:用户运行已插入检测函数后的可执行文件,当遇到有指针越界,缓冲区溢出等内存安全错误,程序会调用终止执行并报错。在第三步中所插入的检测函数会通过检查指针当前的指针地址是否在该指针所指对象的内部,对象的地址是通过边界表查询得到的。如果在边界内部则继续执行下面的语句,如果不在内部则说明该指针已溢出,执行终止程序并报错

本实施例一提供的支持所有针对C和C++的向下兼容边界溢出错误的检查。该方法将源代码转换成一种中间代码表示(IR),并寻找潜在的含有不安全指针运算的操作语句,并且插入检查代码来保证他们的结果始终在边界内部。然后,将IR链接到我们的运行库和二进制库,从而产生健壮的可执行文件。

虽然本发明已以较佳实施例公开如上,但其并非用以限定本发明,任何熟悉此技术的人,在不脱离本发明的精神和范围内,都可做各种的改动与修饰,因此本发明的保护范围应该以权利要求书所界定的为准。

当前第1页1 2 3 
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1