一种将程序代码转换成数据约束的方法和装置与流程

文档序号:11262111阅读:274来源:国知局
一种将程序代码转换成数据约束的方法和装置与流程

本发明涉及程序代码的分析验证技术,特别涉及将程序代码转换成数据约束后,通过数据约束分析程序代码是否存在错误的问题。



背景技术:

在应用软件飞速发展的今天,软件的安全性和可靠性已经成为了一个至关重要的问题,代码的分析验证测试是在相关领域的常用手段。代码分析、测试、验证等均经常需要为程序抽取出对应约束,并在约束集合上判断软件代码的安全性。在这个过程中,约束抽取扮演了非常重要的角色。

现有方法之一是通过编译器先将代码转化成中间代码,然以此为基础对原代码进行约束的抽取。例如,llvm的项目是一个模块化和可重复使用的编译器和工具技术的集合,它可以对多类代码,如c,c++,java等进行编译分析,将其转化为统一的中间指令。

但是在利用类似的中间指令来进行约束抽取的过程中,大量由代码中的指针操作转化而来的中间指令给约束的抽取带来了极大的困难。



技术实现要素:

本发明所要解决的问题:现有技术下,中间指令的复杂程度高,导致数据约束抽取困难。

为解决上述问题,本发明采用的方案如下:

根据本发明的一种将程序代码转换成数据约束的方法,该方法包括以下步骤:

s1:将用户输入的源代码转换成中间指令集;

s2:对中间指令集中的中间指令进行细化拆分,拆分后得到细化的中间指令集;

s3:从细化的中间指令集中提取简单类型的指针指向变量和数值的对应关系作为第一约束;

s4:从细化的中间指令集中提取算术和比较指令作为第二约束;

s5:合并第一约束和第二约束组成最终的数据约束;

所述步骤s2包括以下两个步骤:

s21:将原变量定义指令中涉及的复杂类型的变量细化拆分成多个简单类型的变量,然后将简单类型的变量再进一步细化成简单类型的地址指针变量和简单类型的指针指向变量,最后再将相应的变量定义指令细化拆分成针对简单类型的地址指针变量和简单类型的指针指向变量的内存分配指令和初始化数据存储指令;

s22:创建地址指针寄存器,然后将原地址移位存取指令细化拆分成将地址移位赋值给该地址指针寄存器的指令和针对该地址指针寄存器的数据存取指令。

进一步,根据本发明的一种将程序代码转换成数据约束的方法,该方法还包括从中间指令集中找出一条路径;然后选取该路径上的指令构成路径指令集,然后将该路径指令集作为中间指令集执行所述的步骤s2,s3,s4,s5。

进一步,根据本发明的一种将程序代码转换成数据约束的方法,该方法还包括先从s2中细化的指令集中找出一条路径;之后对该路径上的细化的指令集执行所述步骤的s3,s4,s5。

根据本发明的一种将程序代码转换成数据约束的装置,该装置包括以下模块:

m1,用于:将用户输入的源代码转换成中间指令集;

m2,用于:对中间指令集中的中间指令进行细化拆分,拆分后得到细化的中间指令集;

m3,用于:从细化的中间指令集中提取简单类型的指针指向变量和数值的对应关系作为第一约束;

m4,用于:从细化的中间指令集中提取算术和比较指令作为第二约束;

m5,用于:合并第一约束和第二约束组成最终的数据约束;

所述模块m2包括以下两个模块:

m21,用于:将原变量定义指令中涉及的复杂类型的变量细化拆分成多个简单类型的变量,然后将简单类型的变量再进一步细化成简单类型的地址指针变量和简单类型的指针指向变量,最后再将相应的变量定义指令细化拆分成针对简单类型的地址指针变量和简单类型的指针指向变量的内存分配指令和初始化数据存储指令;

m22,用于:创建地址指针寄存器,然后将原地址移位存取指令细化拆分成将地址移位赋值给该地址指针寄存器的指令和针对该地址指针寄存器的数据存取指令。

本发明的技术效果如下:本发明通过对中间指令集进行细化拆分之后,使得原来复杂的中间指令细化成更为简单的原子指令,组成细化的指令集,然后对路径上细化的指令集抽取出该路径对应的数据约束。其中,原子指令是对单个变量进行单步操作,从而便于约束抽取。得到路径对应的数据约束后,通过约束求解可分析该路径的可行性。

附图说明

图1是本发明实施例中待解决的的示例代码。

图2是图1示例代码转化后的中间指令集。

图3是图2中的中间指令集经细化拆分后的得到的细化的中间指令集。

其中,图2、图3中的中间指令集以控制流程图的格式表示。

具体实施方式

下面结合附图对本发明做进一步详细说明。

