首页> 中国专利> 一种源代码中值计算错误的自动检测和定位方法

一种源代码中值计算错误的自动检测和定位方法

摘要

本发明公开了一种源代码中值计算错误的自动检测和定位方法,属于计算机软件测试领域,该方法利用编译器对源代码进行语法分析,构造抽象语法树,通过遍历抽象语法树,基于表达式类型和用户指定检测的值计算错误类型,判断是否存在值计算错误的潜在风险;对可能产生值计算错误的表达式进行源代码变换,加入值计算错误检测和源代码定位的机制;编译执行变换后的源代码,执行后的源代码程序会自动判断值计算错误的发生,并准确报告错误对应的源代码位置。本方法可以在软件运行过程中自动检测和定位软件中的值计算错误,以实现更准确的错误定位功能,更好的平台普适性,更高的运行时性能和效率,从而克服现有的检测方法中存在的技术问题。

著录项

  • 公开/公告号CN104298594A

    专利类型发明专利

  • 公开/公告日2015-01-21

    原文格式PDF

  • 申请/专利权人 南京航空航天大学;

    申请/专利号CN201410499170.9

  • 发明设计人 陈哲;朱云龙;魏欧;黄志球;

    申请日2014-09-25

  • 分类号

  • 代理机构南京瑞弘专利商标事务所(普通合伙);

  • 代理人杨晓玲

  • 地址 210016 江苏省南京市秦淮区御道街29号

  • 入库时间 2023-12-17 04:06:25

法律信息

  • 法律状态公告日

    法律状态信息

    法律状态

  • 2018-03-02

    授权

    授权

  • 2015-02-18

    实质审查的生效 IPC(主分类):G06F11/36 申请日:20140925

    实质审查的生效

  • 2015-01-21

    公开

    公开

说明书

技术领域

本发明涉及一种源代码中值计算错误的自动检测和定位方法,属于计算机软件测试 领域。

背景技术

值计算错误是一种普遍存在的软件安全漏洞,尤其是在C和C++程序中。值计算错 误包括:(1)除数为0;(2)值溢出,包括值上溢、值下溢和精度丢失;(3)变量使用 前未初始化等。这些错误可能导致软件异常或系统崩溃,与缓冲区漏洞等技术结合可以 被黑客用来执行恶意代码。例如,1996年阿丽亚娜5号火箭的发射失败就是由于一个浮 点数转换为整数的精度丢失错误;Flash 0day漏洞和IE 0day漏洞允许黑客获得最高权 限;在MITRE2011年发布的报告中,值溢出漏洞被列为“25种最危险的软件错误”之一。 对于安全关键系统或安全关键应用,如果值计算错误引起系统错误或被黑客利用,将会 造成巨大损失。所以,实现值计算错误的高效自动检测和源代码定位,能够极大提高软 件开发的质量和维护的效率。

目前,值计算错误的检测方法主要分为两类:静态检测和动态检测。

静态检测是指在不运行软件的前提下,分析软件的设计模型、源代码或者二进制代 码,寻找可能导致软件失效的错误。其优点在于,通过对软件进行建模,保证了检测软 件行为的完备性。其缺点在于:(1)形式化验证会带来状态空间爆炸问题,检测时对系 统资源的要求较高,甚至无法在合理时间内完成验证;(2)由于使用模型抽象,检测结 果存在误报和漏报的可能性。

动态检测是指利用软件运行过程中的输出等信息判断错误的发生,例如软件测试技 术和虚拟机技术。软件测试的优点在于,实现了测试用例管理和执行的自动化。其缺点 在于:(1)没有实现对错误发生位置的自动源代码定位;(2)开发人员需要重新手动调 试运行测试用例,效率较低。虚拟机技术通过对源代码的解释执行,在执行过程中对值 计算错误进行监控。其缺点在于:(1)依赖于特定的虚拟机环境,不具有普遍适用性; (2)虚拟机的解释执行导致软件的性能和效率大幅度降低,不能被安全关键应用所接 受。

因此,有必要提供一种新的值计算错误的自动检测和源代码定位方法,以实现更准 确的错误定位功能,更好的平台普适性,更高的运行时性能和效率,从而克服现有的检 测方法中存在的技术问题。

发明内容

发明目的:为了克服上述已有技术存在的不足,本发明的目的旨在提供一种源代码 中值计算错误的自动检测和定位方法,该方法通过使用源代码变换技术,将源代码变换 为带有自动检测和错误定位功能的源代码,使得可以在软件运行过程中自动检测和定位 软件中的值计算错误,以实现更准确的错误定位功能,更好的平台普适性,更高的运行 时性能和效率,从而克服现有的检测方法中存在的技术问题。

为实现上述目的,本发明采用的技术方案为:一种源代码中值计算错误的自动检测 和定位方法,利用编译器对源代码进行语法分析,构造抽象语法树,通过遍历抽象语法 树,基于表达式类型和用户指定检测的值计算错误类型,判断是否存在值计算错误的潜 在风险;对可能产生值计算错误的表达式进行源代码变换,加入值计算错误检测和源代 码定位的机制;编译执行变换后的源代码,执行后的源代码程序会自动判断值计算错误 的发生,并准确报告错误对应的源代码位置。

本发明的具体步骤如下:包括以下步骤:

步骤S1,选择需要变换的源代码目录,或者单个源代码文件;

步骤S2,指定需要检测的值计算错误类型:除数为0、值溢出、量使用前未初始化 中的一种或他们之间两种以上的组合;

步骤S3,将选择的源代码目录或文件复制到源代码变换的工作目录中;

步骤S4,对工作目录中的所有源文件进行宏扩展处理,并保存扩展结果到相应的源 文件中;

步骤S5,遍历工作目录中的所有源文件,利用编译器生成符号表和抽象语法树;

步骤S6,如果指定需要检测的值计算错误类型中包括除数为0错误,则进行除数为 0错误分析和源代码变换计算;

步骤S7,如果指定需要检测的值计算错误类型中包括值溢出错误,则进行值溢出错 误分析和源代码变换计算;

步骤S8,如果指定需要检测的值计算错误类型中包括变量使用前未初始化错误,则 进行变量使用前未初始化错误分析和源代码变换计算;

步骤S9,将步骤S6、S7或/和S8中所有替换修改写回到相应的源文件中,并根据 本次修改文件更新已处理文件列表;

