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

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

Info

Publication number
CN104298594A
CN104298594A CN201410499170.9A CN201410499170A CN104298594A CN 104298594 A CN104298594 A CN 104298594A CN 201410499170 A CN201410499170 A CN 201410499170A CN 104298594 A CN104298594 A CN 104298594A
Authority
CN
China
Prior art keywords
expression formula
node
poverflowtemp
overflow
value
Prior art date
Legal status (The legal status is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the status listed.)
Granted
Application number
CN201410499170.9A
Other languages
English (en)
Other versions
CN104298594B (zh
Inventor
陈哲
朱云龙
魏欧
黄志球
Current Assignee (The listed assignees may be inaccurate. Google has not performed a legal analysis and makes no representation or warranty as to the accuracy of the list.)
Nanjing University of Aeronautics and Astronautics
Original Assignee
Nanjing University of Aeronautics and Astronautics
Priority date (The priority date is an assumption and is not a legal conclusion. Google has not performed a legal analysis and makes no representation as to the accuracy of the date listed.)
Filing date
Publication date
Application filed by Nanjing University of Aeronautics and Astronautics filed Critical Nanjing University of Aeronautics and Astronautics
Priority to CN201410499170.9A priority Critical patent/CN104298594B/zh
Publication of CN104298594A publication Critical patent/CN104298594A/zh
Application granted granted Critical
Publication of CN104298594B publication Critical patent/CN104298594B/zh
Expired - Fee Related legal-status Critical Current
Anticipated expiration legal-status Critical

Links

Landscapes

  • Debugging And Monitoring (AREA)
  • Information Retrieval, Db Structures And Fs Structures Therefor (AREA)

Abstract

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

Description

一种源代码中值计算错误的自动检测和定位方法
技术领域
本发明涉及一种源代码中值计算错误的自动检测和定位方法,属于计算机软件测试领域。
背景技术
值计算错误是一种普遍存在的软件安全漏洞,尤其是在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 doublecheck_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,栈结构structOverflowStackNode,压栈函数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,constchar*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(longindex),赋值状态检测函数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)本实施例通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断潜在的值计算错误的类型,并相应地进行源代码变换,减少了插入代码段的规模,简化了插入代码段的复杂程度,从而获得了更高的运行时效率和性能。
本实施例可以解决安全关键系统、安全关键应用及常用软件系统中值计算错误检测和定位的难题,能够准确地自动检测和定位错误,实现更好的平台普适性,和更高的运行时性能和效率,从而提高软件开发的质量和软件维护的效率,有良好的社会效益。
以上所述仅是本发明的优选实施方式。应当指出,对于本领域的普通技术人员来说,在不脱离本发明原理的前提下,还可以做出若干优化和改进,或者对其中部分技术特征进行等同替换,这些改进和替换也应视为本发明的保护范围。

Claims (9)