本发明的将程序代码转换成数据约束的方法,包括两个步骤:将源代码转换成中间指令集的步骤和对中间指令集进行处理抽取数据约束的步骤。其中,前者即为前述的步骤s1,将用户输入的源代码转换成中间指令集;后者即为前述的步骤s2、s3、s4和s5。

步骤s1的过程可以由通用的前端编译器实现。本领域技术人员理解,现有编译器理论的框架下,编译器一般由前端编译器、优化模块和后端编译器所组成。前端编译器用于将不同语言的源代码转换成中间指令集,然后优化模块对中间指令集进行优化处理,最后由后端编译器对优化处理后的中间指令集转换成不同平台或处理器下的机器指令集。目前,这种前端编译器是现有的,例如llvm编译器。llvm是构架编译器(compiler)的框架系统,能够对多种语言的源代码进行处理。图1是本发明实施例的示例代码,为c语言的源代码,经过llvm编译器转换成中间指令后得到的中间指令集,如图2所示。图2中的中间指令集以控制流程图的格式表示。本领域技术人员理解,以控制流程图的方式还是以文本的方式表示中间指令集并不影响本发明的实际处理过程。中间指令集虽然是一种能够在假想的通用处理器上运行的机器指令集,但这种中间指令集更多地考虑了各种实际处理器能够执行的机器指令与中间指令之间的对应关系。另一方面,虽然各种处理器的型号有很多种,但目前各种处理器的能够执行的机器指令集大同小异。因此,可以认为,中间指令集中的中间指令很大程度上基于现有处理器所执行的机器指令。特别是中间指令与实际处理器所执行的机器指令存在着对应关系。而实际处理器所执行的机器指令考虑到处理性能的问题,通常会将多个参数和多个步骤进行整合到一个机器指令中,以提高处理器处理性能。因此,处理器所执行的机器指令中,每个机器指令通常能够带有多个参数或者实际包含了多个步骤。由此,中间指令集中的指令也同样如此。如图2所示中,示例代码的中间指令集中的第一个中间指令@a=global[2xdouble][double2.3double4.3]中,涉及了分配内存、内存赋值以及将所分配的内存地址赋值给@a等三个步骤,该中间指令还涉及两个变量,内存中第一个double的变量和第二个double的变量。而数据约束则是二维约束关系,这种多个步骤和多个变量的中间指令很难用数据约束表示。

本实施例对中间指令集进行处理抽取数据约束的步骤采用如下步骤:

s2:对中间指令集中的中间指令进行细化拆分,拆分后得到细化的中间指令集;

s3:从细化的中间指令集中提取简单类型的指针指向变量和数值的对应关系作为第一约束;

s4:从细化的中间指令集中提取算术和比较指令作为第二约束;

s5:合并第一约束和第二约束组成最终的数据约束。

步骤s3、s4、s5得到的第一约束、第二约束以及最终的数据约束都是针对某条路径的数据约束。因此在实际过程中,有两种处理方式:

第一种处理方式是:在步骤s2之前提取路径上的中间指令集,然后将该路径上的中间指令集作为步骤s2的输入,后续的步骤s3、s4、s5得到的第一约束、第二约束以及最终的数据约束显而易见地是该路径上的数据约束。该处理方式也就是:从中间指令集中找出一条路径;然后选取该路径上的指令构成路径指令集,然后将该路径指令集作为中间指令集执行所述的步骤s2,s3,s4,s5。

第二种处理方式是:在步骤s2之后提取路径上的中间指令集,此时,由于步骤s2的细化拆分使得提取得到的该路径上的中间指令集为细化后的中间指令集。该处理方式也就是:先从s2中细化的指令集中找出一条路径;之后对该路径上的细化的指令集执行所述步骤的s3,s4,s5。

显而易见地,与上述两种处理方式相对应的,假如需要提取程序代码所有路径上的数据约束时,遍历路径的过程可以在步骤s2之前执行,也可以在步骤s2之后执行。通常考虑到处理性能的问题,优选采用在步骤s2之后遍历路径,然后对每个路径执行步骤s3、s4、s5。

步骤s2是对中间指令集中的中间指令进行细化拆分的步骤。其输入是步骤s1得到的中间指令集,输出是细化的中间指令集。步骤s2包括了对变量定义指令的细化拆分步骤和对地址移位存取指令的细化拆分步骤。其中前者也就是前述步骤s21,将原变量定义指令中涉及的复杂类型的变量细化拆分成多个简单类型的变量,然后将简单类型的变量再进一步细化成简单类型的地址指针变量和简单类型的指针指向变量,最后再将相应的变量定义指令细化拆分成针对简单类型的地址指针变量和简单类型的指针指向变量的内存分配指令和初始化数据存储指令。后者也就是前述步骤s22,创建地址指针寄存器,然后将原地址移位存取指令细化拆分成将地址移位赋值给该地址指针寄存器的指令和针对该地址指针寄存器的数据存取指令。

