CN103778061B - 数组越界错误的自动检测和校正方法 - Google Patents
数组越界错误的自动检测和校正方法 Download PDFInfo
- Publication number
- CN103778061B CN103778061B CN201410022323.0A CN201410022323A CN103778061B CN 103778061 B CN103778061 B CN 103778061B CN 201410022323 A CN201410022323 A CN 201410022323A CN 103778061 B CN103778061 B CN 103778061B
- Authority
- CN
- China
- Prior art keywords
- pointer
- function
- parameter
- mnt
- aav
- 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.)
- Expired - Fee Related
Links
- 238000001514 detection method Methods 0.000 title claims abstract description 30
- 238000012937 correction Methods 0.000 claims abstract description 27
- 238000007689 inspection Methods 0.000 claims abstract description 18
- 230000009466 transformation Effects 0.000 claims abstract description 17
- 238000006243 chemical reaction Methods 0.000 claims abstract description 14
- 230000006870 function Effects 0.000 claims description 302
- 230000014509 gene expression Effects 0.000 claims description 175
- 230000015654 memory Effects 0.000 claims description 49
- 238000003491 array Methods 0.000 claims description 18
- 239000011800 void material Substances 0.000 claims description 12
- 238000004364 calculation method Methods 0.000 claims description 5
- 238000000034 method Methods 0.000 description 43
- 238000012360 testing method Methods 0.000 description 16
- 238000005516 engineering process Methods 0.000 description 15
- 230000008569 process Effects 0.000 description 13
- 230000008901 benefit Effects 0.000 description 8
- 238000003860 storage Methods 0.000 description 7
- 238000013522 software testing Methods 0.000 description 5
- 230000003068 static effect Effects 0.000 description 5
- 238000013459 approach Methods 0.000 description 4
- 230000008859 change Effects 0.000 description 4
- 238000013461 design Methods 0.000 description 4
- 238000009826 distribution Methods 0.000 description 4
- 238000004590 computer program Methods 0.000 description 3
- 238000010586 diagram Methods 0.000 description 3
- 230000006399 behavior Effects 0.000 description 2
- 230000007423 decrease Effects 0.000 description 2
- 230000000694 effects Effects 0.000 description 2
- 238000012423 maintenance Methods 0.000 description 2
- 238000003672 processing method Methods 0.000 description 2
- 238000012827 research and development Methods 0.000 description 2
- RZVHIXYEVGDQDX-UHFFFAOYSA-N 9,10-anthraquinone Chemical compound C1=CC=C2C(=O)C3=CC=CC=C3C(=O)C2=C1 RZVHIXYEVGDQDX-UHFFFAOYSA-N 0.000 description 1
- 241000208340 Araliaceae Species 0.000 description 1
- 235000005035 Panax pseudoginseng ssp. pseudoginseng Nutrition 0.000 description 1
- 235000003140 Panax quinquefolius Nutrition 0.000 description 1
- 230000009471 action Effects 0.000 description 1
- 238000004458 analytical method Methods 0.000 description 1
- 230000003466 anti-cipated effect Effects 0.000 description 1
- 230000004888 barrier function Effects 0.000 description 1
- 230000009286 beneficial effect Effects 0.000 description 1
- 230000005540 biological transmission Effects 0.000 description 1
- 238000007796 conventional method Methods 0.000 description 1
- 230000008878 coupling Effects 0.000 description 1
- 238000010168 coupling process Methods 0.000 description 1
- 238000005859 coupling reaction Methods 0.000 description 1
- 238000005034 decoration Methods 0.000 description 1
- 230000007812 deficiency Effects 0.000 description 1
- 230000005611 electricity Effects 0.000 description 1
- 238000004880 explosion Methods 0.000 description 1
- 235000008434 ginseng Nutrition 0.000 description 1
- 230000006872 improvement Effects 0.000 description 1
- 230000003993 interaction Effects 0.000 description 1
- 230000004807 localization Effects 0.000 description 1
- 238000004519 manufacturing process Methods 0.000 description 1
- 230000007246 mechanism Effects 0.000 description 1
- 239000000203 mixture Substances 0.000 description 1
- 238000012986 modification Methods 0.000 description 1
- 230000004048 modification Effects 0.000 description 1
- 230000003287 optical effect Effects 0.000 description 1
- 238000012795 verification Methods 0.000 description 1
Landscapes
- Debugging And Monitoring (AREA)
Abstract
本发明提供一种数组越界错误的自动检测和校正方法,包括:选择待变换的源代码;利用编译器生成源代码的符号表和抽象语法树;遍历抽象语法树,构造指针依赖图,并进行源代码变换计算;在源代码中将需替换的部分源代码进行替换;将按照数组越界检测策略和校正策略生成的函数定义写入变换后源代码的开头部分;将变换后的源代码用原编译器进行编译;把生成的可执行文件部署到目标系统并运行,自动检测和校正数组越界错误,并准确报告错误对应的源代码位置。本发明提供的数组越界错误的自动检测和校正方法具有更准确的错误定位功能,更好的运行时效率和性能,更自动化的运行时错误校正功能。
Description
技术领域
本发明涉及计算机软件测试和校正技术领域,特别涉及一种数组越界错误的自动检测和校正方法。
背景技术
缓冲区溢出是一种非常危险的软件漏洞,并广泛存在于各种应用软件中。缓冲区溢出漏洞可能导致软件行为异常、内存访问错误或系统崩溃,也可被黑客用来攻击有价值的软件系统。目前,缓冲区溢出问题已经成为造成软件漏洞的主要原因。例如,根据US-CERT漏洞数据库统计资料可知,在20个最严重的漏洞中,就有11个是由缓冲区溢出引起。尤其对于那些用于控制安全关键工业系统的嵌入式控制软件(例如,飞行控制软件、高速列车控制软件、核电站控制软件等)和安全关键应用软件系统(例如,银行交易软件、网上交易软件等),当因缓冲区溢出漏洞引起软件失效、系统故障或黑客攻击,损失将非常惨重。因此,有效的缓冲区溢出检测和校正技术是软件研发和维护中的重要问题。
软件中缓冲区的内存分配包括两种方式:静态内存分配和动态内存分配。静态内存分配主要指源代码中变量和数组的定义,而动态内存分配主要指使用malloc等内存管理函数为软件分配的堆空间。通常,不带操作系统或内存管理模块的嵌入式工业控制系统不支持动态内存分配。因此,在这样的系统中,数组越界访问成为了缓冲区溢出的主要表现形式。也就是说,检测和校正软件中的数组越界错误是避免缓冲区溢出的主要方式。
目前,现有的检测数组越界错误的方法分为两种类型:静态方法和动态方法。
静态方法是指通过分析软件设计模型或者源代码来检验错误的方法,而不需要实际运行该软件。除人工代码走查之外,静态方法的一个主流技术是模型检验技术,例如SPIN模型检验器等。模型检验工具一般通过抽象建模,运行验证,生成和分析反例等步骤来检验设计模型的正确性。例如,业内曾经使用SPIN模型检验器对某型国产飞机的飞行控制系统的缓冲区控制模块的设计模型进行了验证,准确地找出了由模块间复杂交互行为引起的数组越界错误。模型检验技术的优点在于,可以对软件所有可能的行为进行穷举搜索,确保结果的完备性。然而,该技术的不足之处在于:1、由于模型检验技术本身的计算复杂性是PSPACE完全的,因此它与生俱来的状态爆炸问题使得该技术很难直接被应用于较大规模软件的验证,例如超过10000行代码的软件;2、由于模型检验技术通常是对软件系统抽象出来的设计模型进行验证,而不是全部源代码,所以无法确保该软件的实际实现是正确的,即无法确保源代码的正确性。
动态方法是指通过运行软件,并在软件运行过程中检测错误的方法。动态方法的一个主流技术是软件测试技术。软件测试工具一般通过编译源代码、运行待测试软件等步骤,在软件运行过程中根据设计的测试用例注入测试数据,通过对软件的输出进行分析(例如,与测试用例的预计输出进行对比),来观察软件运行是否正确,检测软件是否存在错误。软件测试技术的优点在于,有一定的自动化功能,可以进行测试用例管理、批量测试和回归测试。然而,该技术的不足之处在于:1、由于不直接面对源代码,无法准确定位导致错误发生的源代码位置;2、由于错误定位不准确,为软件的开发调试和校正造成了障碍。
另一种有效的动态方法是将软件在一个虚拟机上运行,该虚拟机可以模拟内存管理模块,从而检测软件中的数组越界错误。例如JAVA虚拟机就是一个典型的代表。虚拟机技术的优点在于,由于整个软件都处于被监控的状态,因此检测结果非常准确。然而,该技术的不足之处在于:1、由于虚拟机对软件的解释执行,使得软件运行负载过大,以至于软件效率和性能降低非常明显;2、对于嵌入式安全关键工业控制系统,由于高实时性和内存资源受限的要求,这样的效率和性能降低往往不能被接受,因此这种方法并不实用。
在检测到错误的存在后,就需要对错误进行校正,比如修改源代码。对于校正数组越界访问错误,常用的方法是人工调试和校正。也就是说,在软件测试阶段,根据软件测试工具的测试报告,由程序员使用代码调试工具,人工分析源代码的执行过程来定位错误。这一方法的优点是容易操作,不需要使用额外的工具。然而,该技术的不足之处在于:1、当源代码规模较大或者功能较为复杂,程序员不一定能准确定位错误在源代码中的位置,从而无法正确地修改源代码;2、当软件已经被部署到目标平台上,在实际运行过程中出现的数组越界访问错误无法通过这种方法进行校正,因此可能会引起软件失效、系统故障或黑客攻击。
因此,有必要提供一种新的数组越界错误的自动检测和校正方法,以实现更准确的错误定位功能,更好的运行时效率和性能,以及更自动化的运行时错误校正功能,从而克服现有的检测数组越界错误的方法中存在的技术问题。
发明内容
为了克服上述已有技术存在的不足,本发明的目的旨在提供一种新的检测和校正数组越界错误的方法,通过使用源代码变换技术,将源代码变换为带有自动检测和校正功能的源代码,使得可以在软件运行过程中自动检测和校正软件中数组越界错误,以实现更准确的错误定位功能,更好的运行时效率和性能,以及更自动化的运行时错误校正功能,从而克服现有的检测数组越界错误的方法中存在的技术问题。
本发明提供一种数组越界错误的自动检测和校正方法,其特征在于,包括:步骤1、选择待变换的源代码项目目录,或者单个源代码文件;步骤2、针对待变换的源代码,利用编译器生成源代码的符号表和抽象语法树;步骤3、遍历抽象语法树中的所有结点,构造指针依赖图,并进行源代码变换计算;步骤4、根据步骤3的计算结果,在源代码中将需替换的部分源代码进行替换,生成变换后的源代码,并保存到新项目目录或新文件;步骤5、定义数组越界检测策略和校正策略,并将这些策略转化为函数定义,将按照策略生成的函数定义写入变换后源代码的开头部分;步骤6、将变换后的项目目录或文件用原编译器进行编译,生成目标系统上的可执行文件;步骤7、把生成的可执行文件部署到目标系统并运行,当可执行文件运行到那些替换或插入的代码段时,将自动检测和校正数组越界错误,并准确报告错误对应的源代码位置。
进一步地,所述遍历抽象语法树中的所有结点,构造指针依赖图,并进行源代码变换计算,包括:指针依赖图是一个有向图二元组(V,E),其中:V是源代码中的指针集合并构成指针依赖图中的结点集合,E是源代码中的指针依赖关系集合并构成指针依赖图中的有向边集合;在遍历抽象语法树的过程中,根据当前遍历到的结点s的类型进行如下操作之一:向指针依赖图中加入指针依赖关系,将数组下标访问表达式和指针访问表达式替换为函数调用,将函数声明、函数定义、函数调用表达式替换为新的表达式。
进一步地,所述在遍历抽象语法树的过程中,根据当前遍历到的结点s的类型进行如下操作之一:向指针依赖图中加入指针依赖关系,将数组下标访问表达式和指针访问表达式替换为函数调用,将函数声明、函数定义、函数调用表达式替换为新的表达式,包括:在遍历抽象语法树的过程中,对于当前遍历到的结点s,得到其所在的源文件名filename和代码行号loc,并根据结点s的类型分别进行如下操作:
操作1、如果结点s是一个带初始值的指针声明type*p=expr,其中p为指针名,expr为表达式,且expr中包含一个支配指针q,则将(p,q)作为一条边加入指针依赖图;
操作2、如果结点s是一个指针赋值表达式p=expr,其中p为指针名,expr为表达式,且expr中包含一个支配指针q,则将(p,q)作为一条边加入指针依赖图;
操作3、如果结点s是一个数组下标表达式p[expr],其中p为指针名或数组名,expr为表达式,则根据p的类型分别进行如下操作:
操作31、如果p是符号表中已定义的一个数组,则从符号表中获取该数组的类型type和长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(p+expr,p,p+len,filename,loc)))
其中,函数参数p+expr表示数组下标表达式访问的内存地址,函数参数p表示数组的起始地址,函数参数p+len表示数组的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
操作32、如果p是所在函数声明的第n个形式参数,则从符号表中获取该指针的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(p+expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数p+expr表示数组下标表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
操作33、如果p是不满足以上操作31、操作32中的两种情况的指针,则从指针依赖图中获取其最终依赖指针q,如果q不存在,则认为源代码中存在指针使用前未赋初值的错误,并报错,如果q存在,则根据q的类型分别进行如下操作:
操作331、如果q是符号表中已定义的一个数组,则从符号表中获取指针p的类型type和数组q的长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(p+expr,q,q+len,filename,loc)))
其中,函数参数p+expr表示数组下标表达式访问的内存地址,函数参数q表示数组的起始地址,函数参数q+len表示数组的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
操作332、如果q是所在函数声明的第n个形式参数,则从符号表中获取指针p的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(p+expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数p+expr表示数组下标表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
操作333、如果q是不满足以上操作331、操作332中的两种情况的指针,则认为源代码中存在指针使用前未赋初值的错误,并报错;
操作4、如果结点s是一个指针访问表达式*expr,其中expr为表达式,且expr中包含一个支配指针p,则根据p的类型分别进行如下操作:
操作41、如果p是符号表中已定义的一个数组,则从符号表中获取该数组的类型type和长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(expr,p,p+len,filename,loc)))
其中,函数参数expr表示指针访问表达式访问的内存地址,函数参数p表示数组的起始地址,函数参数p+len表示数组的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
操作42、如果p是所在函数声明的第n个形式参数,则从符号表中获取该指针的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数expr表示指针访问表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
操作43、如果p是不满足以上操作41、操作42中的两种情况的指针,则从指针依赖图中获取其最终依赖指针q,如果q不存在,则认为源代码中存在指针使用前未赋初值的错误,并报错,如果q存在,则根据q的类型分别进行如下操作:
操作431、如果q是符号表中已定义的一个数组,则从符号表中获取指针p的类型type和数组q的长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(expr,q,q+len,filename,loc)))
其中,函数参数expr表示指针访问表达式访问的内存地址,函数参数q表示数组的起始地址,函数参数q+len表示数组的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
操作432、如果q是所在函数声明的第n个形式参数,则从符号表中获取指针p的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数expr表示指针访问表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
操作433、如果q是不满足以上操作431、操作432中的两种情况的指针,则认为源代码中存在指针使用前未赋初值的错误,并报错;
操作5、如果结点s是一个函数声明或函数定义表达式type func(…,type_nexpr_n,…),其中:第n个参数表达式expr_n为type_n类型的数组或指针声明p,省略号…表示其它参数表达式,则将该函数声明或函数定义替换为如下函数:
type func(…,type_n expr_n,void*__MNT_CHK_AAV_B_n,void*__MNT_CHK_AAV_E_n,…)
其中,省略号…表示原来的所有参数表达式,函数参数__MNT_CHK_AAV_B_n表示第n个参数表达式的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个参数表达式的结束地址;
操作6、如果结点s是一个函数调用表达式func(…,expr_n,…),其中:第n个参数表达式expr_n中包含一个支配指针p,省略号…表示其它参数表达式,则根据p的类型分别进行如下操作:
操作61、如果p是符号表中已定义的一个数组,则从符号表中获取该数组的长度len,然后将该表达式替换为如下函数调用:func(…,expr_n,p,p+len,…),其中,省略号…表示原来的所有参数表达式,函数参数p表示数组的起始地址,函数参数p+len表示数组的结束地址;
操作62、如果p是所在函数声明的第n个形式参数,则将该表达式替换为如下函数调用:
func(…,expr_n,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,…)
其中,省略号…表示原来的所有参数表达式,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址;
操作63、如果p是不满足以上操作61、操作62中的两种情况的指针,则从指针依赖图中获取其最终依赖指针q,如果q不存在,则认为源代码中存在指针使用前未赋初值的错误,并报错,如果q存在,则根据q的类型分别进行如下操作:
操作631、如果q是符号表中已定义的一个数组,则从符号表中获取数组q的长度len,然后将该表达式替换为如下函数调用:func(…,expr_n,q,q+len,…),其中,省略号…表示原来的所有参数表达式,函数参数q表示数组的起始地址,函数参数q+len表示数组的结束地址;
操作632、如果q是所在函数声明的第n个形式参数,则将该表达式替换为如下函数调用:
func(…,expr_n,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,…)
其中,省略号…表示原来的所有参数表达式,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址;
操作633、如果q是不满足以上操作631、操作632中的两种情况的指针,则认为源代码中存在指针使用前未赋初值的错误,并报错。
进一步地,所述步骤3包括:在指针依赖图中,从p开始沿有向边往前遍历,直到访问到一个没有后继结点的指针q,那么q就是p的最终依赖指针。
进一步地,所述步骤3包括:支配指针是指决定该表达式指向地址的主要指针变量;该表达式的其他部分决定相对于该指针指向地址的偏移量。
进一步地,所述检测策略包括:判断要访问的地址p是否在允许的范围[begin,end)之间,如果超出该范围,则报告数组越界错误。
进一步地,所述校正策略包括:当合法的内存范围为[begin,end),大小为n时,
如果访问地址p为下越界,则将p和begin之间的偏移量进行模n运算,并被end减后作为映射的合法访问地址;
如果访问地址p为上越界,则将p和end之间的偏移量进行模n运算,并加上begin作为映射的合法访问地址;
如果访问地址p在[begin,end)范围内,则不作任何计算;
任何的内存访问都能被映射到合法的内存范围[begin,end)内。
进一步地,所述步骤5包括:检测策略和校正策略直接通过源代码的方式说明;或者,使用特别定义的描述语言来说明,并通过语言自动转化工具自动翻译为源代码。
本发明提供的数组越界错误的自动检测和校正方法通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断潜在的数组越界错误所在的源文件和代码行,并相应地进行源代码变换,使得在错误检测中可以使用这些位置信息,因此具有更准确的错误定位功能。进一步地,本发明通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断潜在的数组越界错误的类型,并相应地进行源代码变换,减少了插入代码段的规模,简化了插入代码段的复杂程度,从而获得了更好的运行时效率和性能。进一步地,本发明通过对用户自定义检测策略和校正策略的集成和源代码变换,使得软件具有更自动化的运行时错误校正功能。
本发明附加的方面和优点将在下面的描述中部分给出,这些将从下面的描述中变得明显,或通过本发明的实践了解到。
附图说明
图1示出了根据本发明的数组越界错误的自动检测和校正方法的流程示意图。
具体实施方式
下面详细描述本发明的实施方式,所述实施方式的示例在附图中示出,其中自始至终相同或类似的标号表示相同或类似的元件或具有相同或类似功能的元件。下面通过参考附图描述的实施方式是示例性的,仅用于解释本发明,而不能解释为对本发明的限制。
本技术领域技术人员可以理解的是,除非特意声明,这里使用的单数形式“一”、“一个”、“所述”和“该”也可包括复数形式。应该进一步理解的是,本发明的说明书中使用的措辞“包括”是指存在所述特征、整数、步骤、操作、元件和/或组件,但是并不排除存在或添加一个或多个其他特征、整数、步骤、操作、元件、组件和/或它们的组。应该理解,当我们称元件被“连接”或“耦接”到另一元件时,它可以直接连接或耦接到其他元件,或者也可以存在中间元件。此外,这里使用的“连接”或“耦接”可以包括无线连接或耦接。这里使用的措辞“和/或”包括一个或更多个相关联的列出项的任一单元和全部组合。
本技术领域技术人员可以理解的是,除非另外定义,这里使用的所有术语(包括技术术语和科学术语)具有与本发明所属领域中的普通技术人员的一般理解相同的意义。还应该理解的是,诸如通用字典中定义的那些术语应该被理解为具有与现有技术的上下文中的意义一致的意义,并且除非像这里一样定义,不会用理想化或过于正式的含义来解释。
下文将对上述各步骤具体展开描述。图1示出了根据本发明的数组越界错误的自动检测和校正方法的流程示意图。如图1所示,本发明的目的是通过下述技术方案实现的,具体操作步骤如下:
步骤S11:选择待变换的源代码项目目录,或者单个源代码文件。
步骤S12:针对待变换的源代码,利用编译器生成源代码的符号表和抽象语法树。
步骤S13:遍历抽象语法树中的所有结点,构造指针依赖图,并进行源代码变换计算。
其中,指针依赖图是一个有向图二元组(V,E),其中V是源代码中的指针集合(也是图的结点集合),E是源代码中的指针依赖关系集合(也是图的有向边集合)。当指针对(p,q)存在于指针依赖图中,或者存在一条从p到q的有向路径时,指针q是指针p的依赖指针,即表示指针p所指向的数组就是指针q所指向的数组。当指针q是指针p的依赖指针,并且指针q没有依赖指针时,指针q是指针p的最终依赖指针。对于指针p,可以通过如下方法获取其最终依赖指针:在指针依赖图中,从p开始沿有向边往前遍历,直到访问到一个没有后继结点的指针q时,q就是p的最终依赖指针。
本步骤具体来说,在遍历抽象语法树的过程中,对于当前遍历到的结点s,得到其所在的源文件名filename和代码行号loc,并根据结点s的类型分别进行如下操作:
步骤S131:如果结点s是一个带初始值的指针声明type*p=expr,其中p为指针名,expr为表达式,且expr中包含一个支配指针q时,将(p,q)作为一条边加入指针依赖图。其中,支配指针是指决定该表达式指向地址的主要指针变量,而该表达式的其他部分决定相对于该指针指向地址的偏移量。
步骤S132:如果结点s是一个指针赋值表达式p=expr,其中p为指针名,expr为表达式,且expr中包含一个支配指针q时,将(p,q)作为一条边加入指针依赖图。
步骤S133:如果结点s是一个数组下标表达式p[expr],其中p为指针名或数组名,expr为表达式时,根据p的类型分别进行如下操作:
步骤S1331:当p是符号表中已定义的一个数组时,从符号表中获取该数组的类型type和长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(p+expr,p,p+len,filename,loc)))
其中,函数参数p+expr表示数组下标表达式访问的内存地址,函数参数p表示数组的起始地址,函数参数p+len表示数组的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
步骤S1332:当p是所在函数声明的第n个形式参数时,从符号表中获取该指针的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(p+expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数p+expr表示数组下标表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
步骤S1333:当p是不满足以上步骤S1331、步骤S1332中的两种情况的指针时,从指针依赖图中获取其最终依赖指针q。当q不存在时,认为源代码中存在指针使用前未赋初值的错误,并报错。当q存在时,根据q的类型分别进行如下操作:
步骤S13331:当q是符号表中已定义的一个数组时,从符号表中获取指针p的类型type和数组q的长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(p+expr,q,q+len,filename,loc)))
其中,函数参数p+expr表示数组下标表达式访问的内存地址,函数参数q表示数组的起始地址,函数参数q+len表示数组的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
步骤S13332:当q是所在函数声明的第n个形式参数时,从符号表中获取指针p的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(p+expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数p+expr表示数组下标表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
步骤S13333:当q是不满足以上步骤S13331、步骤S13332中的两种情况的指针时,认为源代码中存在指针使用前未赋初值的错误,并报错。
步骤S134:如果结点s是一个指针访问表达式*expr,其中expr为表达式,且expr中包含一个支配指针p时,根据p的类型分别进行如下操作:
步骤S1341:当p是符号表中已定义的一个数组时,从符号表中获取该数组的类型type和长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(expr,p,p+len,filename,loc)))
其中,函数参数expr表示指针访问表达式访问的内存地址,函数参数p表示数组的起始地址,函数参数p+len表示数组的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
步骤S1342:当p是所在函数声明的第n个形式参数时,从符号表中获取该指针的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数expr表示指针访问表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
步骤S1343:当p是不满足以上步骤S1341、步骤S1342中的两种情况的指针时,从指针依赖图中获取其最终依赖指针q。当q不存在时,认为源代码中存在指针使用前未赋初值的错误,并报错。当q存在时,根据q的类型分别进行如下操作:
步骤S13431:当q是符号表中已定义的一个数组时,从符号表中获取指针p的类型type和数组q的长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(expr,q,q+len,filename,loc)))
其中,函数参数expr表示指针访问表达式访问的内存地址,函数参数q表示数组的起始地址,函数参数q+len表示数组的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
步骤S13432:当q是所在函数声明的第n个形式参数时,从符号表中获取指针p的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数expr表示指针访问表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址,函数参数filename表示源文件名,函数参数loc表示代码行号;
步骤S13433:当q是不满足以上步骤S13431、步骤S13432中的两种情况的指针时,认为源代码中存在指针使用前未赋初值的错误,并报错。
步骤S135:如果结点s是一个函数声明或函数定义表达式type func(…,type_nexpr_n,…),其中:第n个参数表达式expr_n为type_n类型的数组或指针声明p,省略号…表示其它参数表达式时,将该函数声明或函数定义替换为如下函数:
type func(…,type_n expr_n,void*__MNT_CHK_AAV_B_n,void*__MNT_CHK_AAV_E_n,…)
其中,省略号…表示原来的所有参数表达式,函数参数__MNT_CHK_AAV_B_n表示第n个参数表达式的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个参数表达式的结束地址;
步骤S136:如果结点s是一个函数调用表达式func(…,expr_n,…),其中:第n个参数表达式expr_n中包含一个支配指针p,省略号…表示其它参数表达式时,根据p的类型分别进行如下操作:
步骤S1361:当p是符号表中已定义的一个数组时,从符号表中获取该数组的长度len,然后将该表达式替换为如下函数调用:func(…,expr_n,p,p+len,…),其中,省略号…表示原来的所有参数表达式,函数参数p表示数组的起始地址,函数参数p+len表示数组的结束地址;
步骤S1362:当p是所在函数声明的第n个形式参数时,将该表达式替换为如下函数调用:
func(…,expr_n,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,…)
其中,省略号…表示原来的所有参数表达式,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址;
步骤S1363:当p是不满足以上步骤S1361、步骤S1362中的两种情况的指针时,从指针依赖图中获取其最终依赖指针q。当q不存在时,认为源代码中存在指针使用前未赋初值的错误,并报错。当q存在时,根据q的类型分别进行如下操作:
步骤S13631:当q是符号表中已定义的一个数组时,从符号表中获取数组q的长度len,然后将该表达式替换为如下函数调用:func(…,expr_n,q,q+len,…),其中,省略号…表示原来的所有参数表达式,函数参数q表示数组的起始地址,函数参数q+len表示数组的结束地址;
步骤S13632:当q是所在函数声明的第n个形式参数时,将该表达式替换为如下函数调用:
func(…,expr_n,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,…)
其中,省略号…表示原来的所有参数表达式,函数参数__MNT_CHK_AAV_B_n表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n表示第n个形式参数的结束地址;
步骤S13633:当q是不满足以上步骤S13631、步骤S13632中的两种情况的指针时,认为源代码中存在指针使用前未赋初值的错误,并报错。
步骤S14:根据步骤S13的计算结果,在源代码中将需替换的部分源代码进行替换,生成变换后的源代码,并保存到新项目目录或新文件。
步骤S15:定义数组越界检测策略和校正策略,并将这些策略转化为函数__MNT_CHK_AAV的定义。其中,检测策略是指:如何判断对数组的访问是否超出数组的内存范围。例如,一种可行的检测策略为:判断要访问的地址p是否在允许的范围[begin,end)之间,当超出该范围时,报告数组越界错误。该策略可以被转化为如下源代码:
校正策略是指:如何将超出数组的内存范围的数组访问映射到合法的内存范围以内。例如,一种可行的校正策略为求模运算:假设合法的内存范围为[begin,end),大小为n,(1)当访问地址p为下越界时,将p和begin之间的偏移量进行模n运算,并被end减后作为映射的合法访问地址;(2)当访问地址p为上越界时,将p和end之间的偏移量进行模n运算,并加上begin作为映射的合法访问地址;(3)当访问地址p在[begin,end)范围内时,不作任何计算。这样,任何的内存访问都可以被映射到合法的内存范围[begin,end)内。该策略可以被转化为如下源代码:
检测策略和校正策略可以直接通过源代码的方式说明,也可以使用特别定义的描述语言来说明,然后通过语言自动转化工具自动翻译为源代码。最后将按照策略生成的函数__MNT_CHK_AAV的定义写入变换后源代码的开头部分。
步骤S16:将变换后的项目目录或文件用原编译器进行编译,生成目标系统上的可执行文件。
步骤S17:把生成的可执行文件部署到目标系统并运行,当可执行文件运行到那些替换或插入的代码段时,将自动检测和校正数组越界错误,并准确报告错误对应的源代码位置。
经过上述步骤的操作,即可准确地在软件运行过程中检测出数组越界错误,并进行自动校正。
有益效果:本发明提出的方法与已有技术相比较,有如下优点:
(1)本发明通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断潜在的数组越界错误所在的源文件和代码行,并相应地进行源代码变换,使得在错误检测中可以使用这些位置信息,因此具有更准确的错误定位功能。
(2)本发明通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断潜在的数组越界错误的类型,并相应地进行源代码变换,减少了插入代码段的规模,简化了插入代码段的复杂程度,从而获得了更好的运行时效率和性能。
(3)本发明通过对用户自定义检测策略和校正策略的集成和源代码变换,使得软件具有更自动化的运行时错误校正功能。
本发明可以解决安全关键嵌入式工业控制软件和应用软件系统的数组越界错误检测和校正中的难题,对于准确定位和校正错误,在软件研发和维护阶段提高软件产品的质量有非常重要的作用,有良好的社会效益。
下文将以本发明方法对一段C语言源代码进行检测和校正为例,进一步具体说明本发明的有关方法、流程及相关步骤。例如,源代码如下(文件名为array.c):
本发明方法的具体操作步骤如下:
步骤S21:选择待变换的源代码项目目录,或者单个源代码文件。
本实施例中,选择文件array.c。
步骤S22:针对待变换的源代码,利用编译器生成源代码的符号表和抽象语法树。
本实施例中,所述待变换的源代码为C/C++源代码。
步骤S23:遍历抽象语法树中的所有结点,构造指针依赖图,并进行源代码变换计算。
其中,指针依赖图是一个有向图二元组(V,E),其中V是源代码中的指针集合(也是图的结点集合),E是源代码中的指针依赖关系集合(也是图的有向边集合)。当指针对(p,q)存在于指针依赖图中,或者存在一条从p到q的有向路径时,指针q是指针p的依赖指针,即表示指针p所指向的数组就是指针q所指向的数组。当指针q是指针p的依赖指针,并且指针q没有依赖指针时,指针q是指针p的最终依赖指针。对于指针p,可以通过如下方法获取其最终依赖指针:在指针依赖图中,从p开始沿有向边往前遍历,直到访问到一个没有后继结点的指针q时,q就是p的最终依赖指针。
本步骤具体来说,在遍历抽象语法树的过程中,对于当前遍历到的结点s,得到其所在的源文件名filename和代码行号loc,并根据结点s的类型分别进行如下操作:
步骤S231:如果结点s是一个带初始值的指针声明type*p=expr,其中p为指针名,expr为表达式,且expr中包含一个支配指针q时,将(p,q)作为一条边加入指针依赖图。其中,支配指针是指决定该表达式指向地址的主要指针变量,而该表达式的其他部分决定相对于该指针指向地址的偏移量。
本实施例中,由于main函数中的指针声明“int*pa=a+2”,其中a为支配指针,因此将(main.pa,main.a)加入指针依赖图。
步骤S232:如果结点s是一个指针赋值表达式p=expr,其中p为指针名,expr为表达式,且expr中包含一个支配指针q时,将(p,q)作为一条边加入指针依赖图。
本实施例中,由于foo函数中的指针赋值表达式“pp=p+1”,其中p为支配指针,因此将(foo.pp,foo.p)加入指针依赖图。
步骤S233:如果结点s是一个数组下标表达式p[expr],其中p为指针名或数组名,expr为表达式时,根据p的类型分别进行如下操作:
步骤S2331:当p是符号表中已定义的一个数组时,从符号表中获取该数组的类型type和长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(p+expr,p,p+len,filename,loc)))
本实施例中,main函数中的表达式“a[i]”被替换为如下函数调用:
*((int*)(__MNT_CHK_AAV(a+i,a,a+10,"array.c",12)))
本实施例中,main函数中的表达式“a[i*2-2]”被替换为如下函数调用:
*((int*)(__MNT_CHK_AAV(a+(i*2-2),a,a+10,"array.c",15)))
步骤S2332:当p是所在函数声明的第n个形式参数时,从符号表中获取该指针的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(p+expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
本实施例中,foo函数中的表达式“p[i*2-2]”被替换为如下函数调用:
*((int*)(__MNT_CHK_AAV(p+(i*2-2),__MNT_CHK_AAV_B_1,__MNT_CHK_AAV_E_1,"array.c",4)))
步骤S2333:当p是不满足以上步骤S2331、步骤S2332中的两种情况的指针时,从指针依赖图中获取其最终依赖指针q。当q不存在时,认为源代码中存在指针使用前未赋初值的错误,并报错。当q存在时,根据q的类型分别进行如下操作:
步骤S23331:当q是符号表中已定义的一个数组时,从符号表中获取指针p的类型type和数组q的长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(p+expr,q,q+len,filename,loc)))
步骤S23332:当q是所在函数声明的第n个形式参数时,从符号表中获取指针p的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(p+expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
步骤S23333:当q是不满足以上步骤S23331、步骤S23332中的两种情况的指针时,认为源代码中存在指针使用前未赋初值的错误,并报错。
步骤S234:如果结点s是一个指针访问表达式*expr,其中expr为表达式,且expr中包含一个支配指针p时,根据p的类型分别进行如下操作:
步骤S2341:当p是符号表中已定义的一个数组时,从符号表中获取该数组的类型type和长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(expr,p,p+len,filename,loc)))
本实施例中,main函数中的表达式“*(a+i)”被替换为如下函数调用:
*((int*)(__MNT_CHK_AAV(a+i,a,a+10,"array.c",16)))
步骤S2342:当p是所在函数声明的第n个形式参数时,从符号表中获取该指针的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
本实施例中,foo函数中的表达式“*(p+i*2-1)”被替换为如下函数调用:
*((int*)(__MNT_CHK_AAV(p+i*2-1,__MNT_CHK_AAV_B_1,__MNT_CHK_AAV_E_1,"array.c",5)))
步骤S2343:当p是不满足以上步骤S2341、步骤S2342中的两种情况的指针时,从指针依赖图中获取其最终依赖指针q。当q不存在时,认为源代码中存在指针使用前未赋初值的错误,并报错。当q存在时,根据q的类型分别进行如下操作:
步骤S23431:当q是符号表中已定义的一个数组时,从符号表中获取指针p的类型type和数组q的长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(expr,q,q+len,filename,loc)))
本实施例中,由于(main.pa,main.a)在指针依赖图中,main函数中的表达式“*(pa+i-1)”被替换为如下函数调用:
*((int*)(__MNT_CHK_AAV(pa+i-1,a,a+10,"array.c",17)))
步骤S23432:当q是所在函数声明的第n个形式参数时,从符号表中获取指针p的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
本实施例中,由于(foo.pp,foo.p)在指针依赖图中,foo函数中的表达式“*(pp+i+1)”被替换为如下函数调用:
*((int*)(__MNT_CHK_AAV(pp+i+1,__MNT_CHK_AAV_B_1,__MNT_CHK_AAV_E_1,"array.c",6)))
步骤S23433:当q是不满足以上步骤S23431、步骤S23432中的两种情况的指针时,认为源代码中存在指针使用前未赋初值的错误,并报错。
步骤S235:如果结点s是一个函数声明或函数定义表达式type func(…,type_nexpr_n,…),其中:第n个参数表达式expr_n为type_n类型的数组或指针声明p时,将该函数声明或函数定义替换为如下函数:
type func(…,type_n expr_n,void*__MNT_CHK_AAV_B_n,void*__MNT_CHK_AAV_E_n,…)
本实施例中,foo函数的定义“void foo(int*p)”被替换为如下函数定义:
void foo(int*p,void*__MNT_CHK_AAV_B_1,void*__MNT_CHK_AAV_E_1)
步骤S236:如果结点s是一个函数调用表达式func(…,expr_n,…),其中:第n个参数表达式expr_n中包含一个支配指针p时,根据p的类型分别进行如下操作:
步骤S2361:当p是符号表中已定义的一个数组时,从符号表中获取该数组的长度len,然后将该表达式替换为如下函数调用:func(…,expr_n,p,p+len,…)。
本实施例中,main函数中的表达式“foo(a+1)”被替换为如下函数调用:foo(a+1,a,a+10)。
步骤S2362:当p是所在函数声明的第n个形式参数时,将该表达式替换为如下函数调用:
func(…,expr_n,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,…)
步骤S2363:当p是不满足以上步骤S2361、步骤S2362中的两种情况的指针时,从指针依赖图中获取其最终依赖指针q。当q不存在时,认为源代码中存在指针使用前未赋初值的错误,并报错。当q存在时,根据q的类型分别进行如下操作:
步骤S23631:当q是符号表中已定义的一个数组时,从符号表中获取数组q的长度len,然后将该表达式替换为如下函数调用:func(…,expr_n,q,q+len,…)。
本实施例中,由于(main.pa,main.a)在指针依赖图中,main函数中的表达式“foo(pa+2)”被替换为如下函数调用:foo(pa+2,a,a+10)。
步骤S23632:当q是所在函数声明的第n个形式参数时,将该表达式替换为如下函数调用:
func(…,expr_n,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,…)
步骤S23633:当q是不满足以上步骤S23631、步骤S23632中的两种情况的指针时,认为源代码中存在指针使用前未赋初值的错误,并报错。
步骤S24:根据步骤S23的计算结果,在源代码中将需替换的部分源代码进行替换,生成变换后的源代码,并保存到新项目目录或新文件。
本实施例中,进行源代码变换后生成的源代码(文件名array_out.c)如下:
步骤S25:定义数组越界检测策略和校正策略,并将这些策略转化为函数__MNT_CHK_AAV的定义。其中,检测策略是指:如何判断对数组的访问是否超出数组的内存范围。一种可行的检测策略为:判断要访问的地址p是否在允许的范围[begin,end)之间,当超出该范围时,报告数组越界错误。该策略可以被转化为如下源代码:
校正策略是指:如何将超出数组的内存范围的数组访问映射到合法的内存范围以内。一种可行的校正策略为求模运算:假设合法的内存范围为[begin,end),大小为n,(1)当访问地址p为下越界时,将p和begin之间的偏移量进行模n运算,并被end减后作为映射的合法访问地址;(2)当访问地址p为上越界时,将p和end之间的偏移量进行模n运算,并加上begin作为映射的合法访问地址;(3)当访问地址p在[begin,end)范围内时,不作任何计算。这样,任何的内存访问都可以被映射到合法的内存范围[begin,end)内。该策略可以被转化为如下源代码:
检测策略和校正策略可以直接通过源代码的方式说明,也可以使用特别定义的描述语言来说明,然后通过语言自动转化工具自动翻译为源代码。最后将按照策略生成的函数__MNT_CHK_AAV的定义写入变换后源代码的开头部分。
本实施例中,将以下函数写入array_out.c的开头部分:
步骤S26:将变换后的项目目录或文件用原编译器进行编译,生成目标系统上的可执行文件。
本实施例中,编译文件array_out.c。
步骤S27:把生成的可执行文件部署到目标系统并运行,当可执行文件运行到那些替换或插入的代码段时,将自动检测和校正数组越界错误,并准确报告错误对应的源代码位置。
本实施例中,由于检测策略和校正策略的使用,array.c源代码中第11-12行循环语句中的数组越界错误将在软件运行过程中被完全正确校正,并正确地得到数组所有元素的总和,并且准确报告源代码中第12行引起的数组越界错误。
通过上述实施例发现,通过源代码变换方法,将所有对数组的访问都替换为了函数调用,并在该函数中对数组越界错误进行自动检测和校正。因此,本发明能够准确地在软件运行过程中对数组越界错误进行自动检测和校正。
本发明可以解决安全关键嵌入式工业控制软件和应用软件系统的数组越界错误检测和校正中的难题,对于准确定位和校正错误,在软件研发和维护阶段提高软件产品的质量有非常重要的作用,有良好的社会效益。
本技术领域技术人员可以理解的是,本发明可以涉及用于执行本申请中所述操作中的一项或多项操作的设备。所述设备可以为所需的目的而专门设计和制造,或者也可以包括通用计算机中的已知设备,所述通用计算机有存储在其内的程序选择性地激活或重构。这样的计算机程序可以被存储在设备(例如,计算机)可读介质中或者存储在适于存储电子指令并分别耦联到总线的任何类型的介质中,所述计算机可读介质包括但不限于任何类型的盘(包括软盘、硬盘、光盘、CD-ROM、和磁光盘)、随机存储器(RAM)、只读存储器(ROM)、电可编程ROM、电可擦ROM(EPROM)、电可擦除可编程ROM(EEPROM)、闪存、磁性卡片或光线卡片。可读介质包括用于以由设备(例如,计算机)可读的形式存储或传输信息的任何机构。例如,可读介质包括随机存储器(RAM)、只读存储器(ROM)、磁盘存储介质、光学存储介质、闪存装置、以电的、光的、声的或其他的形式传播的信号(例如载波、红外信号、数字信号)等。
本技术领域技术人员可以理解的是,可以用计算机程序指令来实现这些结构图和/或框图和/或流图中的每个框以及这些结构图和/或框图和/或流图中的框的组合。可以将这些计算机程序指令提供给通用计算机、专业计算机或其他可编程数据处理方法的处理器来生成机器,从而通过计算机或其他可编程数据处理方法的处理器来执行的指令创建了用于实现结构图和/或框图和/或流图的框或多个框中指定的方法。
本技术领域技术人员可以理解的是,本发明中已经讨论过的各种操作、方法、流程中的步骤、措施、方案可以被交替、更改、组合或删除。进一步地,具有本发明中已经讨论过的各种操作、方法、流程中的其他步骤、措施、方案也可以被交替、更改、重排、分解、组合或删除。进一步地,现有技术中的具有与本发明中公开的各种操作、方法、流程中的步骤、措施、方案也可以被交替、更改、重排、分解、组合或删除。
以上所述仅是本发明的部分实施方式,应当指出,对于本技术领域的普通技术人员来说,在不脱离本发明原理的前提下,还可以做出若干改进和润饰,这些改进和润饰也应视为本发明的保护范围。
Claims (7)
1. 一种数组越界错误的自动检测和校正方法,其特征在于,包括:
步骤1、选择待变换的源代码项目目录,或者单个源代码文件;
步骤2、针对待变换的源代码,利用编译器生成源代码的符号表和抽象语法树;
步骤3、遍历抽象语法树中的所有结点,构造指针依赖图,并进行源代码变换计算,其中:所述指针依赖图是一个有向图二元组,所述有向图二元组包括源代码中的指针集合和源代码中的指针依赖关系集合,所述源代码中的指针集合构成指针依赖图中的结点集合,所述源代码中的指针依赖关系集合构成指针依赖图中的有向边集合;所述遍历抽象语法树中的所有结点,构造指针依赖图,并进行源代码变换计算,进一步包括:
在遍历抽象语法树的过程中,根据当前遍历到的结点s的类型进行如下操作之一:
向指针依赖图中加入指针依赖关系,
将数组下标访问表达式和指针访问表达式替换为__MNT_CHK_AAV 函数的调用,
将函数声明、函数定义、函数调用表达式替换为新的表达式;
步骤4、根据步骤3的计算结果,在源代码中将需替换的部分源代码进行替换,生成变换后的源代码,并保存到新项目目录或新文件;
步骤5、定义数组越界检测策略和校正策略,并将这些策略转化为函数__MNT_CHK_AAV的定义,将按照策略生成的函数__MNT_CHK_AAV 的定义写入变换后源代码的开头部分;
步骤6、将变换后的项目目录或文件用原编译器进行编译,生成目标系统上的可执行文件;
步骤7、将生成的可执行文件部署到目标系统并运行,当可执行文件运行到已替换或插入的代码段时,将自动检测和校正数组越界错误,并准确报告错误对应的源代码位置。
2. 如权利要求1所述的数组越界错误的自动检测和校正方法,其特征在于,所述在遍历抽象语法树的过程中,根据当前遍历到的结点s 的类型进行如下操作之一:向指针依赖图中加入指针依赖关系,将数组下标访问表达式和指针访问表达式替换为__MNT_CHK_AAV函数的调用,将函数声明、函数定义、函数调用表达式替换为新的表达式,进一步包括:
在遍历抽象语法树的过程中,对于当前遍历到的结点s,得到其所在的源文件名filename
和代码行号loc,并根据当前遍历到的结点s 的类型分别进行如下操作:
操作1、如果结点s 是一个带初始值的指针声明type*p=expr,其中p 为指针名,expr 为表达式,且expr 中包含一个支配指针q,则将指针对(p,q) 作为一条边加入指针依赖图;
操作2、如果结点s 是一个指针赋值表达式p=expr,其中p 为指针名,expr 为表达式,且expr 中包含一个支配指针q,则将(p,q) 作为一条边加入指针依赖图;
操作3、如果结点s 是一个数组下标表达式p[expr],其中p 为指针名或数组名,expr 为表达式,则根据p 的类型分别进行如下操作:
操作31、如果p 是符号表中已定义的一个数组,则从符号表中获取该数组的类型type和长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(p+expr,p,p+len,filename,loc)))
其中,函数参数p+expr 表示数组下标表达式访问的内存地址,函数参数p 表示数组的起始地址,函数参数p+len 表示数组的结束地址,函数参数filename 表示源文件名,函数参数loc 表示代码行号;
操作32、如果p 是所在函数声明的第n 个形式参数,则从符号表中获取该指针的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(p+expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数p+expr 表示数组下标表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n 表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n 表示第n 个形式参数的结束地址,函数参数filename 表示源文件名,函数参数loc 表示代码行号;
操作33、如果p 是不满足以上操作31、操作32中的两种情况的指针,则从指针依赖图中获取其最终依赖指针q,如果q不存在,则认为源代码中存在指针使用前未赋初值的错误,并报错,如果q存在,则根据q的类型分别进行如下操作:
操作331、如果q 是符号表中已定义的一个数组,则从符号表中获取指针p 的类型type和数组q的长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(p+expr,q,q+len,filename,loc)))
其中,函数参数p+expr 表示数组下标表达式访问的内存地址,函数参数q表示数组的起始地址,函数参数q+len表示数组的结束地址,函数参数filename 表示源文件名,函数参数loc 表示代码行号;
操作332、如果q 是所在函数声明的第n 个形式参数,则从符号表中获取指针p的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(p+expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数p+expr 表示数组下标表达式访问的内存地址,函数参数__MNT_CHK_AAV_B_n 表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n 表示第n个形式参数的结束地址,函数参数filename 表示源文件名,函数参数loc表示代码行号;
操作333、如果q 是不满足以上操作331、操作332中的两种情况的指针,则认为源代码中存在指针使用前未赋初值的错误,并报错;
操作4、如果结点s 是一个指针访问表达式*expr,其中expr 为表达式,且expr 中包含一个支配指针p,则根据p 的类型分别进行如下操作:
操作41、如果p 是符号表中已定义的一个数组,则从符号表中获取该数组的类型type和长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(expr,p,p+len,filename,loc)))
其中,函数参数expr 表示指针访问表达式访问的内存地址,函数参数p 表示数组的起始地址,函数参数p+len 表示数组的结束地址,函数参数filename 表示源文件名,函数参数loc表示代码行号;
操作42、如果p 是所在函数声明的第n 个形式参数,则从符号表中获取该指针的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数expr 表示指针访问表达式访问的内存地址,函数参数_MNT_CHK_AAV_B_n 表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n 表示第n个形式参数的结束地址,函数参数filename 表示源文件名,函数参数loc表示代码行号;
操作43、如果p是不满足以上操作41、操作42 中的两种情况的指针,则从指针依赖图中获取其最终依赖指针q,如果q不存在,则认为源代码中存在指针使用前未赋初值的错误,并报错,如果q存在,则根据q的类型分别进行如下操作:
操作431、如果q是符号表中已定义的一个数组,则从符号表中获取指针p的类型type和数组q的长度len,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHECK_AAV(expr,q,q+len,filename,loc)))
其中,函数参数expr 表示指针访问表达式访问的内存地址,函数参数q表示数组的起始地址,函数参数q+len表示数组的结束地址,函数参数filename表示源文件名,函数参数loc 表示代码行号;
操作432、如果q是所在函数声明的第n个形式参数,则从符号表中获取指针p的类型type,然后将该表达式替换为如下函数调用:
*((type*)(__MNT_CHK_AAV(expr,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,filename,loc)))
其中,函数参数expr 表示指针访问表达式访问的内存地址,函数参数_MNT_CHK_AAV_B_n 表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n 表示第n个形式参数的结束地址,函数参数filename 表示源文件名,函数参数loc表示代码行号;
操作433、如果q是不满足以上操作431、操作432中的两种情况的指针,则认为源代码中存在指针使用前未赋初值的错误,并报错;
操作5、如果结点s是一个函数声明或函数定义表达式type func(…,type_nexpr_n,…),其中:第n个参数表达式expr_n为type_n类型的数组或指针声明p,省略号…表示其它参数表达式,则将该函数声明或函数定义替换为如下函数:
Type func(…,type_nexpr_n,void*__MNT_CHK_AAV_B_n,void*__MNT_CHK_AAV_E_n,…)
其中,省略号…表示原来的所有参数表达式,函数参数__MNT_CHK_AAV_B_n 表示第n个参数表达式的起始地址,函数参数__MNT_CHK_AAV_E_n 表示第n个参数表达式的结束地址;
操作6、如果结点s是一个函数调用表达式func(…,expr_n,…),其中:第n个参数表达式expr_n中包含一个支配指针p,省略号…表示其它参数表达式,则根据p的类型分别进行如下操作:
操作61、如果p是符号表中已定义的一个数组,则从符号表中获取该数组的长度len,然后将该表达式替换为如下函数调用:func(…,expr_n,p,p+len,…),其中,省略号…表示原来的所有参数表达式,函数参数p表示数组的起始地址,函数参数p+len 表示数组的结束地址;
操作62、如果p是所在函数声明的第n个形式参数,则将该表达式替换为如下函数调用:
func(…,expr_n,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,…)
其中,省略号…表示原来的所有参数表达式,函数参数__MNT_CHK_AAV_B_n 表示第n个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n 表示第n 个形式参数的结束地址;
操作63、如果p是不满足以上操作61、操作62 中的两种情况的指针,则从指针依赖图中获取其最终依赖指针q,如果q不存在,则认为源代码中存在指针使用前未赋初值的错误,并报错,如果q存在,则根据q的类型分别进行如下操作:
操作631、如果q是符号表中已定义的一个数组,则从符号表中获取数组q的长度len,然后将该表达式替换为如下函数调用:func(…,expr_n,q,q+len,…),其中,省略号…表示原来的所有参数表达式,函数参数q表示数组的起始地址,函数参数q+len 表示数组的结束地址;
操作632、如果q 是所在函数声明的第n个形式参数,则将该表达式替换为如下函数调用:
func(…,expr_n,__MNT_CHK_AAV_B_n,__MNT_CHK_AAV_E_n,…)
其中,省略号…表示原来的所有参数表达式,函数参数__MNT_CHK_AAV_B_n表示第n 个形式参数的起始地址,函数参数__MNT_CHK_AAV_E_n 表示第n个形式参数的结束地址;
操作633、如果q是不满足以上操作631、操作632中的两种情况的指针,则认为源代码中存在指针使用前未赋初值的错误,并报错。
3. 如权利要求1所述的数组越界错误的自动检测和校正方法,其特征在于,所述步骤3进一步包括:
在指针依赖图中,从p开始沿有向边往前遍历,直到访问到一个没有后继结点的指针q,那么q就是p的最终依赖指针。
4. 如权利要求1所述的数组越界错误的自动检测和校正方法,其特征在于,所述步骤3进一步包括:
支配指针是指决定该表达式指向地址的主要指针变量;
该表达式的其他部分决定相对于该指针指向地址的偏移量。
5. 如权利要求1所述的数组越界错误的自动检测和校正方法,其特征在于,所述检测策略包括:
判断要访问的地址p是否在允许的范围[begin,end)之间,如果超出该范围,则报告数组越界错误。
6. 如权利要求1所述的数组越界错误的自动检测和校正方法,其特征在于:所述校正策略包括:
当合法的内存范围为[begin,end),大小为n 时,
如果访问地址p为下越界,则将p和begin之间的偏移量进行模n运算,并被end减后作为映射的合法访问地址;
如果访问地址p为上越界,则将p和end之间的偏移量进行模n运算,并加上begin作为映射的合法访问地址;
如果访问地址p在[begin,end)范围内,则不作任何计算。
7. 如权利要求1所述的数组越界错误的自动检测和校正方法,其特征在于:所述步骤5进一步包括:
检测策略和校正策略直接通过源代码的方式说明;或者,
使用特别定义的描述语言来说明,并通过语言自动转化工具自动翻译为源代码。
Priority Applications (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201410022323.0A CN103778061B (zh) | 2014-01-17 | 2014-01-17 | 数组越界错误的自动检测和校正方法 |
Applications Claiming Priority (1)
Application Number | Priority Date | Filing Date | Title |
---|---|---|---|
CN201410022323.0A CN103778061B (zh) | 2014-01-17 | 2014-01-17 | 数组越界错误的自动检测和校正方法 |
Publications (2)
Publication Number | Publication Date |
---|---|
CN103778061A CN103778061A (zh) | 2014-05-07 |
CN103778061B true CN103778061B (zh) | 2016-08-24 |
Family
ID=50570322
Family Applications (1)
Application Number | Title | Priority Date | Filing Date |
---|---|---|---|
CN201410022323.0A Expired - Fee Related CN103778061B (zh) | 2014-01-17 | 2014-01-17 | 数组越界错误的自动检测和校正方法 |
Country Status (1)
Country | Link |
---|---|
CN (1) | CN103778061B (zh) |
Families Citing this family (20)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN104298594B (zh) * | 2014-09-25 | 2018-03-02 | 南京航空航天大学 | 一种源代码中值计算错误的自动检测和定位方法 |
CN106155893B (zh) * | 2015-04-03 | 2021-03-02 | 腾讯科技(深圳)有限公司 | 判断应用程序测试覆盖范围的方法及程序测试设备 |
CN105912459A (zh) * | 2016-04-01 | 2016-08-31 | 北京理工大学 | 一种基于符号执行的数组越界检测方法 |
CN106326123B (zh) * | 2016-08-24 | 2018-12-04 | 北京奇虎测腾安全技术有限公司 | 一种用于检测数组越界缺陷的方法及系统 |
CN106371997B (zh) * | 2016-09-07 | 2020-01-10 | 网易(杭州)网络有限公司 | 一种代码检查方法及装置 |
CN106940654B (zh) * | 2017-02-15 | 2020-08-14 | 南京航空航天大学 | 源代码中内存错误的自动检测和定位方法 |
CN108694049B (zh) * | 2017-02-23 | 2021-08-17 | 阿里巴巴集团控股有限公司 | 一种更新软件的方法和设备 |
CN107247668A (zh) * | 2017-06-07 | 2017-10-13 | 成都四象联创科技有限公司 | 代码自动检测和校正方法 |
CN108287767A (zh) * | 2017-12-29 | 2018-07-17 | 五八有限公司 | 一种数据容错方法、设备及计算机可读存储介质 |
CN108446122B (zh) * | 2018-03-22 | 2021-10-29 | 中国银行股份有限公司 | 一种参数一致性验证方法及装置 |
CN108920133B (zh) * | 2018-06-14 | 2021-06-18 | 北京三快在线科技有限公司 | 跨语言编程方法、装置、电子设备及存储介质 |
CN111026631B (zh) * | 2018-10-09 | 2024-03-26 | 顺丰科技有限公司 | 接口自动化检测方法、装置和服务器 |
CN110362501B (zh) * | 2019-07-05 | 2021-09-24 | 北京大学 | 一种执行饱和寻址加载和存储操作的设备和方法 |
CN110488864B (zh) * | 2019-08-15 | 2021-12-03 | 中国商用飞机有限责任公司 | 用于修正飞机的飞行控制系统中的loc信号的方法及系统 |
CN110674495B (zh) * | 2019-09-03 | 2021-07-09 | Xc5 香港有限公司 | 一种数组越界访问的检测方法、装置及设备 |
CN111104347B (zh) * | 2019-12-23 | 2022-03-25 | 北京东土军悦科技有限公司 | 堆内存块查找方法、装置、设备及存储介质 |
CN111443916B (zh) * | 2020-03-10 | 2021-06-22 | 南京航空航天大学 | 一种面向程序内存安全性验证工具的静态优化方法 |
CN112147983B (zh) * | 2020-09-27 | 2022-01-04 | 深圳市元征科技股份有限公司 | 一种车辆诊断方法、装置、电子设备及存储介质 |
CN112733153A (zh) * | 2021-01-27 | 2021-04-30 | 腾讯科技(深圳)有限公司 | 源代码扫描方法、装置、电子设备和存储介质 |
CN114896173B (zh) * | 2022-07-12 | 2022-09-16 | 北京云枢创新软件技术有限公司 | 用于目标数据路径显示的数据处理系统 |
Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN102880547A (zh) * | 2012-09-05 | 2013-01-16 | 无锡江南计算技术研究所 | 一种软件单元与部件与相结合的源代码变更影响分析与测试方法 |
CN102915271A (zh) * | 2012-10-31 | 2013-02-06 | 北京邮电大学 | 基于控制流图的单元回归测试用例集合优化方法 |
-
2014
- 2014-01-17 CN CN201410022323.0A patent/CN103778061B/zh not_active Expired - Fee Related
Patent Citations (2)
Publication number | Priority date | Publication date | Assignee | Title |
---|---|---|---|---|
CN102880547A (zh) * | 2012-09-05 | 2013-01-16 | 无锡江南计算技术研究所 | 一种软件单元与部件与相结合的源代码变更影响分析与测试方法 |
CN102915271A (zh) * | 2012-10-31 | 2013-02-06 | 北京邮电大学 | 基于控制流图的单元回归测试用例集合优化方法 |
Non-Patent Citations (1)
Title |
---|
数组越界的故障模型及其检测方法研究;叶焰锋;《故障诊断》;20071231;第145-147页 * |
Also Published As
Publication number | Publication date |
---|---|
CN103778061A (zh) | 2014-05-07 |
Similar Documents
Publication | Publication Date | Title |
---|---|---|
CN103778061B (zh) | 数组越界错误的自动检测和校正方法 | |
Hazimeh et al. | Magma: A ground-truth fuzzing benchmark | |
Galeotti et al. | TACO: Efficient SAT-based bounded verification using symmetry breaking and tight bounds | |
EP2485149A1 (en) | Symbolic execution and test generation for programs to be run on a graphic processor | |
US20110271253A1 (en) | Enhancing functional tests coverage using traceability and static analysis | |
US20110271252A1 (en) | Determining functional design/requirements coverage of a computer code | |
Yu et al. | Automated assertion generation via information retrieval and its integration with deep learning | |
CN106294148A (zh) | 基于扩展符号变迁系统的c语言程序软件验证方法及装置 | |
CN101908006A (zh) | 一种基于gcc抽象语法树的缓冲区溢出漏洞检测方法 | |
Shishkin | Debugging smart contract’s business logic using symbolic model checking | |
JP2023545140A (ja) | ブロックチェーンネットワークにおいてスマートコントラクトをサポートするための方法およびシステム | |
CN105808438A (zh) | 一种基于函数调用路径的测试用例复用方法 | |
Filieri et al. | Quantification of software changes through probabilistic symbolic execution (N) | |
CN103365774A (zh) | 基于函数调用关系的瞬时错误检测方法 | |
Pailoor et al. | Synthesizing data structure refinements from integrity constraints | |
Zhong et al. | Inferring bug signatures to detect real bugs | |
Hua et al. | Rupair: towards automatic buffer overflow detection and rectification for Rust | |
Marques et al. | Concolic execution for webassembly | |
Binkley | Computing amorphous program slices using dependence graphs | |
Zheng et al. | Dappscan: building large-scale datasets for smart contract weaknesses in dapp projects | |
Liu et al. | PropertyGPT: LLM-driven Formal Verification of Smart Contracts through Retrieval-Augmented Property Generation | |
CN114282227B (zh) | 一种Fabric区块链系统智能合约的安全分析检测方法 | |
Xin et al. | A program vulnerabilities detection frame by static code analysis and model checking | |
Promsky | C program verification: verification condition explanation and standard library | |
Wei et al. | VulRep: vulnerability repair based on inducing commits and fixing commits |
Legal Events
Date | Code | Title | Description |
---|---|---|---|
C06 | Publication | ||
PB01 | Publication | ||
C10 | Entry into substantive examination | ||
SE01 | Entry into force of request for substantive examination | ||
C14 | Grant of patent or utility model | ||
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: 20160824 |