1.一种源代码中值计算错误的自动检测和定位方法,其特征在于:利用编译器对源代码进行语法分析,构造抽象语法树,通过遍历抽象语法树,基于表达式类型和用户指定检测的值计算错误类型,判断是否存在值计算错误的潜在风险;对可能产生值计算错误的表达式进行源代码变换,加入值计算错误检测和源代码定位的机制;编译执行变换后的源代码,执行后的源代码程序会自动判断值计算错误的发生,并准确报告错误对应的源代码位置。
2.根据权利要求1所述的源代码中值计算错误的自动检测和定位方法,其特征在于:包括以下步骤:
步骤S1,选择需要变换的源代码目录,或者单个源代码文件;
步骤S2,指定需要检测的值计算错误类型:除数为0、值溢出、变量使用前未初始化中的一种或他们之间两种以上的组合;
步骤S3,将选择的源代码目录或文件复制到源代码变换的工作目录中;
步骤S4,对工作目录中的所有源文件进行宏扩展处理,并保存扩展结果到相应的源文件中;
步骤S5,遍历工作目录中的所有源文件,利用编译器生成符号表和抽象语法树;
步骤S6,如果指定需要检测的值计算错误类型中包括除数为0错误,则进行除数为0错误分析和源代码变换计算;
步骤S7,如果指定需要检测的值计算错误类型中包括值溢出错误,则进行值溢出错误分析和源代码变换计算;
步骤S8,如果指定需要检测的值计算错误类型中包括变量使用前未初始化错误,则进行变量使用前未初始化错误分析和源代码变换计算;
步骤S9,将步骤S6、S7或/和S8中所有替换修改写回到相应的源文件中,并根据本次修改文件更新已处理文件列表;
步骤S10,将经过变换的源代码目录或源代码文件按原有方式进行编译,生成可执行文件;
步骤S11,将可执行文件部署在目标平台上并运行,当出现值计算错误时,插入的代码可以自动检测到错误的发生,并准确定位和报告值计算错误在源代码中的位置。
3.根据权利要求2所述的源代码中值计算错误的自动检测和定位方法,其特征在于:所述步骤S4中的宏扩展处理包括:
S41,利用编译器的词法分析器对文件进行词法分析,词法分析器返回经过宏扩展处理之后的词法单元;
S42,针对扩展自宏的词法单元,其属性中包括宏扩展之后的内容和宏扩展的位置,用扩展之后的内容替换宏扩展位置的原有内容。
4.根据权利要求3所述的源代码中值计算错误的自动检测和定位方法,其特征在于:所述步骤S6中除数为0错误分析和源代码变换计算的方法如下:利用函数调用,在返回除数给除法表达式之前,对除数进行检查,如果除数为0,则报告错误发生的源代码位置并结束程序,如果除数不为0,则正常地将除数返回给除法表达式。
5.根据权利要求4所述的源代码中值计算错误的自动检测和定位方法,其特征在于:所述步骤S7中值溢出错误分析和源代码变换计算的方法如下:将可能产生值溢出错误的表达式的操作数进行类型提升,然后将类型提升后的表达式值和原表达式值进行比较,如果相同则说明未发生溢出,将原表达式值返回,如果不相同则说明发生溢出,报告错误发生的源代码位置并结束程序。
6.根据权利要求5所述的源代码中值计算错误的自动检测和定位方法,其特征在于:所述步骤S8中变量使用前未初始化错误分析和源代码变换计算的方法如下:记录每一个非全局且声明时未赋初值的变量的赋值状态,检查对于变量的引用,如果是对变量的赋值,则更新该变量的赋值状态记录,如果是对变量值的使用,则检查该变量的赋值状态记录,未赋值则表示出现变量使用前未初始化的错误,已赋值则表示没有出现该错误。
7.根据权利要求6所述的源代码中值计算错误的自动检测和定位方法,其特征在于:所述步骤S6中除数为0错误分析和源代码变换计算的方法包括以下步骤:
步骤S61,遍历编译器生成的抽象语法树,如果当前节点s所在文件路径不在工作目录下,或已经存在于已处理文件列表中,则忽略该节点,不进行处理;
步骤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检测相关函数的声明;
步骤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检测相关函数的声明;
步骤S62,对于包含主函数声明的文件,在文件开始位置插入检测函数long doublecheck_zero(long double num,const char*fileName,unsigned line,unsigned column)的定义,其中参数num代表除数,参数fileName,line,column分别代表运算发生位置所在文件名,行号和列号;检测函数check_zero判断表示除数的参数num是否为0,如果为0,则报告除0错误发生位置的文件名,行号和列号,并结束程序运行,否则将除数作为函数的返回值返回;
步骤S63,根据检测函数声明插入位置集合declLocSet,在相应位置插入检测函数long double check_zero(long double num,const char*fileName,unsigned line,unsigned column)的声明;其中,参数num代表除数,参数fileName,line,column分别代表运算发生位置所在文件名,行号和列号;然后,将检测函数声明插入位置集合declLocSet重置为空。
8.根据权利要求7所述的源代码中值计算错误的自动检测和定位方法,其特征在于:所述步骤S7中值溢出错误分析和源代码变换计算的方法包括以下步骤:
步骤S71,遍历编译器生成的抽象语法树,如果当前节点s所在文件路径不在工作目录下,或已经存在于已处理文件列表中,则忽略该节点,不进行处理;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤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,表示此文件需要值溢出检测相关函数的声明;
步骤S72,对于包含主函数声明的文件,在文件开始位置插入空类型指针pOverflowTemp,long double类型变量overflowTemp1和overflowTemp2,栈结构structOverflowStackNode,压栈函数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进行比较:如果两个参数值相同,说明未发生值溢出,不执行任何动作;如果两个参数值不相同,则说明发生了值溢出,报告值溢出错误发生位置的文件名,行号和列号,清空栈并结束程序运行;
步骤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,constchar*fileName,unsigned line,unsigned column)的声明;其中,压栈函数overflowStackPush的参数num代表需要存储的表达式值,检测函数check_overflow的参数num1和num2分别代表类型提升前表达式值和类型提升后表达式值,参数fileName,line,column分别代表运算发生位置所在文件名,行号和列号;然后,将检测函数声明插入位置集合declLocSet重置为空。
9.根据权利要求8所述的源代码中值计算错误的自动检测和定位方法,其特征在于:所述步骤S8中变量使用前未初始化错误分析和源代码变换计算的方法包括以下步骤:
步骤S81,遍历编译器生成的抽象语法树,如果当前节点s所在文件路径不在工作目录下,或已经存在于已处理文件列表中,则忽略该节点,不进行处理;
步骤S8101,如果当前节点s为声明语句,则遍历声明语句中的每一个声明a8101,并通过抽象语法树获得声明a8101的属性;如果声明a8101同时满足如下条件:(1)为变量声明;(2)非全局变量声明;(3)非函数参数声明;(4)声明没有给出初始化表达式,则将变量声明a8101绑定一个编号varIndex;编号从1开始依次选取,所以varIndex也作为记录变量声明总数的计数器;
步骤S8102,如果当前节点s为变量声明引用a8102,考察节点s在抽象语法树中的父节点;如果s的父节点为隐含左值向右值转换表达式,自增表达式或自减表达式,忽略节点s,不进行处理,否则,表明节点s为对变量的赋值,利用步骤S8101得到的变量声明和编号的绑定关系,查询得a8102所引用变量声明对应编号indexTemp8102,将表达式“a8102”替换为如下逗号表达式:
*(fileName_set_uninit(indexTemp8102),&(a8102))
其中fileName为节点s所在文件名,并作为fileName_set_uninit的一部分构成变量赋值状态设置函数名;然后,从抽象语法树中获得节点s所在文件的开始位置locS,加入到检测函数定义代码插入位置集合defLocSet,表示此文件需要变量使用前未初始化检测相关函数的定义;
步骤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,表示此文件需要变量使用前未初始化检测相关函数的定义;
步骤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,表示此文件需要变量使用前未初始化检测相关函数的定义;
步骤S82,根据检测函数定义代码插入位置集合defLocSet,在相应位置插入变量赋值状态数组fileName_varArray,赋值状态设置函数void fileName_set_uninit(longindex),赋值状态检测函数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,则说明发生了变量使用前未初始化错误,报告错误发生位置的文件名,行号和列号,并结束程序运行。
CN201410499170.9A 2014-09-25 2014-09-25 一种源代码中值计算错误的自动检测和定位方法 Expired - Fee Related CN104298594B (zh)