步骤S10,将经过变换的源代码目录或源代码文件按原有方式进行编译,生成可执 行文件;

步骤S11,将可执行文件部署在目标平台上并运行,当出现值计算错误时,插入的 代码可以自动检测到错误的发生,并准确定位和报告值计算错误在源代码中的位置。

经过上述步骤的操作,即可自动检测软件运行过程中的的值计算错误并定位。

本发明提供的一种源代码中值计算错误的自动检测和定位方法,相比现有技术,具 有以下有益效果:

1.与传统检测技术相比,本发明提供的值计算错误的自动检测和定位方法通过对源 代码的抽象语法树进行分析,具有充分的语义信息来判断潜在的值计算错误所在的源文 件和代码行,并相应地进行源代码变换,使得在错误检测中可以使用这些位置信息,因 此具有更准确的错误定位功能。

2.本发明通过源代码变换技术,使得变换后的源代码可以使用原有编译器进行编译 和部署,因此具有更好的平台普适性。

3.本发明通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断潜在的 值计算错误的类型,并相应地进行源代码变换,减少了插入代码段的规模,简化了插入 代码段的复杂程度,从而获得了更高的运行时效率和性能。

综上所述,本发明可以解决安全关键系统、安全关键应用及常用软件系统中值计算 错误检测和定位的难题,能够准确地自动检测和定位错误,实现更好的平台普适性,和 更高的运行时性能和效率,从而提高软件开发的质量和软件维护的效率,有良好的社会 效益。

附图说明

图1为本发明的流程图。

具体实施方式

下面结合具体实施例对本发明技术方案进行详细描述,但不作为本发明的限定。本 实施例采用本发明方法对一段C语言源代码进行检测和错误定位,进一步具体说明本发 明的有关方法、流程及相关步骤。

一种源代码中值计算错误的自动检测和定位方法,如图1所示,利用编译器对源代 码进行语法分析,构造抽象语法树,通过遍历抽象语法树,基于表达式类型和用户指定 检测的值计算错误类型,判断是否存在值计算错误的潜在风险;对可能产生值计算错误 的表达式进行源代码变换,加入值计算错误检测和源代码定位的机制;编译执行变换后 的源代码,执行后的源代码程序会自动判断值计算错误的发生,并准确报告错误对应的 源代码位置。

本实施例采用本发明方法对一段C语言源代码进行检测和错误定位,本实例的源代 码如下(文件名为test.c):

本发明方法的具体操作步骤如下:

步骤S1,选择需要变换的源代码目录,或者单个源代码文件。

本例中,选择源代码文件test.c。

步骤S2,指定需要检测的值计算错误类型:除数为0,值溢出,变量使用前未初始 化中的一种或几种。

本例中,指定需要检测的值计算错误类型包括除数为0,值溢出和变量使用前未初 始化共三种。

步骤S3,将选择的源代码目录或文件复制到源代码变换的工作目录中。

本例中,将源代码文件test.c复制到工作目录/tmp/work/中。

步骤S4,对工作目录中的所有源文件进行宏扩展处理,并保存扩展结果到相应的源 文件中。进一步地,宏扩展处理具体包括:操作1、利用编译器的词法分析器对文件进 行词法分析,词法分析器返回经过宏扩展处理之后的词法单元;操作2、针对扩展自宏 的词法单元,其属性中包括宏扩展之后的内容和宏扩展的位置,用扩展之后的内容替换 宏扩展位置的原有内容。

本例中,将第3行的语句“int temp1=N”替换为“int temp1=2”,并保存扩展结 果到test.c文件中。

步骤S5,遍历工作目录中的所有源文件,利用编译器生成符号表和抽象语法树。

本例中,因为只有一个文件test.c,利用编译器生成test.c对应的符号表和抽象 语法树。

步骤S6,如果指定需要检测的值计算错误类型中包括除数为0错误,则转至步骤 S61;否则,略过中间步骤,直接转至步骤S7。

本例中,指定需要检测的值计算错误类型中包括除数为0错误,转至步骤S61。

步骤S61,遍历编译器生成的抽象语法树,如果当前节点s所在文件路径不在工作 目录下,或已经存在于已处理文件列表中,则忽略该节点,不进行处理。

本例中,遍历test.c对应的抽象语法树,所有节点所在文件路径均在工作目录 /tmp/work下,且初始时已处理文件列表set<string>类型容器变量parsedFileSet的 元素为空,所以不存在忽略节点的情况。

步骤S6101,如果当前节点s为除法表达式a6101/b6101,则从符号表中获取表达 式b6101的类型typeB6101,然后将表达式“a6101/b6101”替换为如下函数调用:

a6101/(typeB6101)check_zero(b6101,fileNameS,lineS,columnS)

其中check_zero为除0检测函数名,fileNameS,lineS,columnS分别为节点s所在文 件名,行号和列号。然后,从抽象语法树中获得节点s所在文件的开始位置locS,加入 到检测函数声明插入位置集合declLocSet,表示此文件需要除0检测相关函数的声明。

本例中,将第10行的表达式“temp1/temp2”替换为如下函数调用:

temp1/(int)check_zero(temp2,“test.c”,10,15)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要除0检测相关函数的声明。

步骤S6102,如果当前节点s为复合除赋值表达式a6102/=b6102,则从符号表中获 取表达式b6102的类型typeB6102,然后将表达式“a6102/=b6102”替换为如下函数调 用:

a6102/=(typeB6102)check_zero(b6102,fileNameS,lineS,columnS)

其中check_zero为除0检测函数名,fileNameS,lineS,columnS分别为节点s所在文 件名,行号和列号。然后,从抽象语法树中获得节点s所在文件的开始位置locS,加入 到检测函数声明插入位置集合declLocSet,表示此文件需要除0检测相关函数的声明。

本例中,将第16行的表达式“result/=temp2”替换为如下函数调用:

result/=(int)check_zero(temp2,“test.c”,16,9)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要除0检测相关函数的声明。

步骤S62,对于包含主函数声明的文件,在文件开始位置插入检测函数long double  check_zero(long double num,const char*fileName,unsigned line,unsigned column) 的定义,其中参数num代表除数,参数fileName,line,column分别代表运算发生位 置所在文件名,行号和列号。检测函数check_zero判断表示除数的参数num是否为0, 如果为0,则报告除0错误发生位置的文件名,行号和列号,并结束程序运行,否则将 除数作为函数的返回值返回。