步骤s21包括了四个步骤:s211,复杂类型的变量简化步骤;s212:引入地址指针变量和指针指向变量;s213:将变量定义替换成针对地址指针变量和指针指向变量的内存分配指令;s214:添加初始化数据存储指令。

步骤s211中,首先,对所定义变量的类型进行分析。如果该变量的类型为复杂类型,则根据该变量中所涉及的简单类型的变量的个数,创建对应数量的简单类型的变量。以图2的中间指令集为例,变量定义指令@a=global[2xdouble][double2.3double4.3]定义了一个数组。该数组中包含两个简单类型的变量,则分别为之定义两个简单类型的变量:@a.0和@a.1。再比如变量定义指令@z=alloca%structpair定义了一个结构。该结构中包含两个简单类型的变量,则分别为之定义两个简单类型的变量:@z.x和@z.y。对于简单类型的变量定义,如@cond=globali320中的@cond已经是简单类型的变量,无需再定义简单类型的变量。

步骤s212中,对得到的简单类型的变量再进一步细化成简单类型的地址指针变量和简单类型的指针指向变量。以上述例子为例,简单类型的变量@a.0细化后的地址指针变量和指针指向变量为:@a.0和@a.0.v;简单类型的变量@a.1细化后的地址指针变量和指针指向变量为:@a.1和@a.1.v;简单类型的变量@z.x细化后的地址指针变量和指针指向变量为:@z.x和@z.x.v;简单类型的变量@z.y细化后的地址指针变量和指针指向变量为:@z.y和@z.y.v;简单类型的变量@cond细化后的地址指针变量和指针指向变量为:@cond和@cond.v。

步骤s213中,将变量定义指令细化拆分成针对简单类型的地址指针变量和简单类型的指针指向变量的单步操作指令。具体来说,变量定义指令@a=global[2xdouble][double2.3double4.3]拆分成以下三个针对@a.0和@a.0.v、@a.1和@a.1.v的指令:

@a=alloc@a.0@a.1---为@a.0@a.1分配连续的内存后将地址赋值给@a

@a.0=alloc@a.0.v---为@a.0.v分配的内存后将地址赋值给@a.0

@a.1=alloc@a.1.v---为@a.1.v分配的内存后将地址赋值给@a.1

变量定义指令@z=alloca%structpair拆分成以下三个针对@z.x和@z.x.v、@a.1和@a.1.v的指令:

@z=alloc@z.x@z.y---为@z.x@z.y分配连续的内存后将地址赋值给@z

@z.x=alloc@z.x.v---为@z.x.v分配内存后将地址赋值给@z.x

@z.y=alloc@z.y.v---为@z.y.v分配内存后将地址赋值给@z.y

变量定义指令@cond=globali320拆分成以下针对指针指向变量@cond.v的指令:

@cond=alloc@cond.v---为@cond.v分配内存后将地址赋值给@cond

最后步骤s214中,对带有初始化的变量定义指令加上初始化的数值存储指令。比如,对于带有初始化的变量定义指令@a=global[2xdouble][double2.3double4.3],则所增加的数据存储指令为:

@a.0=storedouble2.3---将数值2.3存入@a.0所指向的内存中

@a.1=storedouble4.3---将数值4.3存入@a.1所指向的内存中

对于带有初始化的变量定义指令@cond=globali320,则所增加的数据存储指令为:

@cond=storei320---将数值0存入@cond所指向的内存中

显而易见地,假如原变量定义指令不带有初始化的,则无需执行步骤s214。经过上述四个步骤处理后,如图3所示,变量定义指令@a=global[2xdouble][double2.3double4.3]最终细化拆分成:

@a=alloc@a.0@a.1---为@a.0@a.1分配连续的内存后将地址赋值给@a

@a.0=alloc@a.0.v---为@a.0.v分配的内存后将地址赋值给@a.0

@a.1=alloc@a.1.v---为@a.1.v分配的内存后将地址赋值给@a.1

@a.0=storedouble2.3---将数值2.3存入@a.0所指向的内存中

@a.1=storedouble4.3---将数值4.3存入@a.1所指向的内存中

变量定义指令@cond=globali320最终细化拆分成:

@cond=alloc@cond.v---为@cond.v分配内存后将地址赋值给@cond

@cond=storei320---将数值0存入@cond所指向的内存中

变量定义指令@z=alloca%structpair最终细化拆分成:

@z=alloc@z.x@z.y---为@z.x@z.y分配连续的内存后将地址赋值给@z

@z.x=alloc@z.x.v---为@z.x.v分配内存后将地址赋值给@z.x

