一种缓解路径爆炸的动态符号执行方法转让专利

申请号 : CN201210106958.X

文献号 : CN102708045B

文献日 :

基本信息:

PDF:

法律信息:

相似专利:

发明人 : 张小松陈厅郭世泽吉小丽朱聪侯浩俊

申请人 : 电子科技大学中国人民解放军总参谋部第五十四研究所

摘要 :

本发明属于计算机软件安全测试领域,提供了一种缓解路径爆炸的动态符号执行方法。其旨在与提供一种解决路径爆炸问题的方法,该方法通过将符号执行产生的约束条件在程序静态决策图上的一次映射、获取过程,能够消除程序执行过程中循环产生的冗余约束条件,从而消除动态符号执行中因循环而导致的路径爆炸问题。本发明应用于软件测试。

权利要求 :

1.一种缓解路径爆炸的动态符号执行方法,包括以下步骤:(1)通过控制流图生成及化简模块获取程序对应的静态控制流图,并根据相关理论将控制流图化简为决策图,保存在决策图数据结构中;

(2)通过插桩模块对被测程序插桩;

(3)通过实际执行和符号执行模块,一方面实际运行程序,检测本次运行路径中潜在的漏洞;另一方面进行符号执行,搜集该执行路径上遇到的分支节点的条件表达式,得到约束条件;

(4)通过约束条件映射模块将符号执行收集到的约束条件保存到决策图对应的节点中,如果存在循环,则下一次迭代产生的约束条件将覆盖上一次保存在决策图中的约束条件,保证一个循环中相同的约束条件仅被保存一次;

(5)通过路径约束条件获取模块在完成一次混合执行测试结束后,即实际执行和符号执行模块执行测试结束后,按深度优先搜索策略从决策图中搜索约束条件,组成路径约束条件,输出是精简的路径约束条件;

(6)通过路径约束条件取反模块将路径约束条件的每一个表达式取反,并保存该表达式之前的约束条件,删除之后的约束条件,形成预测路径约束条件;

(7)通过求解器求解模块对预测路径约束条件求解,生成新的测试用例,该测试用例能够驱动下次测试朝不同的分支路径执行,直到所有的路径都被测试完;

所述步骤(5)中路径约束条件获取模块在一次混合执行测试结束后,按深度优先搜索策略从决策图中搜索约束条件,搜索方式如下:a)从决策图的入口点开始访问内容不为空的邻接节点;

b)从该邻接节点不为空的边中取出约束条件加到路径约束条件上,并将该边的内容删除,将该节点压入访问节点堆栈中,该堆栈中的元素不重复;

c)沿着这条边访问以它为进入边的邻接节点;

d)如果邻接节点不为决策图的出口点,并且内容不为空,转到b),如果内容为空则转f);e)如果邻接节点为决策图的出口点,则转到f);

f)如果访问节点堆栈不为空,则从堆栈中依次弹出一个节点,直到该节点的内容不为空,转到b),否则转到g);

g)本次搜索完毕。

说明书 :

一种缓解路径爆炸的动态符号执行方法

技术领域

[0001] 本发明属于计算机软件安全测试领域,提供了一种缓解路径爆炸的动态符号执行方法。

背景技术