Priority Applications (1)

Application Number Priority Date Filing Date Title
CN201410499170.9A CN104298594B (zh) 2014-09-25 2014-09-25 一种源代码中值计算错误的自动检测和定位方法

Applications Claiming Priority (1)

Application Number Priority Date Filing Date Title
CN201410499170.9A CN104298594B (zh) 2014-09-25 2014-09-25 一种源代码中值计算错误的自动检测和定位方法

Publications (2)

Publication Number Publication Date
CN104298594A true CN104298594A (zh) 2015-01-21
CN104298594B CN104298594B (zh) 2018-03-02

Family

ID=52318329

Family Applications (1)

Application Number Title Priority Date Filing Date
CN201410499170.9A Expired - Fee Related CN104298594B (zh) 2014-09-25 2014-09-25 一种源代码中值计算错误的自动检测和定位方法

Country Status (1)

Country Link
CN (1) CN104298594B (zh)

Cited By (10)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN104572470A (zh) * 2015-01-26 2015-04-29 中国人民解放军理工大学 一种基于蜕变关系的整数溢出故障检测方法
CN105389195A (zh) * 2015-10-27 2016-03-09 北京理工大学 一种基于代码替换和正则表达式的静态分析工具改进方法
CN106897211A (zh) * 2015-12-21 2017-06-27 阿里巴巴集团控股有限公司 针对混淆脚本语言的定位方法和系统
CN106940654A (zh) * 2017-02-15 2017-07-11 南京航空航天大学 源代码中内存错误的自动检测和定位方法
CN107247668A (zh) * 2017-06-07 2017-10-13 成都四象联创科技有限公司 代码自动检测和校正方法
CN108459954A (zh) * 2017-02-22 2018-08-28 腾讯科技(深圳)有限公司 应用程序漏洞检测方法和装置
CN108932192A (zh) * 2017-05-22 2018-12-04 南京大学 一种基于抽象语法树的Python程序类型缺陷检测方法
CN110457869A (zh) * 2019-07-23 2019-11-15 Oppo广东移动通信有限公司 程序编译加密方法、装置、存储介质及电子设备
CN110990263A (zh) * 2019-11-09 2020-04-10 上海集成电路研发中心有限公司 一种测试案例集的自动生成器及生成方法
CN113448851A (zh) * 2021-06-29 2021-09-28 中国工商银行股份有限公司 一种rust编程语言编译器的自动化测试方法及系统