@z.y=alloc@z.y.v---为@z.y.v分配内存后将地址赋值给@z.y

步骤s22,对地址移位存取指令的细化拆分步骤,包含了两个步骤:首先是步骤s221,引入或创建虚拟的地址指针寄存器,然后步骤s222,将原地址移位存取指令细化拆分成将地址移位赋值给该地址指针寄存器的指令和针对该地址指针寄存器的数据存取指令。如图2所示,以下为地址移位存取指令:

storedouble4.0double*getelementptrinbounds([2xdouble]*@a,i320,i641)

%1=loaddouble*getelementptrinbounds([2xdouble]*@a,i320,i640)

%2=loaddouble*getelementptrinbounds([2xdouble]*@a,i320,i641)

上述三个地址移位存取指令包含了地址移位指令getelmentptr和存取指令loadstore。对于第一个和第二个地址移位存取指令引入虚拟的地址指针寄存器%1.ptr,对于第三个地址移位存取指令引入虚拟的地址指针寄存器%2.ptr。然后将地址移位指令getelmentptr和存取指令loadstore两种指令分开,最终将上述三个地址移位存取指令替换成三个地址移位指令和三个存取指令,如图3所示:

%1.ptr=getelementptrinbounds([2xdouble]*@a,i320,i641)

%1.ptr=storedouble4.0

%1.ptr=getelementptrinbounds([2xdouble]*@a,i320,i640)

%1=loaddouble*%1.ptr

%2.ptr=getelementptrinbounds([2xdouble]*@a,i320,i641)

%2=loaddouble*%2.ptr

步骤s3,从细化的中间指令集中提取简单类型的指针指向变量和数值的对应关系作为第一约束。首先,将地址移位指令getelmentptr具体到相应的简单类型的地址指针变量上。将相应的寄存器替换成相应的简单类型的地址指针变量。如图3所示,两个指令:

%1.ptr=getelementptrinbounds([2xdouble]*@a,i320,i641)

%1.ptr=storedouble4.0

最终替换成:

@a.1=storedouble4.0

两个指令:

%1.ptr=getelementptrinbounds([2xdouble]*@a,i320,i640)

%1=loaddouble*%1.ptr

最终替换成:

%1=loaddouble*@a.0

两个指令:

%y=getelementptrinbounds%structpair*@z,i320,i321

%y=storedouble%add

替换成:

%z.y=storedouble%add

两个指令:

%2.ptr=getelementptrinbounds([2xdouble]*@a,i320,i641)

%2=loaddouble*%2.ptr

替换成:

%2=loaddouble%a.1

两个指令:

%y1=getelementptrinbounds%structpair*@z,i320,i321

%3=loaddouble*%y1

替换成:

%3=loaddouble*%z.y

然后提取代码中的存取指令。经上述处理后,图3中的路径s1->s3->s4->s-error能够提取出以下存取指令:

@a.0=storedouble2.3

@a.1=storedouble4.3

@cond=storei320

%0=loadi32*@cond

%1=loaddouble*@a.0

%z.y=storedouble%add

%2=loaddouble%a.1

%3=loaddouble*%z.y

然后将上述存取指令中的地址指针变量对应至指针指向变量上。于是得到以下的第一约束:

@a.0.v=2.3

@a.1.v=4.3

@cond.v=0

%0=@cond.v

%1=@a.0.v

%z.y.v=%add

%2=@a.1.v

%3=@z.y.v

也就是步骤s3是提取存取指令作为数据约束的步骤。

步骤s4,中从细化的中间指令集中提取算术和比较指令作为第二约束。如算术:%add=fadddouble%1,2.0表示成算术约束后为:%add=%1+2.0。对于比较指令需要根据路径的条件进行提取。如路径s1至s3中,比较指令%0!=0需满足为false,因此将该比较指令取反后得到%0==0作为约束,路径s4至s-error中,%2==%3需满足为false,因此将该比较指令取反后得到%2!=%3

步骤s3中得到第一约束和步骤s4得到的第二约束合并为最终数据约束:

@a.0.v=2.3

@a.1.v=4.3

@cond.v=0

%0=@cond.v

%0==0

%1=@a.0.v

%add=%1+2.0

%z.y.v=%add

%2=@a.1.v

%3=@z.y.v

%2!=%3

上述步骤s3、s4、s5更为简化的表述为:提取细化后中间指令集中的存取指令、算术指令以及比较指令作为数据约束。至此,指定路径上的程序代码表达为数据约束形式,由此,可以根据该数据约束通过约束求解器求解,当约束求解器求解后能够得到解,那么可以认为该路径可达,否则不可达。通过程序路径的可达分析为程序代码分析、测试、验证等多项任务服务。

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