一种程序静态自动分析方法与流程

文档序号:15298496发布日期:2018-08-31 19:50阅读:251来源:国知局

本发明涉及一种基于数据流分析手段和逻辑推理系统对程序正确性进行自动分析的方法,该方法能够运用于程序的静态分析和验证,代替人工自动地进行程序执行结果可能性的分析,保证分析结果的精确性和完整性,属于软件验证技术应用领域。



背景技术:

软件可靠性是指通过测试、验证、模型检验等多种手段保证程序一定程度上的正确性的方法。程序员编写代码后需要对其进行功能正确的保证,可以通过执行测试用例来观察程序执行结果是否符合预期,也可以通过模型检验技术来分析系统的执行流程是否会导致不安全的状态,对于比较复杂的程序性质,经验不足的程序员难以构造足够有效的测试用例集,容易遗漏潜在的问题,因此对于系统安全攸关的程序,需要对源代码代码分析其执行过程可能产生的性质,通过计算类似算法的循环不变式、语句执行效果的断言的方式来保证代码逻辑满足要求。

代码静态分析验证技术主要基于形式化的逻辑系统来进行程序的性质的推导,如霍尔逻辑,是一种使用语句执行前后程序性质的方式来描述程序状态的变化,它定义了一套逻辑推理规则来描述在基本程序结构如赋值、循环、分支的条件下如何进行程序性质的推理。霍尔逻辑的问题在于无法处理多个变量绑定在同一个内存地址上的情况,因此,后人在其基础上提出如分离逻辑等扩展逻辑系统,定义了程序的堆栈结构和相应的分离算子,并扩展和修改了部分的推理规则,具备了能够处理指针别名的能力,但是分离逻辑过大地改动了逻辑推理的过程,使得推理过程过于复杂,因此后来又有了一些逻辑推理系统较为简单的内存模型,使用较少的逻辑推理规则的扩展具备了相同的内存分析的能力,他们共同的特点是可以对程序语句和表达式所涉及到的变量的实际内存地址进行建模。

数据流分析(Data Flow Analysis)是一种常用于编译优化领域的分析技术,通常作用于编译中间结果的基本块上,通过对基本块构建控制流图,通过求解控制流图节点上的约束方程来不断动态更新特定的数据流值,直到某次对所有节点更新求出的流值都不发生变化为止,得到最终收敛的数据流值。典型的应用实例如到达定值分析、常量传播分析等。



技术实现要素:

技术问题:本发明提出了一种基于形式逻辑系统和数据流分析理论进行程序性质自动化验证的方案。首先由分析人员根据需要分析的性质定制出相应的函数表示和函数性质,将待分析的性质的取值集合以交半格的形式进行描述,并与包含函数的公式一一对应,对程序性质的更新以规则的形式进行描述,并以此为依据实现数据流分析算法框架上的具体操作,系统将自动执行数据流分析算法,根据给出的初始条件,通过逻辑系统进行推理并迭代直至收敛,并将最终得到的结果以逻辑公式的形式保存在程序执行路径上的每个节点中。本发明通过自动化的数据流分析手段减少了用户手工验证的开销,并且避免了用户手工验证容易错漏的情况。同时以公式及其证明方式和依赖关系的形式给出分析结果,便于与其他证明过程协作。

技术方案:本发明提出的基于逻辑系统和数据流分析理论进行程序性质自动化分析,可以自动设置分析结果的性质和依赖关系,解决了手工程序验证困难和易出错的不足。本方法主要分为三个步骤:首先由用户定义严格的递归函数及其性质,用以描述用户待分析的性质,并将待分析的性质的取值集合和其上的交汇运算使用交半格进行描述,并且说明性质的不同取值对应生成哪些包含递归函数的逻辑公式,其次给出数据流值在经过程序的语句节点时更新的方法,最后,将前两步定义的规则编码实现为数据流分析框架中的可扩充元素,给出程序开始执行的初始条件,利用数据流分析的收敛算法自动进行分析,并以公式的形式将结果放置在程序的执行路径上。