[0002] 软件测试是验证软件质量最通用的技术。高质量的软件产品需求,促使软件测试在软件开发周期中占据越来越重要的地位。近几年发展起来的动态软件测试避免了静态软件测试人工开销大、效率低、误报率高等缺点,成为当前软件测试的主要方式。动态符号执行是动态软件测试的重要技术之一,其通过自动生成测试用例以获取高代码覆盖率,被用于当前许多主流动态软件测试工具中。例如NASA的PathFinder,斯坦福大学的KLEE,伯克利大学的CREST,微软的SAGE、Pex、Prefix等测试软件采用的都是动态符号执行技术。利用动态符号执行自动生成测试用例,在理论上能够实现对被测程序全路径的覆盖,从而全面、精确的检测程序中潜在的漏洞。
[0003] 动态符号执行通过对被测程序插装,跟踪抽象符号而非实际输入值来分析程序。在首次执行时,由用户提供输入测试用例,将输入数据符号化,符号执行伴随程序的实际执行,跟踪输入数据的流通,同时更新相应的符号,在路径分支处搜集与输入符号相关的分支转移条件表达式。一次执行过程中搜集到的条件表达式的“与”组成路径约束条件。该路径约束条件的解集与该路径有一一对应的关系。对路径约束条件的某条路径取反并用求解器求解,求解得到的数据作为新测试用例输入,能够驱动下次测试执行不同的路径,如此,直到所有的可执行路径都被测试完,最终达到对被测程序的全路径覆盖测试。
[0004] 动态符号执行的实际执行过程可用示例1简单说明,首先解释一次符号执行的过程。假设x,y是输入值,符号执行分别将其对应为符号 , 。如果初次执行对(x,y)提供的输入值为(1000,503),则执行路径为:1→2→3→4→5→6→10,第3行if条件语句得到的条件表达式为 =1000,第4行的为 <2* 。该次执行的路径约束条件将为:( =1000)Ù( <2* ),解集为: =1000, ={501,502…}。任意解集的集合(如(1000,501),(1000,502),(1000,504)等)作为输入都可使程序沿该路径执行,但用SMT求解器求解只能得到能满足该表达式的一组数据。
[0005] 1. void f(int x, int y) {
[0006] 2. int z = 2*y;
[0007] 3. if (x == 1000) {
[0008] 4. if (x < z) {
[0009] 5. assert(0); /* error */
[0010] 6. }
[0011] 7. }else{
[0012] 8. ……
[0013] 9. }
[0014] 10. }
[0015] 示例 简单程序符号执行示例
[0016] 对路径约束条件中的每个条件表达式进行取操作,可以求得可执行其它分支路径的输入数据。如对第3行的约束条件取反,并保留它之前的条件表达式(该示例没有),将其后的表达式去掉,得到的条件表达式为 !=1000。该预测路径约束条件的解可使程序沿第一个if语句的else分支执行。这里的解集为 !=1000, 为任意值,求得的解可能是(x=0,y=503)。利用以上方式,可以得到程序任何可执行路径对应的输入数据,即测试用例,测试程序的所有可达路径。
[0017] 如果采用一般的动态随机测试,给x,y赋予随机值,则要检测出第5行的错误,需要大量的无关测试,因为要保证随机取值x的值为1000,并且x<2*y这个概率还是很小的。而采用动态符号执行最多需要3次,如图1所示。从图中可以看出,如果第一次的输入值为(x=0,y=0),第一次实际执行完后搜集到的路径约束条件为 !=1000,对表达式取反得=1000,用求解器求解后得x=1000,用该值更新测试用例作为第二次的输入(x=1000,y=0)。
第二次实际执行完后搜集到的路径约束条件为 =1000)Ù( >=2* ,对表达式取反得Sx=1000)Ù(Sx<2*Sy(第一条表达式不需要反复取反),求解后得到一组满足该表达式的解(x=1000,y=501)作为下一次的输入。第三次实际执行即可触发第5行的assert(0)错误。
[0018] 动态符号执行是一种重要的动态软件漏洞检测方法。在实际执行同时符号执行,实际执行检测该路径上潜在的漏洞,符号执行能够生成驱动程序每次执行不同路径的测试用例。动态符号执行自动生成能覆盖程序所有可执行路径的测试用例,保证了测试的精确性、全面性,在理论上可以达到100%的覆盖率。与静态检测和随机检测相比具有非常高的执行效率,因此成为现在软件测试领域的研究热点。
[0019] 但是,动态符号执行还存在着很多需要被完善的地方,比如,路径爆炸、外部函数检测、浮点指针计算等。其中路径爆炸是动态符号执行当前面临的最大挑战,阻碍它实际应用于大中型应用程序的测试。在示例1中只有两个分支节点,需要测试的路径也只有3条,最多不会超过4条。但随着被测程序的增大或者循环次数的增加,整条路径约束条件长度也会线性的增加,每增加一个条件表达式,路径二叉树的高度加1,程序可执行路径最多可能增加一倍。如示例2所示,在while循环中有3条件表达式,如果y的初始值为1,那么该while循环里最少会搜集到502*2条条件表达式(第5行的if语句不一定执行到)。随着路径长度的线性增长,需要测试的路径按2的幂级数增加,当有502*2个条件表达式时,最多502*2
可以有2 条路径需要被测试,即所谓的路径爆炸,在有限的时间资源条件下,不可能测试完所有的路径,不能达到符号执行要求的精确性、全面性。引起路径爆炸的原因是路径约束条件太长,其本质因素主要有两点,一是程序太大,分支条件多;二是循环次数太大。路径约束条件与程序长度、循环三者之间存在以下近似线性关系:
[0020] 路径约束条件的长度=路径分支节点数+循环个数*(每个循环迭代次数-1)*循环内部分支节点数
[0021] 1. void f(int x, int y) {
[0022] 2. int z=2*y;
[0023] 3. while(y < 502){
[0024] 4. if (x > 1000) {
[0025] 5. if (x < z) {
[0026] 6. assert(0); /* error */
[0027] 7. }
[0028] 8. }else{
[0029] 9. z=2*y;
[0030] 10. x++;
[0031] 11. }
[0032] 12. y++;
[0033] 13. }
[0034] 14. }
[0035] 示例 带循环程序的符号执行示例
[0036] 当前国内外已提出了多种解决路径爆炸问题的方法,下面列出了几种最普遍使用的方法,并且分析它们存在的一些缺陷。
[0037] 搜索深度限制:这是在采用深度优先路径遍历策略的动态符号执行算法中采用的基本方法,也就是限制路径二叉树的搜索深度,当达到一定深度时就不再往下继续搜索。该方法没有额外的开销,但会遗失部分路径,导致测试不能覆盖到所有代码,容易遗失漏洞。
[0038] 控制循环次数:在程序运行时识别出循环,并找出循环次数控制变量,当循环次数达到给定上限时,跳出循环。该方法存在跟上面同样的问题,并且运行时循环识别开销很大。
[0039] 启发式搜索算法:每次测试完成后,对所有条件表达式取反,生成该路径上所有未被检测过的分支对应的测试用例。在第一次测试之后,每次测试都选择能够覆盖最多代码块的测试用例作为下一次的输入。该方法是针对大应用程序,采用代码覆盖率最大化的思路,延迟路径爆炸的发生。每次选择能最优测试用例开销非常。
[0040] 以上几种方法以及未列出的其他方法都存在着不完善的地方。本发明从引起路径爆炸原因之一的循环入手,对动态符号执行方法进行改进,消除循环产生的路径爆炸问题。另一方面,本发明不需要专门在运行时去识别循环,找出循环控变量(难度比较大,而且准确性不高),所以开销非常小。该发明思路来自于上面提到的路径约束条件满足的近似线性关系。分析该关系式可知,“路径分支节点数”、“循环个数”、“循环内部分支节点数”这三个变量都是程序内部固有的,不可改变。但是“每个循环迭代次数”可能受输入影响大小不定,可能是一次也可能是无限次。然而,动态符号执行只需要将所有代码都覆盖到,因此路径约束条件只须将该路径上的每个分支节点包含到即可,被测程序一次执行过程中多次符号执行同一段代码只会大大增加路径约束条件的长度,进而增加代码块的检测次数,但不能覆盖到新的代码块。

发明内容

[0041] 本发明的目的在于提供一种利用一种决策图来消除循环产生的冗余约束条件表达式,从而实现缓解因循环而导致的路径爆炸问题的一种缓解路径爆炸的动态符号执行。
[0042] 一种缓解路径爆炸的动态符号执行方法,包括以下步骤:
[0043] 一种缓解路径爆炸的动态符号执行方法,包括以下步骤:
[0044] (1)通过控制流图生成及化简模块获取程序对应的静态控制流图,并根据相关理论将控制流图化简为决策图,保存在决策图数据结构中;
[0045] (2)通过插桩模块对被测程序插桩;
[0046] (3)通过实际执行和符号执行模块,一方面实际运行程序,检测本次运行路径中潜在的漏洞;另一方面进行符号执行,搜集该执行路径上遇到的分支节点的条件表达式,得到约束条件;
[0047] (4)通过约束条件映射模块将符号执行收集到的约束条件保存到决策图对应的节点中,如果存在循环,则下一次迭代产生的约束条件将覆盖上一次保存在决策图中的约束条件,保证一个循环中相同的约束条件仅被保存一次;
[0048] (5)通过路径约束条件获取模块在完成一次混合执行测试结束后,即实际执行和符号执行模块执行测试结束后,按深度优先搜索策略从决策图中搜索约束条件,组成路径约束条件,输出是精简的路径约束条件;
[0049] (6)通过路径约束条件取反模块将路径约束条件的每一个表达式取反,并保存该表达式之前的约束条件,删除之后的约束条件,形成预测路径约束条件;
[0050] (7)通过求解器求解模块对预测路径约束条件求解,生成新的测试用例,该测试用例能够驱动下次测试朝不同的分支路径执行,直到所有的路径都被测试完。
[0051] 进一步的说,所述步骤(5)中路径约束条件获取模块在一次混合执行测试结束后,按深度优先搜索策略从决策图中搜索约束条件,搜索方式如下:
[0052] a)从决策图的入口点开始访问内容不为空的邻接节点;
[0053] b)从该邻接节点不为空的边中取出约束条件加到路径约束条件上,并将该边的内容删除,将该节点压入访问节点堆栈中,该堆栈中的元素不重复;
[0054] c)沿着这条边访问以它为进入边的邻接节点;
[0055] d)如果邻接节点不为决策图的出口点,并且内容不为空,转到b),如果内容为空则转f);
[0056] e)如果邻接节点为决策图的出口点,则转到f);
[0057] f)如果访问节点堆栈不为空,则从堆栈中依次弹出一个节点,直到该节点的内容不为空,转到b),否则转到g)。
[0058] g)本次搜索完毕。
[0059] 在上边的搜索算法中用到了两个数据结构:路径约束条件和访问节点堆栈。在动态符号执行中,路径约束条件是一个专业术语,表示所有输入符号在一次执行过程中所满足的驱使这个过程执行的约束条件的“与”。该算法中的路径约束条件用来保存检索到的所有约束条件,最后得到的是表示该执行路径的完整路径约束条件。访问节点堆栈用先进后出的堆栈方式保存访问过的所有节点。
[0060] 本发明与现有技术相比具有以下有益效果:
[0061] 本发明在现有符号执行方案基础上进行的改进。结合程序的静态决策图,利用(映射,获取)机制,消除循环对符号执行的影响,从而解决了路径爆炸产生两因素之一。本发明现有技术相比有非常明显的效果。其一,决策图一次生成后将用于整个符号执行测试中,开销非常小;另一方面,利用(映射,获取)机制消除循环产生的冗余约束条件,不会丢失任何信息,能够保证所有代码块都被检测到,保证了动态符号测试的精确性、全面性。

