源代码中内存错误的自动检测和定位方法与流程

文档序号:12818746阅读:379来源:国知局

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



背景技术:

内存错误是一种普遍存在于c/c++程序中的设计缺陷,可能导致软件运行异常和安全漏洞。内存错误包括空间内存错误、时间内存错误、类型内存错误。空间内存错误也称为缓冲区溢出错误,包括:数组越界、指针访问越界、指针使用前未初始化、空指针解引用、对任意整型数据进行强制类型转化得到的指针进行解引用等;时间内存错误包括:悬挂栈指针,悬挂堆指针、多次释放、不完全释放等;类型内存错误包括:释放非堆上的内存空间、将函数指针作为数据指针进行解引用、将数据指针作为函数指针进行函数调用等。这些错误可能导致软件运行异常或崩溃,也可能被黑客利用来执行恶意代码,从而导致安全漏洞。对于安全关键软件和系统,这将会导致严重后果和巨大损失。因此,实现内存错误的高效自动检测和源代码定位,能够帮助开发人员在软件开发过程中发现并改正这些错误,极大地提高软件的质量和软件维护的效率。

目前常见的检测技术主要是扩展指针技术,该技术是通过扩展指针的数据类型,使指针在原来只存储一个内存地址的基础上,再额外记录该指针所指向内存块的基地址和大小等信息,以便在指针解引用时可以判断该指针是否在其所指向内存块的有效区间内,从而判断指针解引用是否存在错误。由于该技术修改了代码中指针的存储结构,造成了处理后的代码与原代码之间的不兼容,导致检测结果不稳定。此外,由于该技术只对指针所指向内存块的边界信息进行了记录,所以只能处理空间内存错误,无法检测时间和类型内存错误。

目前常见的检测技术主要针对二进制代码,即通过对程序的二进制代码进行修改,然后运行修改后的二进制代码以检测内存错误。由于该技术是对二进制代码进行修改,所以不具备平台普适性,即修改工具只能处理主流平台的二进制代码,而无法兼容某些领域所使用的特定的运行平台。此外,由于该技术无法准确定位到错误对应的源代码位置,导致不容易对错误进行调试。

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



技术实现要素:

为了克服上述已有技术和方法存在的不足,本发明的目的旨在提供一种源代码中内存错误的自动检测和定位方法,该方法通过使用源代码变换技术,将源代码变换为带有自动检测和错误定位功能的源代码,包括对指针定义初始化和赋值节点插入代码来记录或更新指针元数据,对指针解引用和数组下标访问节点插入内存错误检测和源代码定位的机制,对函数定义节点插入代码来更新形参的指针元数据,并插入包装函数定义来在函数之间传递指针元数据,对函数调用节点插入额外的实参来传入原实参的指针元数据;使得可以在软件运行过程中自动检测和定位软件中的内存访问错误,以实现更全面的错误检测,更好的兼容性,更好的平台普适性,更准确的源代码定位,更高的运行时性能和效率,从而克服现有的检测技术中存在的问题。

技术方案:

源代码中内存错误的自动检测和定位方法,包括:步骤1、使用编译器对源代码进行词法分析和语法分析,构造包含节点类型信息的符号表以及抽象语法树;步骤2、遍历抽象语法树,对指针变量定义初始化节点和赋值节点插入代码来记录或更新指针元数据,其中指针元数据包括指针指向内存块的边界、状态和类型信息;步骤3、遍历抽象语法树,对指针解引用节点和数组下标访问节点插入内存错误检测和源代码定位的机制;步骤4、遍历抽象语法树,对函数定义节点插入代码来更新形参的指针元数据,并插入包装函数定义来在函数之间传递指针元数据;步骤5、遍历抽象语法树,对函数调用节点插入额外的实参来传入原实参的指针元数据;步骤6、编译执行变换后的源代码,程序会自动判断内存错误的发生,并准确报告错误对应的源代码位置。

具体步骤包括:

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

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

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

步骤s4,遍历工作目录中的所有源文件,使用编译器对源代码进行词法分析和语法分析,构造符号表和抽象语法树;所述符号表中包含节点的类型信息;

步骤s5,遍历抽象语法树,若当前节点是变量定义初始化表达式,则判断该变量是否为指针类型或者是包含指针成员的结构体类型,若是则插入代码来记录指针元数据,若不是则转至步骤s6;

步骤s6,遍历抽象语法树,若当前节点是变量赋值表达式,则判断该表达式是否为指针赋值或者是包含指针成员的结构体赋值,若是则插入代码来更新指针元数据,若不是则转至步骤s7;

步骤s7,遍历抽象语法树,若当前节点是指针解引用表达式或者数组下标访问表达式,则插入内存错误检测和源代码定位的机制,否则转至步骤s8;

步骤s8,遍历抽象语法树,若当前节点是函数定义,假设函数名为func8,返回值类型为rettype,则判断该函数的返回值和形参是否为指针类型或者是包含指针成员的结构体类型,若是则插入代码来更新形参的指针元数据,并插入包装函数定义来在函数之间传递指针元数据,若不是则转至步骤s9;

步骤s9,遍历抽象语法树,若当前节点是函数调用表达式func9(a1,an),其中func9是函数名,a1,an表示n个实参,则判断该函数的返回值和形参是否为指针类型或者是包含指针成员的结构体类型,若是则插入额外的实参来传入原实参的指针元数据,若不是则转至步骤s10;

步骤s10,对于所有被改写的源文件,在文件开始位置插入include语句,用于包含所有数据结构、指针元数据操作函数和检测函数的定义文件;

步骤s11,将步骤s5到步骤s10中的所有替换改写保存到相应的源文件中,并将本次改写的文件加入已处理文件列表;

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

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

其中步骤s5~s9的操作可以合并为遍历抽象语法树过程中的一个步骤或者同时执行。

所述步骤s5中插入代码来记录指针元数据,进一步包括:

步骤s51,若当前节点是指针变量定义type51p51=expr51(kpe51),其中type51是指针变量类型,p51是指针变量名,kpe51是初始化表达式中的核心指针,expr51(kpe51)是由kpe51构成的初始化表达式;

步骤s5101,若该核心指针kpe51是指针变量pv51,则将该变量定义替换为:

type51p51=expr51((type51)prfpmd_tbl_update_ptr_ret(&p51,&pv51,pv51))

其中函数prfpmd_tbl_update_ptr_ret用于将p51的指针元数据更新为pv51的指针元数据;