数据流分析技术基于控制流图上的迭代计算,关键在于数据流值从前驱节点传播到后继节点时发生的变化,传统的数据流分析一般以程序的基本块为单元进行分析,为了精确地分析每条程序语句执行后程序的状态,我们以程序的基本语句作为控制流图的单元,引入逻辑系统来进行程序性质的推导,从而可以对以语句为单元的节点分析其上性质的变化,并且使用已有的约束求解工具进行扩充推导,最终自动求得足够精确而且足够完全的分析结果,并按照逻辑系统的推理规则写成证明好的公式形式,便于查看和后续结果复用。

本发明的程序静态自动分析方法包含的具体步骤为:

步骤1:构建程序源码扩展流图

使用程序语法解析工具读取程序源码,获取程序的抽象语法树,并以程序语句为单位展开,设置程序语句执行的先后关系得到一个有向图,在有向图的边上插入程序位置点用来保存公式的集合,描述该处语句执行之后的时刻程序的状态,程序的起始处和结束处也插入程序位置点表示程序的初始状态和结束状态;

步骤2:定义递归函数及其内存范围以及扩展性质

步骤2-1.为待分析的程序性质给出定义,使用一组包含返回值、函数名、参数和函数体的函数来描述,这些函数往往是递归定义的,包括终止情况和递归调用情况,只不过在函数体中对其自身的递归调用时,参数必须与函数定义中的形参不同,并且保证最终能够到达停止递归调用的终止情况,同时给出包含该递归函数所涉及的内存范围描述,为所有的递归函数定义其内存范围函数,用来产生递归函数到其所访问的内存的映射,由于函数是递归的,所以其内存范围函数一般也是递归描述的,所有递归函数和内存范围函数中使用到的函数名都需要明确地给出定义,内存范围描述函数本身的内存范围是由其自身定义;

步骤2-1.给出前面的递归函数相关的性质描述,性质通过一阶逻辑的公式形式给出,并且形如量词+(推理前件→推理结论)的产生式形式,用来说明哪些前件成立的情况下可以推导出哪些结论;

步骤3:定义待分析性质的取值与公式的转换关系

步骤3-1.给出待分析的性质的取值和其上的运算关系,并且保证交汇运算满足结合律、交换律和幂等律,同时能够使得取值集合在该交汇运算下满足自反、反对称和传递的偏序关系,并且取值集合的设置能够保证该偏序关系具有有穷的高度,不会出现无穷上升的链;

步骤3-2.给出待分析的性质的特定取值与具体公式形式的表示的对应关系,用于将最终以公式的形式表示分析结果;

步骤4:设置性质传播规则

基于程序语句为节点扩展成为控制流图,根据待分析的程序性质的不同,给出程序性质在通过程序语句时是如何变化的定制过程,程序性质流值在通过程序语句时的变化按照提取初始流值、消除嫌疑流值、新流值生成和流值闭包推导几个步骤来完成;

步骤5:实现数据流分析具体算法

将步骤1到步骤4的分析过程实现为具体的数据流值类型,添加到逻辑系统的分析工具中;

步骤6:执行数据流分析算法

给出扩展流图入口节点上的公式表示的初始性质,执行步骤5实现的数据流分析过程,将初始性质按照与数据流值的对应关系进行提取,并执行数据流上的迭代算法,将其上的数据流值按照步骤4给出的步骤传播到该点后接程序语句之后的节点上,并比较传播结束后,后继节点上更新的数据流值与旧值相比是否发生变化,如果没有变化,则数据流值已经稳定收敛,可以终止;

步骤7:结果转化为公式

数据流值收敛后,每个程序节点上当前已有的数据流值,按照步骤3定义的转换关系转换为公式形式,按照是传播保留、语句生成还是根据性质推导得到来设置不同的公式类型,并且根据保存的依赖关系来生成公式之间的依赖关系:如果是依赖于程序节点已有的公式,则可以直接设置公式依赖,否则,如果是依赖于数据流值,那么先要递归地将被依赖的数据流值转化为公式,再依赖于其转化而成的那些公式。