本例中,test.c文件中包含主函数声明,所以在test.c文件开始位置插入检测函 数的定义代码如下:

步骤S63,根据检测函数声明插入位置集合declLocSet,在相应位置插入检测函数 long double check_zero(long double num,const char*fileName,unsigned line, unsigned column)的声明。其中,参数num代表除数,参数fileName,line,column 分别代表运算发生位置所在文件名,行号和列号。然后,将检测函数声明插入位置集合 declLocSet重置为空。

本例中,表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet只包含有表示test.c文件开始位置的SourceLocation类型变量loc。所以, 在test.c文件开始位置插入检测函数声明代码如下:

01extern long double check_zero(long double num,const char*fileName,

02                                  unsigned line,unsigned column);

然后,将表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet重置为空。

步骤S7,如果指定需要检测的值计算错误类型中包括值溢出错误,则转至步骤S71; 否则,略过中间步骤,直接转至步骤S8。

本例中,指定需要检测的值计算错误类型中包括值溢出错误,转至步骤S71。

步骤S71,遍历编译器生成的抽象语法树,如果当前节点s所在文件路径不在工作 目录下,或已经存在于已处理文件列表中,则忽略该节点,不进行处理。

本例中,遍历test.c对应的抽象语法树,所有节点所在文件路径均在工作目录 /tmp/work下,且初始时已处理文件列表set<string>类型容器变量parsedFileSet元 素为空,所以不存在忽略节点的情况。

步骤S7101,如果当前节点s为后自增表达式a7101++,则从符号表中获取表达式 a7101的类型typeA7101;如果typeA7101为指针类型,则忽略节点s,不进行处理,否 则将表达式“a7101++”替换为如下逗号表达式:

(pOverflowTemp=&(a7101),

check_overflow(*(typeA7101*)pOverflowTemp+(typeA7101)1,

*(typeA7101*)pOverflowTemp+(long double)1,fileNameS,lineS,columnS),

((*(typeA7101*)pOverflowTemp))++)

其中pOverflowTemp为空类型指针,用来存储表达式a7101的地址,check_overflow 为值溢出检测函数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和 列号。然后,从抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声 明插入位置集合declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第4行表达式“temp1++”替换为如下逗号表达式:

(pOverflowTemp=&(temp1),

check_overflow(*(int*)pOverflowTemp+(int)1,

*(int*)pOverflowTemp+(long double)1,“test.c”,4,15),

((*(int*)pOverflowTemp))++)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7102,如果当前节点s为后自减表达式a7102--,则从符号表中获取表达式 a7102的类型typeA7102;如果typeA7102为指针类型,则忽略节点s,不进行处理,否 则将表达式“a7102--”替换为如下逗号表达式:

(pOverflowTemp=&(a7102),

check_overflow(*(typeA7102*)pOverflowTemp-(typeA7102)1,

*(typeA7102*)pOverflowTemp-(long double)1,fileNameS,lineS,columnS),

((*(typeA7102*)pOverflowTemp))--)

其中pOverflowTemp为空类型指针,用来存储表达式a7102的地址,check_overflow 为值溢出检测函数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和 列号;然后,从抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声 明插入位置集合declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第5行表达式“temp1--”替换为如下逗号表达式:

(pOverflowTemp=&(temp1),

check_overflow(*(int*)pOverflowTemp-(int)1,

*(int*)pOverflowTemp-(long double)1,“test.c”,5,15),

((*(int*)pOverflowTemp))--)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7103,如果当前节点s为前自增表达式++a7103,则从符号表中获取表达式 a7103的类型typeA7103;如果typeA7103为指针类型,则忽略节点s,不进行处理,否 则将表达式“++a7103”替换为如下逗号表达式:

(pOverflowTemp=&(a7103),

check_overflow(*(typeA7103*)pOverflowTemp+(typeA7103)1,

*(typeA7103*)pOverflowTemp+(long double)1,fileNameS,lineS,columnS),

(++(*(typeA7103*)pOverflowTemp)))

其中pOverflowTemp为空类型指针,用来存储表达式a7103的地址,check_overflow 为值溢出检测函数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和 列号;然后,从抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声 明插入位置集合declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第6行表达式“++temp1”替换为如下逗号表达式:

(pOverflowTemp=&(temp1),

check_overflow(*(int*)pOverflowTemp+(int)1,

*(int*)pOverflowTemp+(long double)1,“test.c”,6,10),

(++(*(int*)pOverflowTemp)))

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7104,如果当前节点s为前自减表达式—a7104,则从符号表中获取表达式 a7104的类型typeA7104;如果typeA7104为指针类型,则忽略节点s,不进行处理,否 则将表达式“--a7104”替换为如下逗号表达式:

(pOverflowTemp=&(a7104),

check_overflow(*(typeA7104*)pOverflowTemp-(typeA7104)1,

*(typeA7104*)pOverflowTemp-(long double)1,fileNameS,lineS,columnS),

(--(*(typeA7104*)pOverflowTemp)))

其中pOverflowTemp为空类型指针,用来存储表达式a7104的地址,check_overflow 为值溢出检测函数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和 列号;然后,从抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声 明插入位置集合declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第7行表达式“--temp3”替换为如下逗号表达式:

(pOverflowTemp=&(temp3),

check_overflow(*(int*)pOverflowTemp-(int)1,

*(int*)pOverflowTemp-(long double)1,“test.c”,7,10),

(--(*(int*)pOverflowTemp)))

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7105,如果当前节点s为取负表达式-a7105,则从符号表中获取表达式a7105 的类型typeA7105,将表达式“-a7105”替换为如下逗号表达式:

(overflowTemp1=(a7105),

check_overflow(-(typeA7105)overflowTemp1,

-overflowTemp1,fileNameS,lineS,columnS),

-(typeA7105)overflowTemp1)

其中overflowTemp1为long double类型变量,用来记录表达式a7105的值, check_overflow为值溢出检测函数名,fileNameS,lineS,columnS分别为节点s所在 文件名,行号和列号;然后,从抽象语法树中获得节点s所在文件的开始位置locS,加 入到检测函数声明插入位置集合declLocSet,表示此文件需要值溢出检测相关函数的声 明。