步骤s5102,若该核心指针kpe51是指针常量pc51,则将该变量定义替换为:

type51p51=expr51((type51)prfpmd_tbl_update_as_ret(&p51,

pc51_status,pc51_base,pc51_bound,pc51))

其中函数prfpmd_tbl_update_as_ret用于将p51的指针元数据更新为其余实参所表示的信息,pc51_status、pc51_base、pc51_bound分别是pc51的状态、下界和上界信息;

步骤s5103,若该核心指针kpe51是函数调用func51(a1,an),其中函数func51返回一个指针,a1,an表示n个实参,则将该变量定义替换为:

type51p51=expr51(prffunc51(&p51,a1,an))

其中函数prffunc51是func51的包装函数,用于将p51的指针元数据更新为func51返回值的指针元数据;

步骤s5104,若该核心指针kpe51是条件表达式cond?true_ptr:false_ptr,则将该变量定义替换为:

type51p51=expr51((type51)prfcond_expr(&p51,

prfpmd_tbl_lookup(&true_ptr),prfpmd_tbl_lookup(&false_ptr),

cond,cond?true_ptr:false_ptr))

其中函数prfcond_expr用于将p51的指针元数据更新为条件表达式返回值的指针元数据;

步骤s52,若当前节点是结构体变量定义structst52obj52=expr52,其中st52是结构体变量类型,且包含n个指针成员(用pf1,pfn表示),obj52是结构体变量名,expr52是初始化表达式;

步骤s5201,若该初始化表达式expr52是结构体变量表达式expr52(kpe52),其中kpe52是初始化表达式中的核心结构体,expr52(kpe52)是由kpe52构成的初始化表达式,则将该变量定义替换为:

structst52obj52=(prfpmd_tbl_update_ptr(&obj52.pf1,&kpe52.pf1),

prfpmd_tbl_update_ptr(&obj52.pfn,&kpe52.pfn),

expr52(kpe52));

其中函数prfpmd_tbl_update_ptr和逗号表达式用于将obj52中所有指针成员的指针元数据更新为kpe52中相应指针成员的指针元数据;

步骤s5202,若该初始化表达式expr52是基于函数调用的结构体变量表达式expr52(func52(a1,an)),其中函数func52返回一个结构体,a1,an表示n个实参,则将该变量定义替换为:

structst52obj52=expr52(prffunc52(&obj52,a1,an))

其中函数prffunc52是func52的包装函数,用于将obj52中所有指针成员的指针元数据更新为func52返回值中相应指针成员的指针元数据;

步骤s5203,若该初始化表达式expr52是初始值表达式列表{expr521(kpe521),expr52n(kpe52n)},其中expr52i(kpe52i)是由核心指针kpe52i构成的初始值表达式,1≤i≤n,则将该变量定义替换为:

structst52obj52={

prfpmd_tbl_update_ptr_ret(&obj52.pf1,&kpe521,expr521(kpe521)),

prfpmd_tbl_update_ptr_ret(&obj52.pfn,&kpe52n,expr52n(kpe52n))}

其中函数prfpmd_tbl_update_ptr_ret用于将obj52.pfi的指针元数据更新为kpe52i的指针元数据。

所述步骤s6中插入代码来更新指针元数据,进一步包括:

步骤s61,若当前节点是指针赋值expr61a(p61)=expr61b(kpe61),其中p61是赋值表达式左部中的核心指针变量,其类型是指针类型type61,expr61a(p61)是由p61构成的赋值表达式左部,kpe61是赋值表达式右部中的核心指针,expr61b(kpe61)是由kpe61构成的赋值表达式右部;

步骤s6101,若该赋值表达式右部中的核心指针kpe61是指针变量pv61,则将该赋值表达式替换为:

expr61a(p61)=expr61b((type61)prfpmd_tbl_update_ptr_ret(&p61,&pv61,pv61))其中函数prfpmd_tbl_update_ptr_ret用于将p61的指针元数据更新为pv61的指针元数据;

步骤s6102,若该赋值表达式右部中的核心指针kpe61是指针常量pc61,则将该赋值表达式替换为:

expr61a(p61)=expr61b((type61)prfpmd_tbl_update_as_ret(&p61,

pc61_status,pc61_base,p61_bound,pc61))

其中函数prfpmd_tbl_update_as_ret用于将p61的指针元数据更新为其余实参所表示的信息,pc61_status、pc61_base、pc61_bound分别是pc61的状态、下界和上界信息;

步骤s6103,若该赋值表达式右部中的核心指针kpe61是函数调用func61(a1,an),其中函数func61返回一个指针,a1,an表示n个实参,则将该赋值表达式替换为:

expr61a(p61)=expr61b(prffunc61(&p61,a1,an))

其中函数prffunc61是func61的包装函数,用于将p61的指针元数据更新为func61返回值的指针元数据;

步骤s6104,若该赋值表达式右部中的核心指针kpe61是条件表达式cond?true_ptr:false_ptr,则将该赋值表达式替换为:

expr61a(p61)=expr61b((type61)prfcond_expr(&p61,

prfpmd_tbl_lookup(&true_ptr),prfpmd_tbl_lookup(&false_ptr),

cond,cond?true_ptr:false_ptr))

其中函数prfcond_expr用于将p61的指针元数据更新为条件表达式返回值的指针元数据;

步骤s62,若当前节点是结构体赋值expr62a(obj62)=expr62b,其中obj62是赋值表达式左部中的核心结构体变量,其类型是结构体类型st62,且包含n个指针成员,用pf1,pfn表示,expr62a(obj62)是由obj62构成的赋值表达式左部,expr62b是赋值表达式右部;

步骤s6201,若该赋值表达式右部expr62b是结构体变量表达式expr62b(kpe62),其中kpe62是赋值表达式右部中的核心结构体,expr62b(kpe62)是由kpe62构成的赋值表达式右部,则将该赋值表达式替换为:

expr62a(obj62)=(prfpmd_tbl_update_ptr(&obj62.pf1,&kpe62.pf1),

prfpmd_tbl_update_ptr(&obj62.pfn,&kpe62.pfn),

expr62b(kpe62));

其中函数prfpmd_tbl_update_ptr和逗号表达式用于将obj62中所有指针成员的指针元数据更新为kpe62中相应指针成员的指针元数据;

步骤s6202,若该赋值表达式右部expr62b是基于函数调用的结构体变量表达式expr62b(func62(a1,an)),其中函数func62返回一个结构体,a1,an表示n个实参,则将该赋值表达式替换为:

expr62a(obj62)=expr62b(prffunc62(&obj62,a1,an))

其中函数prffunc62是func62的包装函数,用于将obj62中所有指针成员的指针元数据更新为func62返回值中相应指针成员的指针元数据。

所述步骤s7中插入内存错误检测和源代码定位的机制,进一步包括:

步骤s71,若当前节点是指针解引用表达式*expr71(kpe71),其中kpe71是指针解引用表达式中的核心指针,其类型是指针类型type71,*expr71(kpe71)是由kpe71构成的指针解引用表达式;

步骤s7101,若该核心指针kpe71是指针变量pv71,则将该解引用表达式替换为:

*((type71)(prfcheck_dpv(&pv71,expr71(pv71),sizeof(*type71),

filename,funcname,line,column)))

其中函数prfcheck_dpv用于检测该解引用表达式访问的内存块是否在pv71的指针元数据所记录的范围内,*type71表示type71指向的数据类型,filename,funcname,line,column分别表示当前节点所在的文件名,函数名,行号和列号;

步骤s7102,若该核心指针kpe71是指针常量pc71,则将该解引用表达式替换为:

*((type71)(prfcheck_dpc(pc71_base,pc71_bound,expr71(pc71),sizeof(*type71),

filename,funcname,line,column)))

其中函数prfcheck_dpc用于检测该解引用表达式访问的内存块是否在pc71的下界pc71_base和上界pc71_bound的范围内,*type71表示type71指向的数据类型,filename,funcname,line,column分别表示当前节点所在的文件名,函数名,行号和列号;

步骤s7103,若该核心指针kpe71是函数指针pf71,则将该解引用表达式替换为:

*((type71)(prfcheck_dpf(&pf71,expr71(pf71),

filename,funcname,line,column)))

其中函数prfcheck_dpf用于检测该解引用表达式访问的函数是否在pf71的指针元数据所记录的范围内,filename,funcname,line,column分别表示当前节点所在的文件名,函数名,行号和列号;

步骤s72,若当前节点是数组下标访问表达式expr72(kpe72)[index],其中kpe72是数组下标访问表达式中的核心指针,其类型是指针类型type72,expr72(kpe72)是由kpe72构成的数组基地址表达式,index是数组下标;

步骤s7201,若该核心指针kpe72是指针变量pv72,则将该数组下标访问表达式替换为:

expr72(pv72)[prfcheck_dpv_index(prfpmd_tbl_lookup(&pv72),expr72(pv72),index,

sizeof(*type72),filename,funcname,line,column)]

其中函数prfcheck_dpv_index用于检测该表达式访问的内存块是否在pv72的指针元数据所记录的范围内,*type72表示type72指向的数据类型;

步骤s7202,若该核心指针kpe72是指针常量pc72,则将该数组下标访问表达式替换为:

expr72(pc72)[prfcheck_dpc_index(pc72_base,pc72_bound,expr72(pc72),index,

sizeof(*type72),filename,funcname,line,column)]

其中函数prfcheck_dpc_index用于检测该表达式访问的内存块是否在pc72的下界pc72_base和上界pc72_bound的范围内,*type72表示type72指向的数据类型。

所述步骤s8中插入代码来更新形参的指针元数据,并插入包装函数定义来在函数之间传递指针元数据,进一步包括:

步骤s81,在函数开头插入以下语句:

unsignedcharret_flag=0;

rettyperet_val;

prfauto_stat*stack_as=prfauto_stat_create(prfstack,1);

其中ret_flag用于记录程序运行中该函数是否执行到return语句,ret_val用于记录该函数的返回值,函数prfauto_stat_create用于创建一个类型为栈的状态信息,且引用者数量为1,stack_as是所有局部动态变量共用的状态信息;

在函数结尾前插入以下语句:

其中label_fid是由该函数体唯一标识符fid构成的标签,函数prfpmd_tbl_remove用于删除所有局部指针变量pf1,pfn的指针元数据,函数prfauto_stat_dc用于将stack_as状态的引用者数量减1;

步骤s82,遍历函数中所有的循环体节点,在每个循环体结尾前插入以下语句:

其中label_lid是由该循环体唯一标识符lid构成的标签,函数prfpmd_tbl_remove用于删除循环体中所有局部指针变量pl1,pln的指针元数据,label_pid是由该循环体的上一层复合语句的唯一标识符pid构成的标签,bc_flag_lid是由该循环体唯一标识符lid为部分名字定义的变量,用于记录程序运行中该函数是否执行到break语句和continue语句;

步骤s83,遍历函数中所有的非循环体复合语句节点,在每个复合语句结尾前插入以下语句:

其中label_bid是由该复合语句唯一标识符bid构成的标签,函数prfpmd_tbl_remove用于删除该复合语句中所有局部指针变量pb1,pbn的指针元数据,label_pid是由该复合语句的上一层复合语句的唯一标识符pid构成的标签,bc_flag_lid是由该复合语句的最近上层循环体的唯一标识符lid构成的变量名;

步骤s84,遍历函数中所有的break语句,将break语句替换为:

bc_flag_lid=1;gotolabel_bid;

其中bc_flag_lid是由该复合语句的最近上层循环体的唯一标识符lid构成的变量名,label_bid是由该语句所在的复合语句的唯一标识符bid构成的标签;

步骤s85,遍历函数中所有的continue语句,将continue语句替换为:

bc_flag_lid=2;gotolabel_bid;

步骤s86,遍历函数中所有的return语句,将returnexpr(kpe);语句替换为:

ret_val=expr(kpe);

ret_flag=1;gotolabel_bid;

步骤s87,如果函数有n个形参p1,pn的类型为指针、数组或包含指针成员的结构体,则在函数开头插入以下语句:

prfpmd_tbl_update_fpmd(&p1,prffmd_tbl_lookup_fpmd(func8,1));

prfpmd_tbl_update_fpmd(&pn,prffmd_tbl_lookup_fpmd(func8,n));

其中函数prffmd_tbl_lookup_fpmd用于从指针元数据表中取出函数func8的第i个实参的指针元数据,1≤i≤n;函数prfpmd_tbl_update_fpmd用于将形参pi的指针元数据更新为取出的指针元数据;

步骤s88,如果函数的返回值类型为指针、数组或包含指针成员的结构体,则在函数的返回语句returnexpr(kpe);之前插入:

prffmd_tbl_update_pmd(func8,0,prfpmd_tbl_lookup(&kpe));