附图说明

[0062] 图1为例1对应的路径二叉树,从根到每个叶子节点代表不同的可执行路径。分支箭头上的表达式为条件转移符号表达式,叶子节点中的数据表示对应该路径的输入值;
[0063] 图2为混合执行测试模块装置图;
[0064] 图3为示例2对应的控制流图;
[0065] 图4为示例2对应的决策图。

具体实施方式

[0066] 本实施案例详细讲述了一种实现本发明的方式,但本发明的保护范围不仅仅局限于采用这种方式,凡是采用本发明思想的实施方式都在本发明的保护范围内。
[0067] 控制流图生成及化简模块
[0068] 本装置的功能是利用数据结构抽象被测程序的控制流图,并将其化简为决策图。输出为保存在内存中的决策图数据结构。下面简要介绍抽象及化简过程。抽象过程可以利用IDA pro进行,IDA pro是一款商业静态调试软件,可以生成可执行程序的控制流图。一个程序的控制流图可以用一个4元组G = (N, E, entry, exit)表示,N是控制流图G中节点的集合,每个节点代表程序一个基本块。基本块为程序一段连续的代码,只能按顺序从第一条语句执行到最后一条语句,然后转入其他基本块。E是控制流图有向边的集合,有向边表示两个基本块之间的控制转移。entry,exit分别表示程序的入口点和出口点。如图3是示例2的控制流图。
[0069] 只保留控制流图中的入口点,出口点,以及所有的决策点,可以将控制流图转化为决策图,如图4所示。决策图中除入口点和出口点外,其它节点都表示程序中的分支语句。因为分支语句根据条件的真假取值跳转到不同的地方执行,所以除入口点和出口点外,其它节点出度不会小于2,分别表示条件表达式取值为 “true”或“false”的边。
[0070] 约束条件映射模块
[0071] 该模块的作用是在软件测试过程中,将符号执行产生的约束条件根据它们的真假取值情况,保存到决策图中对应节点的相应边上。因为决策图中的节点与程序的分支语句有一一对应关系(除入口点和出口点),所以每条分支语句都可以在决策图中找到与它对应的节点。如示例2第4行的if语句,在决策图4中对应的节点为节点4,所以第4行收集到的约束条件将保存在节点4的数据结构中。假设分支节点的左边表示“false”,右边表示“true”,如果表达式“x>1000”成立,则保存到表示“true”的(4,5)边上,否则对表达式取反,保存到表示“false”的(4,3)边上。
[0072] 约束条件映射是消除循环产生的冗余约束条件的核心。如果程序中存在着循环,那么循环迭代产生的约束条件,在保存在决策图中时,大量存在着覆盖已保存约束条件的情况。如示例2的while循环,存在三个分支(分别为3,4,5)。第一次迭代产生的约束条件将保存到决策图中3,4,5节点上,第二次循环产生的约束条件同样被保存到3,4,5节点上,只是可能保存的边不同。
[0073] 路径约束条件获取模块
[0074] 该模块的功能是在一次混合执行测试结束后,按深度优先搜索策略从决策图中搜索约束条件,形成路径约束条件。输出是精简的路径约束条件。搜索方式如下:
[0075] a)从决策图的入口点开始访问内容不为空的邻接节点。
[0076] b)从该邻接节点不为空的边中取出约束条件加到路径约束条件上,并将该边的内容删除,将该节点压入访问节点堆栈中(该堆栈中的元素不重复)。
[0077] c)沿着这条边访问以它为进入边的邻接节点。
[0078] d)如果邻接节点不为决策图的出口点,并且内容不为空,转到b)。如果内容为空则转f)。
[0079] e)如果邻接节点为决策图的出口点,则转到f)。
[0080] f)如果访问节点堆栈不为空,则从堆栈中依次弹出一个节点,直到该节点的内容不为空,转到b)。否则转到g)。
[0081] g)本次搜索完毕。
[0082] 该搜索访问到的节点都是此次实际执行路径上的所有分支节点。在搜索过程中可能遇到两条边中都保存有约束条件的节点,访问节点堆栈就是为了保证两条边都被访问到。出现这种情况是因为该节点被不止一次被执行,如循环内的分支节点。循环的多次迭代可能会使分支节点的两条转移边都被执行,所以两条边中都会保存约束条件。如决策图4中(3,4)边保存的约束条件可能为“Sy<502”,(3,exit)边保存的约束条件可能为“Sy+1>=502”。两个约束条件都要记录到路径约束条件中,因为它们对应的路径不透。访问节点堆栈保证了所有被访问到的节点都被唯一保存到堆栈中,便于二次访问该节点,检测是否还有未被访问到的边。
[0083] 1.插桩模块
[0084] 该模块的功能是对被测程序进行运行时动态二进制插桩。在动态符号执行时,通过对被测程序插桩,可以监测符号的流动路径、操作运算,从而收集路径约束条件,是动态符号执行的一项基本且不可或缺的措施。现有的二进制插桩工具有ATOM、Dynins、Valgrind、PIN、Nirvana、HDTrans等,在本方案中采用PIN作为插桩工具。
[0085] 符号执行和实际执行模块
[0086] 本模块是将实际运行程序和符号执行同时进行,检测漏洞并且利用符号执行产生新的测试用例。该模块首次执行的输入是用户提供的数据,以后每次执行的输入都是前一次符号执行产生的测试用例。这样,混合测试仅在启动时需要用户提供输入,之后自动完成程序路径状态空间的检测。
[0087] 实际运行程序同时,会利用现有的动态漏洞检测软件检测该运行路径中可能存在的漏洞。
[0088] 符号执行将输入数据符号化,在执行过程中跟踪符号,并在分支语句上形成条件表达式。并映射到决策图数据结构中。
[0089] 路径约束条件取反模块
[0090] 该模块的功能是将路径约束条件上的每个条件表达式按照符号执行驱动算法取反。取反后的条件表达式与它之前的条件表达式组成预测路径约束条件。驱动算法可以一次取反一个条件表达式,在求解器求解后产生新测试用例,驱动下次执行。或者一次完成所有条件表达式的取反且求解,从中选出一个测试用例驱动下次执行。
[0091] 求解器求解模块
[0092] 该模块的功能是对预测路径约束条件求解,产生可以执行到不同路径的新测试用例。符号执行产生的路径约束条件由一系列条件表达式组成,可以看作SMT问题,对它们的求解就是判定这些问题是否可解,并给出一组可满足该问题的具体值。例如,表达式2 2
x-y>0,是可满足的,可给出满足该公式的一组值(2,1);x+y<0是不可满足的,不能给出一组(Sx,Sy)值,使该表达式成立。常见的SMT求解器有STP,CVC,OpenSMT,Yices,Z3等,在本模块中选择STP作为路径约束条件的求解器。该模块产生的测试用例将用来驱动下次符号执行,直到所有的路径都被测试完。
[0093] 关键词和专业术语解释:
[0094] 符号执行(symbolic execution)将输入数据映射为特定的符号,通过跟踪抽象符号建立符号表达式来分析程序,不用关心实际输入值。该符号代表可执行同一条路径的所有输入值的集合。
[0095] 代码覆盖率 覆盖即被检测到,表示已检测代码块占总代码块的百分比,主要用于软件测试中度量被测程序检测深入的程度。代码覆盖率越高,也就意味着有更多代码被检测,越容易发现软件漏洞;代码覆盖率增量也是一项度量标准,其增量越大,越能快速的发现漏洞。
[0096] 约束条件 符号在执行路径上遇到分支语句形成的满足分支转移条件的关系表达式,通常以数学表达式的方式表示。
[0097] 路径约束条件 表示所有输入符号在一次执行过程中所满足的驱使这个过程执行的约束条件的“与”。是一类可满足(SMT)问题,用求解器求解可得出满足该路径的一组数据。
[0098] 测试用例 为某个特殊目标而编制的一组测试输入、执行条件以及预期结果,以便测试某个程序路径或核实是否满足某个特定需求。
[0099] 动态符号执行 一种实际执行(concrete execution,分析实际输入值)与符号执行(symbolic execution)交错进行,自动生成输入测试用例以达到对软件全路径覆盖的动态软件测试技术,主要用于软件漏洞发掘领域。
[0100] 路径二叉树 在程序路径上,每到达一个分支语句,都有两条路径可选择,选择不同的分支就意味着执行不同的路径,动态符号执行是对程序的全路径检测,对可执行路径上的所有分支的走向形成一棵路径二叉树,从根到每个叶节点都是一条路径。
[0101] 路径爆炸问题 程序中每一个分支条件语句都可能会使当前的路径再分支出一条新的路径,并且每一个分支条件使路径二叉树的高度增1,叶节点增加一倍,使得路径成“指数级”增长,俗称“路径爆炸”。
[0102] SMT问题 SMT问题是一种一阶逻辑公式的判定问题或者可满足问题,只需判断满足或不满足,可以给出满足的一组值,不需要给出精确答案。
[0103] SMT求解器 一种数学工具,用来判定给出的条件表达式是否可满足,并且解出满足条件的一组数据。
[0104] 二进制级插桩 插桩指在程序中插入额外的代码以获得程序在执行时的行为信息。二进制级插桩指对二进制可执行文件进行插桩,不需要获得程序的源代码就可以实现的插桩方式。
[0105] 控制流图(CFG)用有向图表示一个程序过程的一种抽象数据结构。图中的节点表示一个程序基本块,基本块是没有任何跳转的顺序语句代码块。图中的有向边表示每个基本块之间的控制转移。