本例中,将第8行表达式“-temp1”替换为如下逗号表达式:

(overflowTemp1=(temp1),

check_overflow(-(int)overflowTemp1,-overflowTemp1,“test.c”,8,10),

-(int)overflowTemp1)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7106,如果当前节点s为乘法表达式a7106*b7106,则从符号表中获取表达 式a7106的类型typeA7106,b7106的类型typeB7106,将表达式“a7106*b7106”替换 为如下逗号表达式:

(overflowTemp1=(a7106),

overflowStackPush(overflowTemp1),

overflowTemp2=(b7106),

overflowTemp1=overflowStackPop(),

check_overflow((typeA7106)overflowTemp1*(typeB7106)overflowTemp2,

             overflowTemp1*overflowTemp2,fileNameS,lineS,columnS),

(typeA7106)overflowTemp1*(typeB7106)overflowTemp2)

其中overflowTemp1和overflowTemp2为long double类型变量,分别用来记录表达式 a7106和表达式b7106的值,overflowStackPush和overflowStackPop分别为栈结构的 压栈和出栈操作,check_overflow为值溢出检测函数名,fileNameS,lineS,columnS 分别为节点s所在文件名,行号和列号;然后,从抽象语法树中获得节点s所在文件的 开始位置locS,加入到检测函数声明插入位置集合declLocSet,表示此文件需要值溢 出检测相关函数的声明。

本例中,将第9行表达式“temp1*temp2”替换为如下逗号表达式:

(overflowTemp1=(temp1),

overflowStackPush(overflowTemp1),

overflowTemp2=(temp2),

overflowTemp1=overflowStackPop(),

check_overflow((int)overflowTemp1*(int)overflowTemp2,

             overflowTemp1*overflowTemp2,“test.c”,9,15),

(int)overflowTemp1*(int)overflowTemp2)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7107,如果当前节点s为除法表达式a7107/b7107,则从符号表中获取表达 式a7107的类型typeA7107,b7107的类型typeB7107,将表达式“a7107/b7107”替换 为如下逗号表达式:

(overflowTemp1=(a7107),

overflowStackPush(overflowTemp1),

overflowTemp2=(b7107),

overflowTemp1=overflowStackPop(),

check_overflow((typeA7107)overflowTemp1/(typeB7107)overflowTemp2,

             overflowTemp1/overflowTemp2,fileNameS,lineS,columnS),

(typeA7107)overflowTemp1/(typeB7107)overflowTemp2)

其中overflowTemp1和overflowTemp2为long double类型变量,分别用来记录表达式 a7107和表达式b7107的值,overflowStackPush和overflowStackPop分别为栈结构的 压栈和出栈操作,check_overflow为值溢出检测函数名,fileNameS,lineS,columnS 分别为节点s所在文件名,行号和列号;然后,从抽象语法树中获得节点s所在文件的 开始位置locS,加入到检测函数声明插入位置集合declLocSet,表示此文件需要值溢 出检测相关函数的声明。

本例中,将第10行表达式“temp1/temp2”替换为逗号表达式,特别地,由于在步 骤S6101中对表达式“temp1/temp2”已进行了一次替换,所以综合两次操作表达式 “temp1/temp2”将被替换为:

(overflowTemp1=(temp1),

overflowStackPush(overflowTemp1),

overflowTemp2=((int)check_zero(temp2,“test.c”,10,15)),

overflowTemp1=overflowStackPop(),

check_overflow((int)overflowTemp1/(int)overflowTemp2,

             overflowTemp1/overflowTemp2,“test.c”,10,15),

(int)overflowTemp1/(int)overflowTemp2)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7108,如果当前节点s为加法表达式a7108+b7108,则从符号表中获取表达 式a7108的类型typeA7108,b7108的类型typeB7108;如果typeA7108或typeB7108 为指针类型,则忽略节点s,不进行处理,否则将表达式“a7108+b7108”替换为如下逗 号表达式:

(overflowTemp1=(a7108),

overflowStackPush(overflowTemp1),

overflowTemp2=(b7108),

overflowTemp1=overflowStackPop(),

check_overflow((typeA7108)overflowTemp1+(typeB7108)overflowTemp2,

             overflowTemp1+overflowTemp2,fileNameS,lineS,columnS),

(typeA7108)overflowTemp1+(typeB7108)overflowTemp2)

其中overflowTemp1和overflowTemp2为long double类型变量,分别用来记录表达式 a7108和表达式b7108的值,overflowStackPush和overflowStackPop分别为栈结构的 压栈和出栈操作,check_overflow为值溢出检测函数名,fileNameS,lineS,columnS 分别为节点s所在文件名,行号和列号;然后,从抽象语法树中获得节点s所在文件的 开始位置locS,加入到检测函数声明插入位置集合declLocSet,表示此文件需要值溢 出检测相关函数的声明。

本例中,将第11行表达式“temp1+temp2”替换为如下逗号表达式:

(overflowTemp1=(temp1),

overflowStackPush(overflowTemp1),

overflowTemp2=(temp2),

overflowTemp1=overflowStackPop(),

check_overflow((int)overflowTemp1+(int)overflowTemp2,

             overflowTemp1+overflowTemp2,“test.c”,11,15),

(int)overflowTemp1+(int)overflowTemp2)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7109,如果当前节点s为减法表达式a7109-b7109,则从符号表中获取表达 式a7109的类型typeA7109,b7109的类型typeB7109;如果typeA7109或typeB7109 为指针类型,则忽略节点s,不进行处理,否则将表达式“a7109-b7109”替换为如下逗 号表达式:

(overflowTemp1=(a7109),

overflowStackPush(overflowTemp1),

overflowTemp2=(b7109),

overflowTemp1=overflowStackPop(),

check_overflow((typeA7109)overflowTemp1-(typeB7109)overflowTemp2,

             overflowTemp1-overflowTemp2,fileNameS,lineS,columnS),

(typeA7109)overflowTemp1-(typeB7109)overflowTemp2)