其中函数prffmd_tbl_update_pmd用于将函数func8的第0个指针元数据更新为kpe的指针元数据,即返回值的指针元数据;

步骤s89,在原函数之前插入一个包装函数定义rettypeprffunc8(rettype*ret_addr,prfpmd*p1_pmd,prfpmd*pn_pmd,t1p1,tnpn),用于在函数之间传递参数和返回值的指针元数据,其中prfpmd是指针元数据结构,包括指针指向内存块的边界、状态和类型信息。

所述步骤s9中插入额外的实参来传入原实参的指针元数据,进一步包括:

步骤s91,若该函数的返回值或形参是指针类型,则将该函数调用表达式替换为以下对包装函数的调用:

prffunc9(ret_addr,prfpmd_tbl_lookup(&a1),prfpmd_tbl_lookup(&an),a1,an)其中ret_addr是函数返回值被赋予的变量的地址;

步骤s92,若该函数的返回值或形参是包含指针成员的结构体类型,则将该函数调用表达式替换为以下对包装函数的调用:

prffunc9(ret_addr,prfpmd_tbl_lookup(&a1.a1pf1),prfpmd_tbl_lookup(&a1.a1pfj),

prfpmd_tbl_lookup(&an.anpf1),prfpmd_tbl_lookup(&an.anpfk),a1,an)

其中a1pf1,a1pfj表示a1中的j个指针成员,anpf1,anpfk表示an中的k个指针成员。

有益效果:与传统检测技术相比,本发明提供的内存错误的自动检测和定位方法,通过使用指针元数据记录指针指向内存块的边界、状态和类型信息,使得可以检测所有种类的内存错误,从而实现更全面的错误检测。进一步地,本发明通过将指针元数据和指针分开存储,并不修改代码中指针的存储结构,使得处理后的代码与原代码之间的数据结构兼容,从而实现更好的兼容性。进一步地,本发明通过使用源代码变换技术,使得变换后的源代码可以使用原有编译器进行编译和部署,从而实现更好的平台普适性。进一步地,本发明通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断潜在的内存错误所在的源文件和代码行,并相应地进行源代码变换,使得在错误检测中可以使用这些位置信息,从而实现更准确的源代码定位。进一步地,本发明通过对源代码的抽象语法树进行分析,具有充分的语义信息来消除不必要的代码变换,并通过使用各种优化的哈希表对指针元数据进行存储和索引,使得对指针元数据的查找和存取操作更加高效,从而实现更高的运行时性能和效率。

因此,本发明可以解决计算机软件和系统,尤其是安全关键软件和系统开发过程中的内存错误的自动检测和定位的难题,能够实现更全面的错误检测,更好的兼容性,更好的平台普适性,更准确的源代码定位,更高的运行时性能和效率,从而克服现有的检测技术中存在的问题。提高软件的质量和软件维护的效率,具有良好的社会效益。

附图说明

图1为本发明的示意图。

具体实施方式

下面结合附图对本发明作更进一步的说明。

本实施例采用本发明方法对一段c语言源代码进行内存错误的检测和定位,进一步具体说明本发明的有关方法、流程及相关步骤。例如,源代码如下(文件名为test.c):

本发明的具体步骤如下:

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

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

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

本例中,将源代码文件test.c复制到工作目录c:/work/中(windows系统)或者/tmp/work/中(linux系统)。

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

本例中,将第18行“inta[n]={1,2};”替换为“inta[2]={1,2};”,并保存扩展结果到test.c文件中。

步骤s4,遍历工作目录中的所有源文件,使用编译器对源文件的源代码进行词法分析和语法分析,构造符号表和抽象语法树;所述符号表中包含节点的类型信息;

本例中,只有一个源文件test.c,使用编译器构造test.c对应的符号表和抽象语法树。

步骤s5,遍历抽象语法树,若当前节点是变量定义初始化表达式,则判断该变量是否为指针类型或者是包含指针成员的结构体类型,若是则插入代码来记录指针元数据,若不是则转至步骤s6;

本例中,第8行“int*r=&j;”是指针变量r的定义初始化表达式,第10行“int*t=&j;”是指针变量t的定义初始化表达式,第20行“int*p=(int*)malloc(5*sizeof(int));”是指针变量p的定义初始化表达式,第21~25行是结构体变量s1的定义初始化表达式,第26行“structsts2=s1;”是结构体变量s2的定义初始化表达式,第27行“int*q=p+1;”是指针变量q的定义初始化表达式,第28行“int*(*fp)(int*)=foo;”是函数指针变量fp的定义初始化表达式,第29行“int*r=i?p+2:q;”是指针变量r的定义初始化表达式。

步骤s51,若当前节点是指针变量定义type51p51=expr51(kpe51),其中type51是指针变量类型,p51是指针变量名,kpe51是初始化表达式中的核心指针,expr51(kpe51)是由kpe51构成的初始化表达式;

本例中,第8行“int*r=&j;”中指针变量r的类型为int*,&j是初始化表达式中的核心指针,第10行“int*t=&j;”中指针变量t的类型为int*,&j是初始化表达式中的核心指针,第20行“int*p=(int*)malloc(5*sizeof(int));”中指针变量p的类型为int*,malloc(5*sizeof(int))是初始化表达式中的核心指针,第27行“int*q=p+1;”中指针变量q的类型为int*,p是初始化表达式中的核心指针,第28行“int*(*fp)(int*)=foo;”中指针变量fp的类型为int*(*)(int*),foo是初始化表达式中的核心指针,第29行“int*r=i?p+2:q;”中指针变量r的类型为int*,i?p+2:q是初始化表达式中的核心指针。

步骤s5101,若该核心指针kpe51是指针变量pv51,则将该变量定义替换为:

type51p51=expr51((type51)prfpmd_tbl_update_ptr_ret(&p51,&pv51,pv51))

其中函数prfpmd_tbl_update_ptr_ret用于将p51的指针元数据更新为pv51的指针元数据;

本例中,第27行“int*q=p+1;”中的核心指针p是指针变量,则将该变量定义替换为:

27int*q=(int*)prfpmd_tbl_update_ptr_ret(&q,&p,p)+1;

步骤s5102,若该核心指针kpe51是指针常量pc51,则将该变量定义替换为:

type51p51=expr51((type51)prfpmd_tbl_update_as_ret(&p51,

pc51_status,pc51_base,pc51_bound,pc51))