Citations (1)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN103778061A (zh) * 2014-01-17 2014-05-07 南京航空航天大学 数组越界错误的自动检测和校正方法

Patent Citations (1)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN103778061A (zh) * 2014-01-17 2014-05-07 南京航空航天大学 数组越界错误的自动检测和校正方法

Non-Patent Citations (2)

* Cited by examiner, † Cited by third party
Title
XINKAI LI 等: "A Method for Hensel Code Overflow Detection", 《MARCH 2012 ACM SIGAPP APPLIED COMPUTING REVIEW》 *
王文俊: "C/C++程序缓冲区越界静态检测研究", 《中国优秀硕士学位论文全文数据库信息科技辑》 *

Cited By (17)

* Cited by examiner, † Cited by third party
Publication number Priority date Publication date Assignee Title
CN104572470A (zh) * 2015-01-26 2015-04-29 中国人民解放军理工大学 一种基于蜕变关系的整数溢出故障检测方法
CN104572470B (zh) * 2015-01-26 2017-10-03 中国人民解放军理工大学 一种基于蜕变关系的整数溢出故障检测方法
CN105389195A (zh) * 2015-10-27 2016-03-09 北京理工大学 一种基于代码替换和正则表达式的静态分析工具改进方法
CN105389195B (zh) * 2015-10-27 2018-08-10 北京理工大学 一种基于代码替换和正则表达式的静态分析工具改进方法
CN106897211A (zh) * 2015-12-21 2017-06-27 阿里巴巴集团控股有限公司 针对混淆脚本语言的定位方法和系统
WO2017107808A1 (zh) * 2015-12-21 2017-06-29 阿里巴巴集团控股有限公司 针对混淆脚本语言的定位方法和系统
CN106940654A (zh) * 2017-02-15 2017-07-11 南京航空航天大学 源代码中内存错误的自动检测和定位方法
CN106940654B (zh) * 2017-02-15 2020-08-14 南京航空航天大学 源代码中内存错误的自动检测和定位方法
CN108459954A (zh) * 2017-02-22 2018-08-28 腾讯科技(深圳)有限公司 应用程序漏洞检测方法和装置
CN108932192A (zh) * 2017-05-22 2018-12-04 南京大学 一种基于抽象语法树的Python程序类型缺陷检测方法
CN108932192B (zh) * 2017-05-22 2020-01-14 南京大学 一种基于抽象语法树的Python程序类型缺陷检测方法
CN107247668A (zh) * 2017-06-07 2017-10-13 成都四象联创科技有限公司 代码自动检测和校正方法
CN110457869A (zh) * 2019-07-23 2019-11-15 Oppo广东移动通信有限公司 程序编译加密方法、装置、存储介质及电子设备
CN110990263A (zh) * 2019-11-09 2020-04-10 上海集成电路研发中心有限公司 一种测试案例集的自动生成器及生成方法
CN110990263B (zh) * 2019-11-09 2024-03-15 上海集成电路研发中心有限公司 一种测试案例集的自动生成器及生成方法
CN113448851A (zh) * 2021-06-29 2021-09-28 中国工商银行股份有限公司 一种rust编程语言编译器的自动化测试方法及系统
CN113448851B (zh) * 2021-06-29 2024-06-21 中国工商银行股份有限公司 一种rust编程语言编译器的自动化测试方法及系统