其中overflowTemp1和overflowTemp2为long double类型变量,分别用来记录表达式 a7109和表达式b7109的值,overflowStackPush和overflowStackPop分别为栈结构的 压栈和出栈操作,check_overflow为值溢出检测函数名,fileNameS,lineS,columnS 分别为节点s所在文件名,行号和列号;然后,从抽象语法树中获得节点s所在文件的 开始位置locS,加入到检测函数声明插入位置集合declLocSet,表示此文件需要值溢 出检测相关函数的声明。

本例中,将第12行表达式“temp1-temp2”替换为如下逗号表达式:

(overflowTemp1=(temp1),

overflowStackPush(overflowTemp1),

overflowTemp2=(temp2),

overflowTemp1=overflowStackPop(),

check_overflow((int)overflowTemp1-(int)overflowTemp2,

             overflowTemp1-overflowTemp2,“test.c”,12,15),

(int)overflowTemp1-(int)overflowTemp2)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7110,如果当前节点s为位左移表达式a7110<<b7110,则从符号表中获取表 达式a7110的类型typeA7110,b7110的类型typeB7110,将表达式“a7110<<b7110”替 换为如下逗号表达式:

(overflowTemp1=(a7110),

overflowStackPush(overflowTemp1),

overflowTemp2=(b7110),

overflowTemp1=overflowStackPop(),

check_overflow((typeA7110)overflowTemp1<<(typeB7110)overflowTemp2,

       (long long int)overflowTemp1<<(long long int)overflowTemp2,

       fileNameS,lineS,columnS),

(typeA7110)overflowTemp1<<(typeB7110)overflowTemp2)

其中overflowTemp1和overflowTemp2为long double类型变量,分别用来记录表达式 a7110和表达式b7110的值,overflowStackPush和overflowStackPop分别为栈结构的 压栈和出栈操作,check_overflow为值溢出检测函数名,fileNameS,lineS,columnS 分别为节点s所在文件名,行号和列号;然后,从抽象语法树中获得节点s所在文件的 开始位置locS,加入到检测函数声明插入位置集合declLocSet,表示此文件需要值溢 出检测相关函数的声明。

本例中,将第13行表达式“temp1<<temp2”替换为如下逗号表达式:

(overflowTemp1=(temp1),

overflowStackPush(overflowTemp1),

overflowTemp2=(temp2),

overflowTemp1=overflowStackPop(),

check_overflow((int)overflowTemp1<<(int)overflowTemp2,

              (long long int)overflowTemp1<<(long long int)overflowTemp2,

              “test.c”,13,15),

(int)overflowTemp1<<(int)overflowTemp2)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7111,如果当前节点s为位右移表达式a7111>>b7111,则从符号表中获取表 达式a7111的类型typeA7111,b7111的类型typeB7111,将表达式“a7111>>b7111”替 换为如下逗号表达式:

(overflowTemp1=(a7111),

overflowStackPush(overflowTemp1),

overflowTemp2=(b7111),

overflowTemp1=overflowStackPop(),

check_overflow((typeA7111)overflowTemp1>>(typeB7111)overflowTemp2,

      (long long int)overflowTemp1>>(long long int)overflowTemp2,

       fileNameS,lineS,columnS),

(typeA7111)overflowTemp1>>(typeB7111)overflowTemp2)

其中overflowTemp1和overflowTemp2为long double类型变量,分别用来记录表达式 a7111和表达式b7111的值,overflowStackPush和overflowStackPop分别为栈结构的 压栈和出栈操作,check_overflow为值溢出检测函数名,fileNameS,lineS,columnS 分别为节点s所在文件名,行号和列号;然后,从抽象语法树中获得节点s所在文件的 开始位置locS,加入到检测函数声明插入位置集合declLocSet,表示此文件需要值溢 出检测相关函数的声明。

本例中,将第14行表达式“temp1>>temp2”替换为如下逗号表达式:

(overflowTemp1=(temp1),

overflowStackPush(overflowTemp1),

overflowTemp2=(temp2),

overflowTemp1=overflowStackPop(),

check_overflow((int)overflowTemp1>>(int)overflowTemp2,

      (long long int)overflowTemp1>>(long long int)overflowTemp2,

       “test.c”,14,15),

(int)overflowTemp1>>(int)overflowTemp2)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7112,如果当前节点s为复合乘赋值表达式a7112*=b7112,则从符号表中获 取表达式a7112的类型typeA7112,b7112的类型typeB7112,将表达式“a7112*=b7112” 替换为如下逗号表达式:

(pOverflowTemp=&(a7112),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(b7112),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((typeA7112)((*(typeA7112*)pOverflowTemp)*

(typeB7112)overflowTemp1),(*(typeA7112*)pOverflowTemp)*overflowTemp1,

fileNameS,lineS,columnS),

(*(typeA7112*)pOverflowTemp)*=(typeB7112)overflowTemp1)

其中pOverflowTemp为空类型指针,用来存储表达式a7112的地址,overflowTemp1为 long double类型变量,用来记录表达式b7112的值,overflowStackPush和 overflowStackPop分别为栈结构的压栈和出栈操作,check_overflow为值溢出检测函 数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和列号;然后,从 抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声明插入位置集合 declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第15行表达式“result*=temp2”替换为如下逗号表达式:

(pOverflowTemp=&(result),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(temp2),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((int)((*(int*)pOverflowTemp)*(int)overflowTemp1),

         (*(int*)pOverflowTemp)*overflowTemp1,“test.c”,15,9),

(*(int*)pOverflowTemp)*=(int)overflowTemp1)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7113,如果当前节点s为复合除赋值表达式a7113/=b7113,则从符号表中获 取表达式a7113的类型typeA7113,b7113的类型typeB7113,将表达式“a7113/=b7113” 替换为如下逗号表达式:

(pOverflowTemp=&(a7113),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(b7113),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((typeA7113)((*(typeA7113*)pOverflowTemp)/(typeB7113)

overflowTemp1),(*(typeA7113*)pOverflowTemp)/overflowTemp1,

fileNameS,lineS,columnS),

(*(typeA7113*)pOverflowTemp)/=(typeB7113)overflowTemp1)

其中pOverflowTemp为空类型指针,用来存储表达式a7113的地址,overflowTemp1为 long double类型变量,用来记录表达式b7113的值,overflowStackPush和 overflowStackPop分别为栈结构的压栈和出栈操作,check_overflow为值溢出检测函 数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和列号;然后,从 抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声明插入位置集合 declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第16行表达式“result/=temp2”替换为逗号表达式,特别地,由于在 步骤S6102中对表达式“result/=temp2”已进行了一次转换,所以综合两次操作表达 式“result/=temp2”将被替换为:

