一种检测C代码空指针引用的方法及系统
技术领域
本发明属于计算机信息安全领域中的程序代码检测领域,尤其涉及一种检测C代码中空指针引用的方法。
背景技术
C语言是大量计算机程序所采用的编程语言,在操作系统内核、嵌入式系统、基础程序、大型应用软件等领域都有大量采用C语言编写的软件,作为一门传统的底层系统编程语言,C语言的历史已超过40年,是当前计算机系统中最重要的基础语言之一,但由于C语言本身的特性,一方面允许程序员操作各类底层内存对象(如指针等),另一方面也由此导致了大量与此相关的代码缺陷,空指针引用就是其中比较严重的一类代码缺陷,空指针引用是指程序中存在对值为NULL(空)的指针进行引用的代码,由于这类代码往往只会在部分情况下被执行,而且并非每次都会发生,因此具有很强的隐蔽性,而程序中一旦对空指针进行引用,由于该操作违反了操作系统的内存访问控制,将立即导致程序发生内存错误并被操作系统强行终止,进而导致用户数据丢失,关键部件失效等致命后果。
当前解决空指针引用的方法主要有静态方法和动态方法两类。静态方法主要是模型检测技术,即通过对待检测代码进行建模规约后,针对相应的安全属性进行验证的技术,例如一种使用简单而不复杂的方法来侦测内存访问错误,对于工业代码和学生代码都有一定的效果,但由于需要对大量的状态进行建模,存在状态爆炸问题,其检测效率较低、耗时多的问题;定理证明技术,即通过对程序代码的逻辑语义进行形式化推理来验证相关属性的技术,例如一种基于证明技术对C程序进行验证以排除空指针引用等缺陷的方法,但该定理证明技术需要用户对目标代码具有相当程度的理解以便应用相关的定理策略,具有难以应用、复杂性较高等缺点,同时对于一些复杂的控制逻辑也无法做到完全自动化证明;代码分析技术,即首先获得源代码的抽象语法树,然后进行一定的控制流、数据流分析,结合预先制定的缺陷规则库进行匹配的方法,动态方法主要特点是通过代码插桩、运行时拦截、沙箱虚拟执行等方法,在程序执行的过程中动态检测可能存在的空指针引用缺陷,例如一种使用代码漏洞模式匹配的方法来侦测java中代码缺陷,虽然在一定程度上弥补了模型检测的低效率和定理证明的难应用问题,但仍然存在使用复杂耗时的缺点。
发明专利“一种充分检测空指针引用缺陷的方法”公开了一种充分检测空指针引用缺陷的方法,包括:基于抽象语法树识别出被测应用的全部可寻址表达式;根据控制流图对被测应用进行保守的区间运算与指针分析并根据区间运算与指针分析的结果,生成函数摘要;根据所述函数摘要以及抽象语法树识别出全部的指针引用以及被引用的指针,并对每个被引用的指针创建空指针引用缺陷状态机实例;基于控制流图运行空指针引用缺陷状态机实例,在控制流图的每个节点上,根据区间运算、指针分析的结果对每个缺陷状态机实例进行状态迁移,进行空指针引用检测。采用该发明,能有效解决空指针引用缺陷的漏报问题,实现空指针引用缺陷检测零漏报及低误报。但是该发明通过基于区间运算和状态机建模分析的方法对源代码进行检测,这与本发明提出的插入动态监测代码的方法完全不同,同时本发明也不需要对源代码进行区间分析,也不需要对目标程序进行缺陷状态机建模等步骤。
发明专利“一种检查源代码中空指针的方法和计算机系统”公开了一种检查源代码中空指针的方法,包括:计算机系统构造可引用变量类型字典,计算机系统接收扫描空指针的指令,格式化待检查的源代码;扫描待检查源代码的全局代码区域,获得与所述字典中变量声明类型对应的未初始化全局变量列表;扫描所述待检查源代码的方法区域,获得与所述字典中变量声明类型对应的未初始化局部变量列表;根据未初始化全局变量列表,获取方法下未初始化全局变量列表;扫描所述待检查源代码的方法区域,查找被调用的变量名,如果所述被调用的变量名在未初始化局部变量列表或方法下未初始化全局变量列表中,该查找到的被调用的变量名即为源代码中的空指针。但是该发明采用直接扫描源代码,通过一定的模式来分析源代码中可能存在的空指针问题,这与本方法的插入动态检测代码,同时编译后进行执行时判断执行返回的结果来确定空指针错误的方法完全不同,该发明只需要扫描源代码而不需要编译,本发明的检测阶段是在运行时自动完成,所采用技术方法存在明显差异。
发明内容
针对现有技术不足,本发明提出一种新的检测C代码中空指针引用缺陷的方法。
为实现上述目的,本发明提供了一种检测C代码空指针引用的方法,该方法包括:
步骤1,简化该C代码,得到目标代码,根据该目标代码通过语义分析获取该目标代码的语法树结构;
步骤2,获取待检测的函数,遍历该语法树结构,查找与该函数相对应的语法树结构,遍历该函数的语法树结构,在判断语句或循环语句的节点之前插入动态检测过程的语法树结构,得到新语法树结构;
步骤3,根据该新语法树结构,得到最终代码,编译并执行该最终代码,若该最终代码执行异常,则输出该空指针的位置。
所述的检测C代码空指针引用的方法,该步骤1还包括,将该C代码中的判断语句统一为if else语句,将循环语句统一为while语句。
所述的检测C代码空指针引用的方法,该动态检测过程包括生成未使用赋值集合,其中该赋值集合中的值使该判断语句或循环语句产生真、假两种赋值结果。
所述的检测C代码空指针引用的方法,还包括,将该值赋予该判断语句或循环语句中条件语句的变量,将该值存入已使用赋值集合,同时将该未使用赋值集合中的该值删除。
所述的检测C代码空指针引用的方法,还包括若该最终代码执行异常,则输出该值,并将该已使用赋值集合中的该值删除,遍历该未使用赋值结合。
本发明还提供了一种检测C代码空指针引用的系统,包括:
生成语法树结构模块,用于简化该C代码,得到目标代码,根据该目标代码通过语义分析获取该目标代码的语法树结构;
检测模块,用于获取待检测的函数,遍历该语法树结构,查找与该函数相对应的语法树结构,遍历该函数的语法树结构,在判断语句或循环语句的节点之前插入动态检测过程的语法树结构,得到新语法树结构;
输出模块,用于根据该新语法树结构,得到最终代码,编译并执行该最终代码,若该最终代码执行异常,则输出该空指针的位置。
所述的检测C代码空指针引用的方法,简化代码模块,用于将该C代码中的判断语句统一为if else语句,将循环语句统一为while语句。
所述的检测C代码空指针引用的方法,该动态检测过程包括生成未使用赋值集合,其中该赋值集合中的值使该判断语句或循环语句产生真、假两种赋值结果。
所述的检测C代码空指针引用的方法,还包括,将该值赋予该判断语句或循环语句中条件语句的变量,将该值存入已使用赋值集合,同时将该未使用赋值集合中的该值删除。
所述的检测C代码空指针引用的方法,还包括若该最终代码执行异常,则输出该值,并将该已使用赋值集合中的该值删除,遍历该未使用赋值结合。
由以上方案可知,本发明的优点在于:
通过应用本发明,可以达到方便、快速检测C代码中可能存在的空指针引用缺陷,优点是本发明直接编译生成嵌入了检测代码的可执行二进制文件,该二进制文件执行的同时就完成了检测,具有检测速度快,方便易用,能完美利用现有硬件平台的各项特性与程序内在特性,使用者能在完成代码编写后,立即对程序代码进行检测,并根据检测结果,确定程序代码中可能的空指针引用缺陷然后进行相关修改工作,通过在不需要先验知识的情况下快速检测代码,可以节省使用者大量精力和时间,方便使用者快速消除代码中的内存访问错误漏洞等优点。
附图说明
图1为本发明的整体应用流程图;
图2为本发明的检测时原理流程图;
图3为本发明中简化源代码时的目标。
其中附图标记为:
步骤100为本发明整体步骤,包括:
步骤101/102/103/104/105/106/107/108;
步骤200为动态过程算法步骤,包括:
步骤201/202/203/204/205/206/207/208/209/210/211。
具体实施方式
本发明所述方法需要源代码的简化、源代码的变换(插入检测代码)、该检测代码包括控制流逻辑与具体代码检测两部分。本发明提供的方法针对的目标是用户编写完成的C源代码文件,本发明的输出是经过变换后的C源代码文件,用户通过编译变换后的C源代码文件并执行来获得检测结果,本发明的成功实施要求用户调用标准的C编译器完成编译操作。
本发明包含以下几部分:
源代码的简化:对于待检测的源代码,为了降低后续分析的复杂度,节省分析的时间并提高效率,首先对源代码在保持语义不变的情况下进行简化,简化指将源代码中的各种语句进行统一和规整,包括将多种不同的循环语句(如for,do while等)统一为采用while语句形式的循环,将多种判断语句(如?:,switch等)统一为if else的形式,本步骤简化后的源代码将作为分析获得源代码的语法树结构步骤的输入。
分析获得源代码的语法树结构:对于输入的源代码进行语法树分析,首先调用标准C语言预处理器(如gcc)对源代码进行预处理操作,然后对预处理后的源代码进行语法分析,获得能够完整表达该源代码语义的语法树结构,本步骤获得的语法树结构将传递到构造并插入检测代码步骤作为输入。
构造并插入检测代码:本步骤基于输入的语法树结构,进行语法树层面的变换操作,具体步骤为:根据用户指定的需要检测的函数,遍历语法树结构获得所述函数对应的语法树结点,之后进入该语法树结点进行后续步骤分析;在函数体中遇到if、while语句时,在遇到的if、while语句前插入动态检测过程,所述动态检测过程用于动态决定代码执行流程,完成空指针引用的检测等操作,其具体内容需要基于上下文语义信息来决定;重复遍历所有的语法树结点,直到所有if、while语句前都插入动态检测过程,并将新产生的语法树结构作为产生变换后的源代码步骤的输入。
产生变换后的源代码:将上一步骤产生的语法树结构重新生成对应的C源代码,所生成的C源代码必须从语义上完全与所述语法树结构表达的语义一致,输出对应的C源代码文件作为编译源代码后执行检测步骤的输入。
编译源代码后执行检测:按照所述源代码文件本身的编译参数对源代码文件进行编译,编译生成可执行二进制文件,执行所述二进制文件并等待结果即可,执行该二进制文件的过程同时就是检测过程,检测结果会自动输出到标准输出。
以下为本发明整体流程,如图1所示,具体步骤如下:
执行步骤101用户提交C源代码,执行步骤102代码简化,语法树生成,首先对待检测的C源代码进行简化以及预处理工作,简化源代码时的具体方法如图3所示,图3中第1列给出了需要简化的源代码的模式,第2列给出了简化后的源代码模式,主要转化目的是将不同形式的语句统一为具有单一形式同时保持语义不变的结构,可以直接调用CIL(C中间语言)对目标源代码进行简化,对简化后的代码进行预处理操作,直接调用用户所使用编译器完成即可,例如gcc编译器可使用命令gcc -E对目标源代码进行预处理,对预处理后的目标源代码进行语法树分析,可以基于编译器或完全手工编写相关词法分析、语法分析模块,通过对源代码的语法分析,得到对应目标源代码的语法树结构,执行步骤103根据用户指定的需要检测的函数,基于上下文进行语义分析,遍历语法树结构找到用户指定函数的定义部分所对应的语法树结构,执行步骤104动态检测过程检测语法树结构,遍历所述语法树结构,找到所有的if语句和while语句结点,在遍历得到的所有if语句和while语句结点之前插入动态检测过程(检测代码),执行步骤105生成动态检测过程的语法树结构,所述动态检测过程的语法树结构必须满足源代码语法树结构的语义环境,然后将所述动态检测过程的语法树结构设置为所述if语句和while语句语法树结构的父节点,从效果上来说,使所述动态检测过程在if语句和while语句的前一刻执行,执行步骤106代码生成,将变换后的语法树结构输出为C源代码,保存C源代码到相应文件中,替换原来的C源代码文件内容,执行步骤107编译,编译新生成的C源代码文件,得到可执行二进制文件,执行步骤108执行该二进制文件,执行该二进制文件,执行的过程就是检测过程,执行的结果会自动输出检测到的内存访问错误漏洞。
以下对本发明的动态检测过程进行详细说明:
动态检测过程的代码内容需要根据插入位置的上下文语义进行变化,变化的依据是使得动态检测过程插入位置后面的if语句和while语句中的条件判断部分能产生不同的赋值结果,即分别产生能够使后续if语句、while语句产生真、假两类情况的变量赋值,然后针对真、假两种赋值情况,动态检测过程分别fork(分叉)出一个子进程并跟踪子进程的执行结果,同时为了检测输出时向用户提供具体的赋值情况,动态检测过程还需要将插入点的赋值信息预先保存到一个临时记录中,等待动态检测过程读取该记录获得对应的赋值信息,当动态检测过程等待的子进程返回时,需要取出之前保存的条件变量的赋值情况,同时根据所等待的子进程的执行结果决定具体报告信息,如果子进程成功执行并返回一个成功值,说明在相应的后续代码中没有检测到空指针引用漏洞,如果后续进程发生段错误导致程序异常退出,说明在当前变量赋值情况下发生了空指针引用错误,因此需要打印出前述保存的变量赋值情况和当前动态检测过程插入点位置,向用户报告检测到的空指针引用情况。
以下为本发明的一个实施例,具体如下:
首先用户确定待检测的目标函数为bad_func,该函数是经过简化后的代码,具体如下所示:
根据本发明的方法,需要对该段代码插入动态检测过程,首先确定函数中一共有两处if和一处while语句,因此共需要插入3处动态监测过程。
第1处if语句:为了使后续if中的条件表达式产生真、假两种赋值情况,这里需要产生a<b和!(a<b)两种约束条件下的a、b赋值;
第2处while语句:为了使后续while中的条件表达式产生真、假两种赋值情况,这里需要产生b<c和!(b<c)两种约束条件下的b、c赋值;
第3处if语句:为了使后续if中的条件表达式产生真、假两种赋值情况,这里需要产生a+b>c和!(a+b>c)两种约束条件下的a、b、c赋值。
确定如何产生赋值情况后,可以采用穷举法或者调用Z3定理证明器等对获得的约束情况进行求解,进而得到具体的赋值情况,如针对上述第一处if语句中的两种可能的赋值情况分别为:a=3,b=5(约束条件为真)和a=22,b=-88(约束条件为假)。
完成上述过程后,对第一处,第二处,第三处,三处插入点依照获得变量赋值情况按如下算法产生动态检测过程,具体步骤如下:
如图2所示,执行步骤201算法开始,执行步骤202检查是否已遍历所有赋值情况,如果是,则执行步骤211结束算法,如果否,则执行步骤203从集合S(记录未处理过得赋值情况的集合)中取出一种赋值情况,记为a并从S中删去a,执行步骤204按照记录a的说明对涉及变量进行赋值,执行步骤205将赋值情况a加入集合V(记录已赋值情况的集合),执行步骤206分叉产生一个子进程,执行步骤207等待子进程执行完毕并判断其返回值,若返回程序执行成功,则执行步骤202,若返回程序执行失败,则执行步骤208向用户报告发生了空指针引用错误,执行步骤209输出集合V中所有的赋值情况和空指针位置,执行步骤210清空集合V,执行步骤202。
本发明还包括以下模块:
生成语法树结构模块,用于简化该C代码,得到目标代码,根据该目标代码通过语义分析获取该目标代码的语法树结构;
检测模块,用于获取待检测的函数,遍历该语法树结构,查找与该函数相对应的语法树结构,遍历该函数的语法树结构,在判断语句或循环语句的节点之前插入动态检测过程的语法树结构,得到新语法树结构;
输出模块,用于根据该新语法树结构,得到最终代码,编译并执行该最终代码,若该最终代码执行异常,则输出该空指针的位置。
简化代码模块,用于将该C代码中的判断语句统一为if else语句,将循环语句统一为while语句。
该动态检测过程包括生成未使用赋值集合,其中该赋值集合中的值使该判断语句或循环语句产生真、假两种赋值结果;将该值赋予该判断语句或循环语句中条件语句的变量,将该值存入已使用赋值集合,同时将该未使用赋值集合中的该值删除;若该最终代码执行异常,则输出该值,并将该已使用赋值集合中的该值删除,遍历该未使用赋值结合。