所述的步骤4设置性质传播规则,处理的步骤和方法能够有效地结合逻辑推理系统和约束求解器的能力,并且实现自动化的求解,能够保证求解结果准确并且完整,具体步骤如下:

步骤4-1.对除了入口程序点外的某个程序节点,从其前驱程序节点提取传播的初始流值,如果有多个前驱程序点,那么首先对这些前驱程序点中的数据流值逐个对应做交汇运算,直至得到最终的结果,拷贝一份该初始流值作为工作流值;

步骤4-2.消除嫌疑流值,考虑当前经过的程序语句为赋值语句时,依据形式逻辑系统提取赋值语句左侧的表达式所涉及的内存范围Mstmt,将当前初始流值逐一展开成公式表示,依据递归函数内存范围的定义和逻辑系统本身来求解公式的内存范围Mform,使用如下准则决定该数据流值的取值是否可以被保留:将该内存范围与语句左侧表达式的内存范围进行比较,如果不能证明两个内存范围严格不相交,则该流值不能保留,被更新为流值取值半格的底元素,否则保留流值;从初始流值拷贝一份并执行修改得到工作流值,证明内存范围不相交的过程涉及到调用证据寻找模块,即步骤4-8,以公式作为待证明的目标公式;

步骤4-3.设置传播依赖,对于步骤4-2.中保留下来的流值,使用其内存范围与语句左侧内存范围不相交性质对应的公式作为该流值能够被保留的证据,设置依赖关系;

步骤4-4.所以根据定制的规则,多次跳转到步骤4-8进行分析并返回,根据返回的证据集合判断当前语句可以生成何种数据流值,根据找到的最精确的前提条件生成对应的数据流值并设置依赖关系,依赖于证据寻找子模块返回的证据;

步骤4-5.根据步骤4-4得到的语句生成的数据流值,更新工作流值的对应项;

步骤4-6.拷贝工作流值得到参考流值,遍历步骤2定义的关于递归函数公式的各种性质,对于每个性质,遍历当前流值中的所有可能符合该性质产生式前件的公式组合,对所有满足该性质中推理前件的组合,将推理结论更新到参考流值中对应的项,依赖关系设置为前件对应的公式组合;遍历完成后,首先将工作流值与参考流值交换,接着比较参考流值和工作流值,如果发生了变化,则重复执行步骤4-6,否则跳转到步骤4-7;

步骤4-7.将经过前面步骤后得到的工作流值设置为当前程序节点的流值,完成当前语句的分析过程。继续分析其他的程序节点;如果有某个节点更新后的流值发生了变化,则继续进行迭代,对除了入口程序点外的每个程序点,执行从步骤4-1.开始的流程;

步骤4-8.证据寻找子模块,在分析过程中被多次调用并返回,该子模块以函数的形式通过给定的输入求解出对应的输出,输入包括:一组数据流值的集合,即程序某个节点上已有的数据流值、一组已证明公式,即程序点上已被证明的公式的集合、待证明的目标公式,即需要推导判断是否成立的公式,通过将数据流值集合中的数据流展开为公式,并上已证明公式集合,整体作为前提条件公式集,结合上用户定义的递归函数的性质,使用基于已有结论和推导规则的约束求解工具,去判断是否可以证明待证明的目标公司,如果能够证明,则不断缩减前提条件集合,最终得到最小的证据集合并作为输出返回,如果不能证明,则返回空集,如有多个最小证据集合,返回任意一个。

数据流值收敛后,每个程序节点上当前已有的数据流值,按照步骤3定义的转换关系转换为公式形式,按照是传播保留、语句生成还是根据性质推导得到来设置不同的公式类型,并且根据保存的依赖关系来生成公式之间的依赖关系:如果是依赖于程序节点已有的公式,则可以直接设置公式依赖,否则,如果是依赖于数据流值,那么先要递归地将被依赖的数据流值转化为公式,再依赖于其转化而成的那些公式。