(pOverflowTemp=&(result),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=((int)check_zero(temp2,“test.c”,16,9)),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((int)((*(int*)pOverflowTemp)/(int)overflowTemp1),

        (*(int*)pOverflowTemp)/overflowTemp1,“test.c”,16,9),

(*(int*)pOverflowTemp)/=(int)overflowTemp1)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7114,如果当前节点s为复合加赋值表达式a7114+=b7114,则从符号表中获 取表达式a7114的类型typeA7114,b7114的类型typeB7114;如果typeA7114或 typeB7114为指针类型,则忽略节点s,不进行处理,否则将表达式“a7114+=b7114” 替换为如下逗号表达式:

(pOverflowTemp=&(a7114),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(b7114),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((typeA7114)((*(typeA7114*)pOverflowTemp)+

(typeB7114)overflowTemp1),

(*(typeA7114*)pOverflowTemp)+overflowTemp1,fileNameS,lineS,columnS),

(*(typeA7114*)pOverflowTemp)+=(typeB7114)overflowTemp1)

其中pOverflowTemp为空类型指针,用来存储表达式a7114的地址,overflowTemp1为 long double类型变量,用来记录表达式b7114的值,overflowStackPush和 overflowStackPop分别为栈结构的压栈和出栈操作,check_overflow为值溢出检测函 数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和列号;然后,从 抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声明插入位置集合 declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第17行表达式“result+=temp2”替换为如下逗号表达式:

(pOverflowTemp=&(result),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(temp2),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((int)((*(int*)pOverflowTemp)+(int)overflowTemp1),

       (*(int*)pOverflowTemp)+overflowTemp1,“test.c”,17,9),

(*(int*)pOverflowTemp)+=(int)overflowTemp1)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7115,如果当前节点s为复合减赋值表达式a7115-=b7115,则从符号表中获 取表达式a7115的类型typeA7115,b7115的类型typeB7115;如果typeA7115或 typeB7115为指针类型,则忽略节点s,不进行处理,否则将表达式“a7115-=b7115” 替换为如下逗号表达式:

(pOverflowTemp=&(a7115),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(b7115),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((typeA7115)((*(typeA7115*)pOverflowTemp)-

(typeB7115)overflowTemp1),

(*(typeA7115*)pOverflowTemp)-overflowTemp1,fileNameS,lineS,columnS),

(*(typeA7115*)pOverflowTemp)-=(typeB7115)overflowTemp1)

其中pOverflowTemp为空类型指针,用来存储表达式a7115的地址,overflowTemp1为 long double类型变量,用来记录表达式b7115的值,overflowStackPush和 overflowStackPop分别为栈结构的压栈和出栈操作,check_overflow为值溢出检测函 数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和列号;然后,从 抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声明插入位置集合 declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第18行表达式“result-=temp2”替换为如下逗号表达式:

(pOverflowTemp=&(result),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(temp2),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((int)((*(int*)pOverflowTemp)-(int)overflowTemp1),

       (*(int*)pOverflowTemp)-overflowTemp1,“test.c”,18,9),

(*(int*)pOverflowTemp)-=(int)overflowTemp1)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7116,如果当前节点s为复合位左移赋值表达式a7116<<=b7116,则从符号 表中获取表达式a7116的类型typeA7116,b7116的类型typeB7116,将表达式 “a7116<<=b7116”替换为如下逗号表达式:

(pOverflowTemp=&(a7116),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(b7116),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((typeA7116)((*(typeA7116*)pOverflowTemp)<<

(typeB7116)overflowTemp1),(long long int)(*(typeA7116*)pOverflowTemp)

<<(long long int)overflowTemp1,fileNameS,lineS,columnS),

(*(typeA7116*)pOverflowTemp)<<=(typeB7116)overflowTemp1)

其中pOverflowTemp为空类型指针,用来存储表达式a7116的地址,overflowTemp1为 long double类型变量,用来记录表达式b7116的值,overflowStackPush和 overflowStackPop分别为栈结构的压栈和出栈操作,check_overflow为值溢出检测函 数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和列号;然后,从 抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声明插入位置集合 declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第19行表达式“result<<=temp2”替换为如下逗号表达式:

(pOverflowTemp=&(result),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(temp2),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((int)((*(int*)pOverflowTemp)<<(int)overflowTemp1),

(long long int)(*(int*)pOverflowTemp)<<(long long int)overflowTemp1,

“test.c”,19,9),

(*(int*)pOverflowTemp)<<=(int)overflowTemp1)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7117,如果当前节点s为复合位右移赋值表达式a7117>>=b7117,则从符号 表中获取表达式a7117的类型typeA7117,b7117的类型typeB7117,将表达式 “a7117>>=b7117”替换为如下逗号表达式:

(pOverflowTemp=&(a7117),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(b7117),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((typeA7117)((*(typeA7117*)pOverflowTemp)>>

(typeB7117)overflowTemp1),(long long int)(*(typeA7117*)pOverflowTemp)>>

(long long int)overflowTemp1,fileNameS,lineS,columnS),

(*(typeA7117*)pOverflowTemp)>>=(typeB7117)overflowTemp1)

其中pOverflowTemp为空类型指针,用来存储表达式a7117的地址,overflowTemp1为 long double类型变量,用来记录表达式b7117的值,overflowStackPush和 overflowStackPop分别为栈结构的压栈和出栈操作,check_overflow为值溢出检测函 数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和列号;然后,从 抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数声明插入位置集合 declLocSet,表示此文件需要值溢出检测相关函数的声明。

本例中,将第20行表达式“result>>=temp2”替换为如下逗号表达式:

(pOverflowTemp=&(result),

overflowStackPush((long)pOverflowTemp),

overflowTemp1=(temp2),

pOverflowTemp=(void*)(long)overflowStackPop(),

check_overflow((int)((*(int*)pOverflowTemp)>>(int)overflowTemp1),

(long long int)(*(int*)pOverflowTemp)>>(long long int)overflowTemp1,

“test.c”,20,9),

(*(int*)pOverflowTemp)>>=(int)overflowTemp1)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S7118,如果当前节点s为隐含转换表达式(typeCast7118)a7118,其中 typeCast7118为隐含转换类型,则从符号表中获取表达式a7118的类型typeA7118;如 果typeA7118和typeCast7118相同,忽略节点s,不进行处理,否则将表达式 “(typeCast7118)a7118”替换为如下逗号表达式:

(overflowTemp1=(a7118),

check_overflow((typeCast7118)overflowTemp1,

overflowTemp1,fileNameS,lineS,columnS),

(typeCast7118)overflowTemp1)

其中overflowTemp1为long double类型变量,用来记录表达式a7118的值, check_overflow为值溢出检测函数名,fileNameS,lineS,columnS分别为节点s所在 文件名,行号和列号;然后,从抽象语法树中获得节点s所在文件的开始位置locS,加 入到检测函数声明插入位置集合declLocSet,表示此文件需要值溢出检测相关函数的声 明。

本例中,将第21行表达式“2.0”替换为如下逗号表达式:

(overflowTemp1=(2.0),

check_overflow((int)overflowTemp1,overflowTemp1,“test.c”,21,10),

(int)overflowTemp1)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet,表示test.c需要值溢出检测相关函数的声明。

步骤S72,对于包含主函数声明的文件,在文件开始位置插入空类型指针 pOverflowTemp,long double类型变量overflowTemp1和overflowTemp2,栈结构struct  OverflowStackNode,压栈函数void overflowStackPush(long double num),出栈函数 long double overflowStackPop(),清空栈函数void overflowStackFree()和值溢出检 测函数void check_overflow(long double num1,long double num2,const char *fileName,unsigned line,unsigned column)的定义。其中,压栈函数 overflowStackPush的参数num代表需要存储的表达式值,检测函数check_overflow 的参数num1和num2分别代表类型提升前表达式值和类型提升后表达式值,参数 fileName,line,column分别代表运算发生位置所在文件名,行号和列号。检测函数 check_overflow对参数num1和num2进行比较:如果两个参数值相同,说明未发生值溢 出,不执行任何动作;如果两个参数值不相同,则说明发生了值溢出,报告值溢出错误 发生位置的文件名,行号和列号,清空栈并结束程序运行。

本例中,test.c文件包含主函数声明,所以在test.c文件开始位置插入如下定义 代码:

步骤S73,根据检测函数声明插入位置集合declLocSet,在相应位置插入空类型指 针pOverflowTemp,long double类型变量overflowTemp1和overflowTemp2,栈结构 struct OverflowStackNode,压栈函数void overflowStackPush(long double num), 出栈函数long double overflowStackPop(),清空栈函数void overflowStackFree() 和值溢出检测函数void check_overflow(long double num1,long double num2,const  char*fileName,unsigned line,unsigned column)的声明。其中,压栈函数 overflowStackPush的参数num代表需要存储的表达式值,检测函数check_overflow 的参数num1和num2分别代表类型提升前表达式值和类型提升后表达式值,参数 fileName,line,column分别代表运算发生位置所在文件名,行号和列号。然后,将检 测函数声明插入位置集合declLocSet重置为空。

本例中,表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet只包含有表示test.c文件开始位置的SourceLocation类型变量loc。所以, 在test.c文件开始位置插入声明代码如下:

然后,将表示检测函数声明插入位置集合的set<SourceLocation>类型容器变量 declLocSet重置为空。

步骤S8,如果指定需要检测的值计算错误类型中包括变量使用前未初始化错误,则 转至步骤S81;否则,略过中间步骤,直接转至步骤S9。

本例中,指定需要检测的值计算错误类型中包括变量使用前未初始化错误,转至步 骤S81。

步骤S81,遍历编译器生成的抽象语法树,如果当前节点s所在文件路径不在工作 目录下,或已经存在于已处理文件列表中,则忽略该节点,不进行处理。

本例中,遍历test.c对应的抽象语法树,所有节点所在文件路径均在工作目录 /tmp/work下,且初始时已处理文件列表set<string>类型容器变量parsedFileSet的 元素为空,所以不存在忽略节点的情况。

步骤S8101,如果当前节点s为声明语句,则遍历声明语句中的每一个声明a8101, 并通过抽象语法树获得声明a8101的属性;如果声明a8101同时满足如下条件:(1)为 变量声明;(2)非全局变量声明;(3)非函数参数声明;(4)声明没有给出初始化表达 式,则将变量声明a8101绑定一个编号varIndex;编号从1开始依次选取,所以varIndex 也作为记录变量声明总数的计数器。

本例中,第3行的temp3声明同时满足上述条件,将temp3的声明绑定编号1,此 时varIndex也为1。

步骤S8102,如果当前节点s为变量声明引用a8102,考察节点s在抽象语法树中 的父节点;如果s的父节点为隐含左值向右值转换表达式,自增表达式或自减表达式, 忽略节点s,不进行处理,否则,表明节点s为对变量的赋值,利用步骤S8101得到的 变量声明和编号的绑定关系,查询得a8102所引用变量声明对应编号indexTemp8102, 将表达式“a8102”替换为如下逗号表达式:

*(fileName_set_uninit(indexTemp8102),&(a8102))

其中fileName为节点s所在文件名,并作为fileName_set_uninit的一部分构成变量 赋值状态设置函数名;然后,从抽象语法树中获得节点s所在文件的开始位置locS,加 入到检测函数定义代码插入位置集合defLocSet,表示此文件需要变量使用前未初始化 检测相关函数的定义。

本例中,将第23行的表达式“temp3”替换为如下逗号表达式:

*(test_c_set_uninit(1),&(temp3))

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数定义代码插入位置集合的set<SourceLocation>类型容器变量 defLocSet,表示test.c需要变量使用前未初始化检测相关函数的定义。

步骤S8103,如果当前节点s为隐含转换表达式(typeCast8103)a8103,考察该节点: 如果typeCast8103不是左值向右值的转换或a8103不是变量声明引用,忽略节点s,不 进行处理,否则,表明节点s为对变量值的引用,利用步骤S8101得到的变量声明和编 号的绑定关系,查询得a8103所引用变量声明对应编号indexTemp8103,将表达式 “a8103”替换为如下逗号表达式:

(fileName_check_uninit(indexTemp8103,fileNameS,lineS,columnS),a8103)

其中fileName为节点s所在文件名,并作为fileName_check_uninit的一部分构 成赋值状态检测函数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号 和列号;然后,从抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数 定义代码插入位置集合defLocSet,表示此文件需要变量使用前未初始化检测相关函数 的定义。

本例中,将第22行的表达式“temp3”替换为如下逗号表达式:

(test_c_check_uninit(1,“test.c”,22,10),temp3)

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数定义代码插入位置集合的set<SourceLocation>类型容器变量 defLocSet,表示test.c需要变量使用前未初始化检测相关函数的定义。

步骤S8104,如果当前节点s为自增或自减表达式++a8104,--a8104,a8104++或 a8104--,考察该节点:如果a8104不是变量声明引用,忽略节点s,不进行处理,否则, 表明节点s为对变量值的引用,利用步骤S8101得到的变量声明和编号的绑定关系,查 询得a8104所引用变量声明对应编号indexTemp8104,将表达式“a8104”替换为如下逗 号表达式:

*(fileName_check_uninit(indexTemp8104,fileNameS,lineS,columnS),&a8104)

其中fileName为节点s所在文件名,并作为fileName_check_uninit的一部分构成赋 值状态检测函数名,fileNameS,lineS,columnS分别为节点s所在文件名,行号和列 号;然后,从抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数定义 代码插入位置集合defLocSet,表示此文件需要变量使用前未初始化检测相关函数的定 义。

本例中,将第7行的表达式“temp3”替换为逗号表达式,特别地,由于在步骤S7104 中对第7行的表达式“result=--temp3”已进行了一次转换,所以综合两次操作表达式 “result=--temp3”将被替换为:

(pOverflowTemp=&(*(test_c_check_uninit(1,“test.c”,7,12),&temp3)),

check_overflow(*(int*)pOverflowTemp-(int)1,

      *(int*)pOverflowTemp-(long double)1,“test.c”,7,10),

(--(*(int*)pOverflowTemp)))

然后,从抽象语法树中获得表示test.c文件开始位置的SourceLocation类型变量loc, 加入到表示检测函数定义代码插入位置集合的set<SourceLocation>类型容器变量 defLocSet,表示test.c需要变量使用前未初始化检测相关函数的定义。

步骤S82,根据检测函数定义代码插入位置集合defLocSet,在相应位置插入变量 赋值状态数组fileName_varArray,赋值状态设置函数void fileName_set_uninit(long  index),赋值状态检测函数void fileName_check_uninit(long index,char*fileName, unsigned line,unsigned column)的定义;其中,fileName为插入位置所在文件名, 构成数组名和函数名的一部分;数组fileName_varArray的类型为_Bool,数组大小由 步骤S8101的变量声明总数的计数器决定,数组元素初始值均为0;函数 fileName_set_uninit的参数index代表变量声明对应的编号,函数 fileName_check_uninit的参数index代表变量声明对应的编号,参数fileName,line, column分别代表变量声明引用位置所在文件名,行号和列号;函数fileName_set_uninit 将数组元素fileName_varArray[index]的值设置为1,函数fileName_check_uninit对 数组元素fileName_varArray[index]的值进行检查:如果为1,说明未发生错误,不执 行任何动作;如果为0,则说明发生了变量使用前未初始化错误,报告错误发生位置的 文件名,行号和列号,并结束程序运行。

本例中,表示检测函数定义代码插入位置集合的set<SourceLocation>类型容器变 量defLocSet只包含有表示test.c文件开始位置的SourceLocation类型变量loc。所 以,在test.c文件开始位置插入定义代码如下:

步骤S9,将步骤S6、S7或/和S8中所有替换修改写回到相应的源文件中,并根据 本次修改文件更新已处理文件列表,其中,已处理文件列表用于步骤S61,步骤S71, 步骤S81中的判断。

本例中,将以上所有替换修改写回到test.c中,并更新表示已处理文件列表 set<string>类型容器变量parsedFileSet,将“test.c”字符串作为元素加入其中。

步骤S10,将经过变换的源代码目录或源代码文件按原有方式进行编译,生成可执 行文件。

本例中,按原有方式编译test.c,生成可执行文件。

步骤S11,将可执行文件部署在目标平台上并运行,当出现值计算错误时,插入的 代码可以自动检测到错误的发生,并准确定位和报告值计算错误在源代码中的位置。

本例中,运行生成的可执行文件,自动检测到值计算错误的发生,并报告在test.c 文件的第7行第12列发生使用前未初始化错误。

通过上述实施例可见,使用源代码变换方法,经过上述步骤的操作,在源代码中加 入值计算错误自动检测和源代码定位机制,即可准确地在软件运行过程中自动检测和定 位源代码中的值计算错误。

与传统检测技术相比,本实施例提供的值计算错误的自动检测和定位方法有如下优 点:

(1)本实施例通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断 潜在的值计算错误所在的源文件和代码行,并相应地进行源代码变换,使得在错误检测 中可以使用这些位置信息,因此具有更准确的错误定位功能。

(2)本实施例通过源代码变换技术,使得变换后的源代码可以使用原有编译器进 行编译和部署,因此具有更好的平台普适性。

(3)本实施例通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断 潜在的值计算错误的类型,并相应地进行源代码变换,减少了插入代码段的规模,简化 了插入代码段的复杂程度,从而获得了更高的运行时效率和性能。

本实施例可以解决安全关键系统、安全关键应用及常用软件系统中值计算错误检测 和定位的难题,能够准确地自动检测和定位错误,实现更好的平台普适性,和更高的运 行时性能和效率,从而提高软件开发的质量和软件维护的效率,有良好的社会效益。

以上所述仅是本发明的优选实施方式。应当指出,对于本领域的普通技术人员来说, 在不脱离本发明原理的前提下,还可以做出若干优化和改进,或者对其中部分技术特征 进行等同替换,这些改进和替换也应视为本发明的保护范围。

去获取专利,查看全文>

相似文献

  • 专利
  • 中文文献
  • 外文文献
获取专利

客服邮箱:kefu@zhangqiaokeyan.com

京公网安备:11010802029741号 ICP备案号:京ICP备15016152号-6 六维联合信息科技 (北京) 有限公司©版权所有
  • 客服微信

  • 服务号