Also Published As

Publication number Publication date
CN104298594B (zh) 2018-03-02

Similar Documents

Publication Publication Date Title
CN104298594A (zh) 一种源代码中值计算错误的自动检测和定位方法
Baldoni et al. A survey of symbolic execution techniques
US10423518B2 (en) Systems and methods for analyzing violations of coding rules
Lee et al. TIE: Principled reverse engineering of types in binary programs
Reder et al. Computing repair trees for resolving inconsistencies in design models
CN101739339B (zh) 一种基于程序动态依赖关系的软件故障定位方法
Visser et al. A language designer's workbench: a one-stop-shop for implementation and verification of language designs
US8719802B2 (en) Interprocedural exception method
US9928156B2 (en) Missing include suggestions for external files
Feldthaus et al. Semi-automatic rename refactoring for JavaScript
US20130159964A1 (en) System and method for systematic error injection in generated code
Hassan et al. Rudsea: recommending updates of dockerfiles via software environment analysis
CN103577324A (zh) 移动应用中隐私信息泄露的静态检测方法
CN110989997A (zh) 基于定理证明的形式化验证方法
Lin et al. Clone-based and interactive recommendation for modifying pasted code
Ren et al. Making smart contract development more secure and easier
Melo et al. Inference of static semantics for incomplete C programs
CN103914379A (zh) 故障自动注入与故障检测的方法及其系统
Cuoq et al. Fan-C, a Frama-C plug-in for data flow verification
CN111966578A (zh) 一种安卓兼容性缺陷修复效果的自动化评估方法
Demange et al. Mechanizing conventional SSA for a verified destruction with coalescing
Zou et al. {D-Helix}: A Generic Decompiler Testing Framework Using Symbolic Differentiation
Krijnen et al. Translation certification for smart contracts
Matoussi et al. Loop aware ir-level annotation framework for performance estimation in native simulation
Chatterjee et al. A low-level memory model and an accompanying reachability predicate

Legal Events

Date Code Title Description
C06 Publication
PB01 Publication
C10 Entry into substantive examination
SE01 Entry into force of request for substantive examination
GR01 Patent grant
GR01 Patent grant
CF01 Termination of patent right due to non-payment of annual fee
CF01 Termination of patent right due to non-payment of annual fee

Granted publication date: 20180302