所述的步骤4设置性质传播规则,处理的步骤和方法能够有效地结合逻辑推理系统和约束求解器的能力,并且实现自动化的求解,能够保证求解结果准确并且完整,具体步骤如下:

有益效果:本发明提出的基于逻辑系统和数据流分析理论分析程序性质的流程主要分为三个步骤:首先由用户定义出需要分析的程序性质的递归函数描述,包括函数的内存范围定义和全局性质,并按照交半格的理论给出分析性质的取值集合和交汇操作,接着给出分析性质在程序语句上传播的过程,最后将这些设计按照给定接口实现为数据流分析框架的元素,给出分析的初始条件,执行分析并得到结果。具体来说,本发明所述的方法具有如下的有益效果:

(1)本发明使用递归函数、递归函数的内存范围项、递归函数的性质来描述想要分析的特定性质,可以将递归函数融合进一阶逻辑的公式进行表达,并且便于使用已有的约束求解工具根据定义进行推理;另外,根据递归函数定义的内存范围项,可以容易地求解包含递归函数的逻辑公式的内存范围表达式,从而可以与有能力分析内存指针别名的形式逻辑系统相结合,进行程序性质的静态分析。此方法有效地适配了约束求解和逻辑推理系统之间的接口,可以将约束求解器的自动化推导能力有效地应用到约束求解的推理规则中,提高了程序验证的效率。

(2)本发明将数据流分析理论引入程序性质的分析中,通过将待分析的性质的取值集合和运算以交半格的形式给出,并且给出数据流值传播过程中的处理流程,可以使用数据流分析算法的框架来进行自动化的运算和推导,严格按照推导规则进行推导可以避免用户手工分析中出现的错误,严格地按照迭代直至收敛的方式进行自动分析性质闭包可以避免用户手工分析中出现遗漏,并且将分析结果按照分析过程中收集的产生类型和依赖关系进行相应设置,也简化了手工分析分析判断依赖关系的复杂过程,大大简化了分析的难度,同时提高了分析的准确性和完整度。

(3)本发明给出的分析结果,以公式、公式的类型、公式的依赖关系分别写在程序执行的每个中间点上,通过查看最终结果,并与预期的结果进行比较,可以方便地对错误的程序定位出错的位置,即程序点性质不符预期的位置,可以有效地帮助用户对错误程序进行问题定位。

附图说明

图1是使用数据流分析手段进行程序性质分析方法的流程图。

具体实施方式

具体的实施方式可以利用目前已有的逻辑推理系统、源代码语法分析工具和抽象语法树构建工具、约束求解工具和图形界面库。以下部分就实施过程中的细节做更加具体的描述。

一.构建完整的语句粒度程序扩展流图

在具体的实施中,首先要将需要分析的源代码通过语法解析、插入程序状态点构成扩展流图用于后续的分析。

1.使用源代码语法分析工具分析源代码,按照语句粒度构造抽象语法树,形成基本的控制流图,控制流图是一个有向图,控制流图中的每个节点代表一条基本语句,流图中的边表示程序运行时的执行前后顺序。

2.在基本控制流图的每条边上插入一个程序节点,表示程序在执行该边起点语句后,还未执行终点语句时刻程序的状态,对于循环语句和分支语句,如果存在多个边指向同一个语句,则还要在两个分支边上的程序节点之后额外再添加他们的一个共同后继程序节点,其上存储从两个分支交汇得来的程序性质。在控制流图的入口和出口处各添加一个程序节点,分别用来存储程序的初始性质和终止性质。记录所用程序节点前驱的程序节点和前驱的语句、后继的程序节点和后继的语句。构成最终的扩展流图。

二.定义分析的性质和取值

将需要分析的性质定义为数据流分析所需要形式。

1.待分析的程序性质按照函数的方式给出定义,对于需要以递归方式描述的性质给出递归的函数的定义。