其中函数prfpmd_tbl_update_as_ret用于将p51的指针元数据更新为其余实参所表示的信息,pc51_status、pc51_base、pc51_bound分别是pc51的状态、下界和上界信息;

本例中,第8行“int*r=&j;”中的核心指针&j是指针常量,第10行“int*t=&j;”中的核心指针&j是指针常量,第28行“int*(*fp)(int*)=foo;”中的核心指针foo是指针常量,则将以上变量定义分别替换为:

08int*r=(int*)prfpmd_tbl_update_as_ret(&r,stack_as,&j,&j+1,&j);

10int*t=(int*)prfpmd_tbl_update_as_ret(&t,stack_as,&j,&j+1,&j);

28int*(*fp)(int*)=(int*(*)(int*))prfpmd_tbl_update_as_ret(&fp,function_as,foo,foo+1,foo);

其中stack_as是所有局部动态变量共用的状态信息,function_as是所有函数共用的状态信息。

步骤s5103,若该核心指针kpe51是函数调用func51(a1,an),其中函数func51返回一个指针,a1,an表示n个实参,则将该变量定义替换为:

type51p51=expr51(prffunc51(&p51,a1,an))

其中函数prffunc51是func51的包装函数,用于将p51的指针元数据更新为func51返回值的指针元数据;

本例中,第20行“int*p=(int*)malloc(5*sizeof(int));”中的核心指针malloc(5*sizeof(int))是函数调用,则将该变量定义替换为:

20int*p=(int*)prfmalloc(&p,5*sizeof(int));

其中函数prfmalloc是malloc的包装函数,用于将p的指针元数据更新为malloc返回值的指针元数据,定义如下:

步骤s5104,若该核心指针kpe51是条件表达式cond?true_ptr:false_ptr,则将该变量定义替换为:

type51p51=expr51((type51)prfcond_expr(&p51,

prfpmd_tbl_lookup(&true_ptr),prfpmd_tbl_lookup(&false_ptr),

cond,cond?true_ptr:false_ptr))

其中函数prfcond_expr用于将p51的指针元数据更新为条件表达式返回值的指针元数据;

本例中,第29行“int*r=i?p+2:q;”中的核心指针i?p+2:q是条件表达式,则将该变量定义替换为:

29int*r=(int*)prfcond_expr(&r,

prfpmd_tbl_lookup(&p),prfpmd_tbl_lookup(&q),i,i?p+2:q);

步骤s52,若当前节点是结构体变量定义structst52obj52=expr52,其中st52是结构体变量类型,且包含n个指针成员(用pf1,pfn表示),obj52是结构体变量名,expr52是初始化表达式;

本例中,第21~25行是结构体变量定义,其中st是结构体变量类型,且包含1个指针成员ptr,s1是结构体变量名,{‘a’,p}是初始化表达式,第26行“structsts2=s1;”是结构体变量定义,其中st是结构体变量类型,且包含1个指针成员ptr,s2是结构体变量名,s1是初始化表达式。

步骤s5201,若该初始化表达式expr52是结构体变量表达式expr52(kpe52),其中kpe52是初始化表达式中的核心结构体,expr52(kpe52)是由kpe52构成的初始化表达式,则将该变量定义替换为:

structst52obj52=(prfpmd_tbl_update_ptr(&obj52.pf1,&kpe52.pf1),

prfpmd_tbl_update_ptr(&obj52.pfn,&kpe52.pfn),

expr52(kpe52));

其中函数prfpmd_tbl_update_ptr和逗号表达式用于将obj52中所有指针成员的指针元数据更新为kpe52中相应指针成员的指针元数据;

本例中,第26行中的初始化表达式s1是结构体变量表达式,其中s1是初始化表达式中的核心结构体,则将该变量定义替换为:

26structsts2=(prfpmd_tbl_update_ptr(&s2.ptr,&s1.ptr),s1);

步骤s5202,若该初始化表达式expr52是基于函数调用的结构体变量表达式expr52(func52(a1,an)),其中函数func52返回一个结构体,a1,an表示n个实参,则将该变量定义替换为:

structst52obj52=expr52(prffunc52(&obj52,a1,an))

其中函数prffunc52是func52的包装函数,用于将obj52中所有指针成员的指针元数据更新为func52返回值中相应指针成员的指针元数据;

本例中,不存在初始化表达式是基于函数调用的结构体变量表达式,故不作处理。

步骤s5203,若该初始化表达式expr52是初始值表达式列表{expr521(kpe521),expr52n(kpe52n)},其中expr52i(kpe52i)是由核心指针kpe52i构成的初始值表达式,1≤i≤n,则将该变量定义替换为:

structst52obj52={

prfpmd_tbl_update_ptr_ret(&obj52.pf1,&kpe521,expr521(kpe521)),

prfpmd_tbl_update_ptr_ret(&obj52.pfn,&kpe52n,expr52n(kpe52n))}

其中函数prfpmd_tbl_update_ptr_ret用于将obj52.pfi的指针元数据更新为kpe52i的指针元数据;

本例中,第25行中的初始化表达式是初始值表达式列表{‘a’,p},则将该变量定义替换为:

25structsts1={‘a’,prfpmd_tbl_update_ptr_ret(&s1.ptr,&p,p)};

步骤s6,遍历抽象语法树,若当前节点是变量赋值表达式,则判断该表达式是否为指针赋值或者是包含指针成员的结构体赋值,若是则插入代码来更新指针元数据,若不是则转至步骤s7;

本例中,第30行、31行和第32行是指针赋值表达式,第34行是包含指针成员的结构体赋值表达式;

步骤s61,若当前节点是指针赋值expr61a(p61)=expr61b(kpe61),其中p61是赋值表达式左部中的核心指针变量,其类型是指针类型type61,expr61a(p61)是由p61构成的赋值表达式左部,kpe61是赋值表达式右部中的核心指针,expr61b(kpe61)是由kpe61构成的赋值表达式右部;

本例中,第30行是指针赋值q=foo(p)+1,其中q是赋值表达式左部中的核心指针变量,其类型是指针类型int*,foo(p)是赋值表达式右部中的核心指针,第31行是指针赋值q=&i,其中q是赋值表达式左部中的核心指针变量,其类型是指针类型int*,&i是赋值表达式右部中的核心指针,第32行是指针赋值q=p+5,其中q是赋值表达式左部中的核心指针变量,其类型是指针类型int*,p是赋值表达式右部中的核心指针;

步骤s6101,若该赋值表达式右部中的核心指针kpe61是指针变量pv61,则将该赋值表达式替换为:

expr61a(p61)=expr61b((type61)prfpmd_tbl_update_ptr_ret(&p61,&pv61,pv61))其中函数prfpmd_tbl_update_ptr_ret用于将p61的指针元数据更新为pv61的指针元数据;

本例中,第32行“q=p+5;”赋值表达式右部中的核心指针p是指针变量,则将该赋值表达式替换为:

32q=(int*)prfpmd_tbl_update_ptr_ret(&q,&p,p)+5;

步骤s6102,若该赋值表达式右部中的核心指针kpe61是指针常量pc61,则将该赋值表达式替换为:

expr61a(p61)=expr61b((type61)prfpmd_tbl_update_as_ret(&p61,

pc61_status,pc61_base,p61_bound,pc61))

其中函数prfpmd_tbl_update_as_ret用于将p61的指针元数据更新为其余实参所表示的信息,pc61_status、pc61_base、pc61_bound分别是pc61的状态、下界和上界信息;

本例中,第31行“q=&i;”赋值表达式右部中的核心指针&i是指针常量,则将该赋值表达式替换为:

31q=prfpmd_tbl_update_as_ret(&q,prfstack_as,&i,&i+1,&i);

步骤s6103,若该赋值表达式右部中的核心指针kpe61是函数调用func61(a1,an),其中函数func61返回一个指针,a1,an表示n个实参,则将该赋值表达式替换为:

expr61a(p61)=expr61b(prffunc61(&p61,a1,an))

其中函数prffunc61是func61的包装函数,用于将p61的指针元数据更新为func61返回值的指针元数据;

本例中,第30行“q=foo(p)+1;”赋值表达式右部中的核心指针foo(p)是函数调用,其中函数foo返回一个指针,p是实参,则将该赋值表达式替换为:

30q=prffoo(&q,p)+1;

步骤s6104,若该赋值表达式右部中的核心指针kpe61是条件表达式cond?true_ptr:false_ptr,则将该赋值表达式替换为:

expr61a(p61)=expr61b((type61)prfcond_expr(&p61,

prfpmd_tbl_lookup(&true_ptr),prfpmd_tbl_lookup(&false_ptr),

cond,cond?true_ptr:false_ptr))

其中函数prfcond_expr用于将p61的指针元数据更新为条件表达式返回值的指针元数据;

本例中,不存在赋值表达式右部中的核心指针是条件表达式的情况,故不作处理。

步骤s62,若当前节点是结构体赋值expr62a(obj62)=expr62b,其中obj62是赋值表达式左部中的核心结构体变量,其类型是结构体类型st62,且包含n个指针成员(用pf1,pfn表示),expr62a(obj62)是由obj62构成的赋值表达式左部,expr62b是赋值表达式右部;

本例中,第34行是结构体赋值s3=s2,其中s3是赋值表达式左部中的核心结构体变量,其类型是结构体类型st,且包含1个指针成员ptr,s2是赋值表达式右部。

步骤s6201,若该赋值表达式右部expr62b是结构体变量表达式expr62b(kpe62),其中kpe62是赋值表达式右部中的核心结构体,expr62b(kpe62)是由kpe62构成的赋值表达式右部,则将该赋值表达式替换为:

expr62a(obj62)=(prfpmd_tbl_update_ptr(&obj62.pf1,&kpe62.pf1),

prfpmd_tbl_update_ptr(&obj62.pfn,&kpe62.pfn),

expr62b(kpe62));

其中函数prfpmd_tbl_update_ptr和逗号表达式用于将obj62中所有指针成员的指针元数据更新为kpe62中相应指针成员的指针元数据;

本例中,第34行赋值表达式右部s2是结构体变量表达式,其中s2是核心结构体,则将该赋值表达式替换为:

34s2=(prfpmd_tbl_update_ptr(&s2.ptr,&s3.ptr),s3);

步骤s6202,若该赋值表达式右部expr62b是基于函数调用的结构体变量表达式expr62b(func62(a1,an)),其中函数func62返回一个结构体,a1,an表示n个实参,则将该赋值表达式替换为:

expr62a(obj62)=expr62b(prffunc62(&obj62,a1,an))

其中函数prffunc62是func62的包装函数,用于将obj62中所有指针成员的指针元数据更新为func62返回值中相应指针成员的指针元数据;

本例中,不存在赋值表达式右部是基于函数调用的结构体变量表达式,故不作处理。

步骤s7,遍历抽象语法树,若当前节点是指针解引用表达式或者数组下标访问表达式,则插入内存错误检测和源代码定位的机制,否则转至步骤s8;

本例中,第5行“inti=*(p+1);”、第35行“i=*q;”和第36行“i=*(a+1);”中存在指针解引用节点,第19行“inti=a[2];”中存在数组下标访问节点。

步骤s71,若当前节点是指针解引用表达式*expr71(kpe71),其中kpe71是指针解引用表达式中的核心指针,其类型是指针类型type71,*expr71(kpe71)是由kpe71构成的指针解引用表达式;

本例中,第5行中存在指针解引用表达式*(p+1),其中p是指针解引用表达式中的核心指针,其类型是指针类型int*;第35行中存在指针解引用表达式*q,其中q是指针解引用表达式中的核心指针,其类型是指针类型int*;第36行中存在指针解引用表达式*(a+1),其中a是指针解引用表达式中的核心指针,其类型是指针类型int*。

步骤s7101,若该核心指针kpe71是指针变量pv71,则将该解引用表达式替换为:

*((type71)(prfcheck_dpv(&pv71,expr71(pv71),sizeof(*type71),

filename,funcname,line,column)))

其中函数prfcheck_dpv用于检测该解引用表达式访问的内存块是否在pv71的指针元数据所记录的范围内,*type71表示type71指向的数据类型,filename,funcname,line,column分别表示当前节点所在的文件名,函数名,行号和列号,

本例中,第5行中*(p+1)的核心指针p是指针变量,第35行中*q的核心指针q是指针变量,则分别替换为:

05inti=*((int*)(prfcheck_dpv(&p,p+1,sizeof(int),“test.c”,“foo”,5,11);

35i=*((int*)(prfcheck_dpv(&q,q,sizeof(int),“test.c”,“main”,35,7);

步骤s7102,若该核心指针kpe71是指针常量pc71,则将该解引用表达式替换为:

*((type71)(prfcheck_dpc(pc71_base,pc71_bound,expr71(pc71),sizeof(*type71),

filename,funcname,line,column)))

其中函数prfcheck_dpc用于检测该解引用表达式访问的内存块是否在pc71的下界pc71_base和上界pc71_bound的范围内,*type71表示type71指向的数据类型,filename,funcname,line,column分别表示当前节点所在的文件名,函数名,行号和列号;

本例中,第36行中*(a+1)的核心指针a是指针常量,则将该解引用表达式替换为:

36i=*((int*)(prfcheck_dpc(a,a+sizeof(a),a+1,“test.c”,“main”,36,7);

步骤s7103,若该核心指针kpe71是函数指针pf71,则将该解引用表达式替换为:

*((type71)(prfcheck_dpf(&pf71,expr71(pf71),

filename,funcname,line,column)))

其中函数prfcheck_dpf用于检测该解引用表达式访问的函数是否在pf71的指针元数据所记录的范围内,filename,funcname,line,column分别表示当前节点所在的文件名,函数名,行号和列号;

本例中,没有对函数指针进行解引用,故不作处理。

步骤s72,若当前节点是数组下标访问表达式expr72(kpe72)[index],其中kpe72是数组下标访问表达式中的核心指针,其类型是指针类型type72,expr72(kpe72)是由kpe72构成的数组基地址表达式,index是数组下标;

本例中,第19行中存在数组下标访问表达式a[2],其中a是数组下标访问表达式中的核心指针,其类型是指针类型int*,a也是数组基地址表达式,2是数组下标。

步骤s7201,若该核心指针kpe72是指针变量pv72,则将该数组下标访问表达式替换为:

expr72(pv72)[prfcheck_dpv_index(prfpmd_tbl_lookup(&pv72),expr72(pv72),index,

sizeof(*type72),filename,funcname,line,column)]

其中函数prfcheck_dpv_index用于检测该表达式访问的内存块是否在pv72的指针元数据所记录的范围内,*type72表示type72指向的数据类型;

本例中,不存在数组下标访问表达式中的核心指针是指针变量的情况,故不作处理。

步骤s7202,若该核心指针kpe72是指针常量pc72,则将该数组下标访问表达式替换为:

expr72(pc72)[prfcheck_dpc_index(pc72_base,pc72_bound,expr72(pc72),index,

sizeof(*type72),filename,funcname,line,column)]

其中函数prfcheck_dpc_index用于检测该表达式访问的内存块是否在pc72的下界pc72_base和上界pc72_bound的范围内,*type72表示type72指向的数据类型;

本例中,第19行中a[2]的核心指针a是指针常量,则将该数组下标访问表达式替换为:

19inti=a[prfcheck_dpc_index(a,a+sizeof(a),a,2,

sizeof(int),“test.c”,“main”,19,11)];

步骤s8,遍历抽象语法树,若当前节点是函数定义,假设函数名为func8,返回值类型为rettype,则判断该函数的返回值和形参是否为指针类型或者是包含指针成员的结构体类型,若是则插入代码来更新形参的指针元数据,并插入包装函数定义来在函数之间传递指针元数据,若不是则转至步骤s9;

本例中,foo函数的返回值和形参均为指针类型。

步骤s81,在函数开头插入以下语句:

unsignedcharret_flag=0;

rettyperet_val;//对于void类型函数不插入此语句

prfauto_stat*stack_as=prfauto_stat_create(prfstack,1);

其中ret_flag用于记录程序运行中该函数是否执行到return语句(值为1),ret_val用于记录该函数的返回值,函数prfauto_stat_create用于创建一个类型为栈的状态信息,且引用者数量为1,stack_as是所有局部动态变量共用的状态信息;

在函数结尾前插入以下语句:

其中label_fid是由该函数体唯一标识符fid构成的标签,函数prfpmd_tbl_remove用于删除所有局部指针变量pf1,pfn的指针元数据,函数prfauto_stat_dc用于将stack_as状态的引用者数量减1;

本例中,在foo函数开头插入以下语句:

unsignedcharret_flag=0;

int*ret_val;

prfauto_stat*stack_as=prfauto_stat_create(prfstack,1);

在函数结尾前插入以下语句:

步骤s82,遍历函数中所有的循环体节点,在每个循环体结尾前插入以下语句:

其中label_lid是由该循环体唯一标识符lid构成的标签,函数prfpmd_tbl_remove用于删除循环体中所有局部指针变量pl1,pln的指针元数据,label_pid是由该循环体的上一层复合语句的唯一标识符pid构成的标签,bc_flag_lid是由该循环体唯一标识符lid为部分名字定义的变量,用于记录程序运行中该函数是否执行到break语句(值为1)和continue语句(值为2);

本例中,在第13行函数foo中的while循环体结尾前插入以下语句:

步骤s83,遍历函数中所有的非循环体复合语句节点,在每个复合语句结尾前插入以下语句:

其中label_bid是由该复合语句唯一标识符bid构成的标签,函数prfpmd_tbl_remove用于删除该复合语句中所有局部指针变量pb1,pbn的指针元数据,label_pid是由该复合语句的上一层复合语句的唯一标识符pid构成的标签,bc_flag_lid是由该复合语句的最近上层循环体的唯一标识符lid构成的变量名;

本例中,在第12行的if复合语句结尾前插入以下语句:

步骤s84,遍历函数中所有的break语句,将break语句替换为:

bc_flag_lid=1;gotolabel_bid;

其中bc_flag_lid是由该复合语句的最近上层循环体的唯一标识符lid构成的变量名,label_bid是由该语句所在的复合语句的唯一标识符bid构成的标签;

本例中,将第11行的break语句替换为:

bc_flag_l1=1;gotolabel_b1;

步骤s85,遍历函数中所有的continue语句,将continue语句替换为:

bc_flag_lid=2;gotolabel_bid;

本例中,不存在continue语句,故不作处理。

步骤s86,遍历函数中所有的return语句,将returnexpr(kpe);语句替换为:

ret_val=expr(kpe);//对于void类型函数不插入此语句

ret_flag=1;gotolabel_bid;

本例中,将第14行的returnp;语句替换为:

ret_val=p;

ret_flag=1;gotolabel_fid;

步骤s87,如果函数有n个形参p1,pn的类型为指针、数组或包含指针成员的结构体,则在函数开头插入以下语句:

prfpmd_tbl_update_fpmd(&p1,prffmd_tbl_lookup_fpmd(func8,1));

prfpmd_tbl_update_fpmd(&pn,prffmd_tbl_lookup_fpmd(func8,n));

其中函数prffmd_tbl_lookup_fpmd用于从指针元数据表中取出函数func8的第i个实参的指针元数据(1≤i≤n),函数prfpmd_tbl_update_fpmd用于将形参pi的指针元数据更新为取出的指针元数据;

本例中,函数foo有一个形参p的类型为指针,则在函数开头插入以下语句:

prfpmd_tbl_update_fpmd(&p,prffmd_tbl_lookup_fpmd(foo,1));

步骤s88,如果函数的返回值类型为指针、数组或包含指针成员的结构体,则在函数的返回语句returnexpr(kpe);之前插入:

prffmd_tbl_update_pmd(func8,0,prfpmd_tbl_lookup(&kpe));

其中函数prffmd_tbl_update_pmd用于将函数func8的第0个指针元数据更新为kpe的指针元数据(即返回值的指针元数据);

本例中,函数foo的返回值类型为指针,则在函数的返回语句returnp;之前插入:

prffmd_tbl_update_pmd(foo,0,prfpmd_tbl_lookup(&p));

步骤s89,在原函数之前插入一个包装函数定义,用于在函数之间传递参数和返回值的指针元数据,定义如下:

其中函数prffmd_tbl_create用于为函数func8创建一个大小为n+1的指针元数据表,函数prffmd_tbl_update_pmd用于将函数func8的第i个实参的指针元数据更新为相应的传入实参的指针元数据(1≤i≤n),函数prfpmd_tbl_update_fpmd用于将返回值的指针元数据更新为函数func8的第0个指针元数据;

本例中,在函数foo之前插入一个包装函数定义,该函数定义如下:

步骤s9,遍历抽象语法树,若当前节点是函数调用表达式func9(a1,an),其中func9是函数名,a1,an表示n个实参,则判断该函数的返回值和形参是否为指针类型或者是包含指针成员的结构体类型,若是则插入额外的实参来传入原实参的指针元数据,若不是则转至步骤s10;

本例中,第20行中包含函数调用表达式malloc(5*sizeof(int)),其中malloc是函数名,且该函数的返回值是指针类型;第30行中包含函数调用表达式foo(p),其中foo是函数名,且该函数的返回值是指针类型,形参也是指针类型;第37行和第38行中包含函数调用表达式free(p),其中free是函数名,且该函数的形参是指针类型。

步骤s91,若该函数的返回值或形参是指针类型,则将该函数调用表达式替换为以下对包装函数的调用:

prffunc9(ret_addr,prfpmd_tbl_lookup(&a1),prfpmd_tbl_lookup(&an),a1,an)

其中ret_addr是函数返回值被赋予的变量的地址;

本例中,第20行中“malloc(5*sizeof(int))”、第30行中“foo(p)”、第37行和第38行中“free(p)”函数的返回值或形参是指针类型,则分别替换为以下对包装函数的调用:

20prfmalloc(&p,5*sizeof(int))

30prffoo(&q,prfpmd_tbl_lookup(&p),p)

37prffree(prfpmd_tbl_lookup(&p),p)

38prffree(prfpmd_tbl_lookup(&p),p)

其中prffree函数的定义如下:

步骤s92,若该函数的返回值或形参是包含指针成员的结构体类型,则将该函数调用表达式替换为以下对包装函数的调用:

prffunc9(ret_addr,prfpmd_tbl_lookup(&a1.a1pf1),prfpmd_tbl_lookup(&a1.a1pfj),

prfpmd_tbl_lookup(&an.anpf1),prfpmd_tbl_lookup(&an.anpfk),a1,an)

其中a1pf1,a1pfj表示a1中的j个指针成员,anpf1,anpfk表示an中的k个指针成员;

本例中,不存在函数的返回值或形参是包含指针成员的结构体类型,故不作处理。

步骤s10,对于所有被改写的源文件,在文件开始位置插入include语句,用于包含所有数据结构、指针元数据操作函数和检测函数的定义文件;

本例中,在test.c文件开始位置插入“#include“memsafe.h””语句,用于包含所有数据结构、指针元数据操作函数和检测函数的定义。

步骤s11,将步骤s5到步骤s10中的所有替换改写保存到相应的源文件中,并将本次改写的文件加入已处理文件列表;

本例中,将以上所有替换改写保存到test.c中,并将“test.c”加入已处理文件列表。

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

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

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

本例中,运行生成的可执行文件,可以自动检测到错误的发生,并报告在test.c中的第5行和第35行出现指针访问越界,第19行出现数组访问越界,第38行出现指针重复释放错误。

通过上述实施例可见,使用源代码变换方法,经过上述步骤的操作,在源代码中加入内存错误自动检测和定位机制,即可在软件运行过程中自动检测内存错误并在源代码中定位。其中步骤s5~s9的操作可以合并为遍历抽象语法树过程中的一个步骤或者同时执行。

与传统检测技术相比,本实施例提供的内存错误的自动检测和定位方法,通过使用指针元数据记录指针指向内存块的边界、状态和类型信息,使得可以检测所有种类的内存错误,从而实现更全面的错误检测。进一步地,本实施例通过将指针元数据和指针分开存储,并不修改代码中指针的存储结构,使得处理后的代码与原代码之间的数据结构兼容,从而实现更好的兼容性。进一步地,本实施例通过使用源代码变换技术,使得变换后的源代码可以使用原有编译器进行编译和部署,从而实现更好的平台普适性。进一步地,本实施例通过对源代码的抽象语法树进行分析,具有充分的语义信息来判断潜在的内存错误所在的源文件和代码行,并相应地进行源代码变换,使得在错误检测中可以使用这些位置信息,从而实现更准确的源代码定位。进一步地,本实施例通过对源代码的抽象语法树进行分析,具有充分的语义信息来消除不必要的代码变换,并通过使用各种优化的哈希表对指针元数据进行存储和索引,使得对指针元数据的查找和存取操作更加高效,从而实现更高的运行时性能和效率。

因此,本实施例可以解决计算机软件和系统,尤其是安全关键软件和系统开发过程中的内存错误的自动检测和定位的难题,能够实现更全面的错误检测,更好的兼容性,更好的平台普适性,更准确的源代码定位,更高的运行时性能和效率,从而克服现有的检测技术中存在的问题。提高软件的质量和软件维护的效率,具有良好的社会效益。

以上所述仅是本发明的优选实施方式,应当指出:对于本技术领域的普通技术人员来说,在不脱离本发明原理的前提下,还可以做出若干改进和润饰,这些改进和润饰也应视为本发明的保护范围。

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