2.给出函数访问的内存范围的函数表示,内存范围函数自身的内存范围函数由其自身定义。

3.给出与定义的函数相关的性质,以包含递归函数的逻辑产生式的形式,给出前提条件和结论。给出的性质尽可能完整。

4.使用半格和其上的交汇运算来描述程序性质的取值集合和集合内值之间的交汇运算。控制半格的高度使之有穷,对于可能产生无穷长偏序上升链的情况,强制规定一个最大阈值。

5.设置程序性质的特定取值与使用递归函数表示的公式之间的对应关系。

6.编程实现数据流值类型,定制其上的取值和交汇操作,以及数据流值和公式之间的对应关系。

7.在扩展控制流图的入口节点给出程序的初始流值性质。

三.执行分析性质的迭代求解

根据数据流分析算法过程,需要迭代地执行数据流分析算法,每轮迭代都要对所有程序节点执行一次数据流值的前向传播操作。

1.遍历控制流图每个程序节点,提取其前驱程序节点上的数据流值,若有多个前驱节点,则执行逐一的交汇运算,得到初始数据流值。

2.对初始数据流值执行一次拷贝,得到工作流值。

3.若程序节点没有前驱语句,跳转到6,否则,根据逻辑推理系统计算该语句左侧赋值部分所访问的内存范围集合,遍历初始数据流值中的每一项,将其转换为对应的逻辑公式,并根据递归函数内存范围的定义,计算该公式对应的内存范围集合,调用证据收集子模块9搜索证据集合,分析公式的内存是否与语句访问的内存不相交,若证据集合为空,将该工作流值对应的项设置为半格的底,否则工作流值中该项不变,并设置该项依赖于证据集合。

4.根据语句的语义和其类型,多次调用证据收集子模块9,在初始流值和前驱程序节点搜索相关的可能导致产生不同结果的证据集合,然后依据最精确的证据,生成对应的新的取值,并记录其依赖关系为证据集合,在工作流值中更新这个被生成的流值。

5.拷贝一份当前工作流值,记为参考流值,遍历定义的递归函数的所有性质,用性质产生式中的前提条件,去匹配工作流值中已有的流值对应的公式和公式集合,对所有满足的匹配,生成产生式结论对应的流值取值并更新参照流值中对应的项,依赖关系设置为产生式前提条件对应的流值公式集合。遍历完所有递归函数的性质后,替换工作流值为参照流值,并比较参照流值和工作流值,如果有某些项上取值不同,则继续执行步骤5。

6.将工作流值作为该程序节点的新流值加以更新,并与当前程序节点原有的流值进行比较,记录是否发生变化。

7.在所有程序节点上的流值都按照步骤2至6进行更新之后,如果存在某个程序点上更新前后发生了变化,则跳转到1继续执行,否则终止迭代过程。

8.将得到的性质对应转换成公式形式表示,并且生成依赖关系,对于直接依赖于程序节点已有公式的,只需记录依赖的公式编号即可,对于依赖于流值表示的性质的,则先递归地将这些性质转还为公式并设置依赖,然后依赖于这些转换后的公式。

9.证据收集子模块,输入是待分析的公式,通过将初始流值全部转换为公式,再加上前驱程序点已有的公式作为前提条件,结合递归函数的性质,调用SMT约束求解工具分析待分析公式是否可被满足,如果不可,返回空集,否则,逐步减少前提条件中公式的个数,直到得到一个最小的可以推出待分析公式的公式集合,并返回该公式集合,如有多个最小集合,返回任意一个。

三.实现可视化的自动推导过程

1.将前面两步给出的扩展流图、程序节点和公式表示和程序性质的传播过程等使用图形界面库来实现,可以使用鼠标进行代码的载入、递归函数和性质的输入和公式的输入、删除等基本操作。

2.给出程序的初始性质,调用实现的分析过程自动分析得到结果,查看结果,如果出口程序点的结果不符合预期,则从控制流图的反方向去依次查看前驱程序点上的性质,分析某些特定性质未能产生的原因。

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