本发明属于程序分析与测试领域,具体涉及一种基于标识符一致性的堆对象use-after-free漏洞检测方法。
背景技术:
c和c 等低级语言提供了对堆内存的低级管理,开发人员可以灵活地在堆内存上分配和释放堆对象。由于c和c 语言的灵活性和高效性,大量的系统程序,例如浏览器、数据库、服务器等使用c和c 语言开发,广泛地应用在日常生活中。但随着程序规模的扩大,以及模块化思想的发展,开发人员很难做到始终正确地分配和释放堆对象。因此,以c和c 语言开发的程序容易出现use-after-free漏洞等时序错误,这已经成为现代软件系统中不安全的重要原因。一项研究报告指出,从2009年到2019年,登记在册的use-after-free漏洞数量呈逐年上升趋势,其中80%以上的use-after-free漏洞标记为高风险或严重风险。因此,开发人员应重视c/c 程序中的堆对象use-after-free漏洞。
目前,与其他内存错误相比,c/c 程序中的堆对象use-after-free漏洞难以通过手动或静态分析方式进行检测,原因如下。首先,指针存在别名,很难推断出分布在众多数据结构中的所有指针别名。其次,确定指针应该指向哪个内存对象具有挑战性。最后,但并非最不重要的一点是,由于程序规模增大而引起的路径爆炸问题使得执行过程间分析变得相当困难。
尽管有很多研究工作提出了针对c/c 程序中堆对象use-after-free漏洞的动态的检测方法,但它们大多是基于对象位置的方法。它们使用影子内存记录堆对象的分配/释放状态,但不区分随着程序执行先后在同一堆地址上分配的不同堆对象。这意味着它们只能检测到已释放的堆内存尚未重新分配时发生的use-after-free漏洞。当存储已释放堆对象的堆内存被重新分配以存储另一个堆对象时,它们不能检测发生在重新分配的堆内存上的堆对象use-after-free漏洞。对于基于预测的动态检测方法,它们只适用于检测多线程程序的并发use-after-free漏洞,并不能检测到顺序程序和多线程程序的单线程内的use-after-free漏洞。因此,目前仍然缺乏一种动态的,能够不受内存重用影响的,有效地检测c/c 程序中的堆对象use-after-free漏洞的方法。
技术实现要素:
本发明的目的在于提供一种基于标识符一致性的堆对象use-after-free漏洞检测方法,用于高效、有效地检测c/c 程序中的堆对象use-after-free漏洞。
实现本发明目的的技术解决方案为:一种基于标识符一致性的堆对象use-after-free漏洞检测方法,以c/c 程序的源码为输入,以检测出的堆对象use-after-free漏洞为输出结果,步骤如下:
步骤1,使用llvm和clang将输入的c/c 程序源码转换为llvmir文件,基于llvmir文件来找到输入的c/c 程序中堆对象分配、指针传播以及指针解引用的代码的位置;
步骤2,对输入的c/c 程序进行代码插桩,实现为每个分配的堆对象以及指向该堆对象的所有指针指定相同的唯一标识符和为指针解引用插入内存检查的功能,得到插桩后的c/c 程序;
步骤3,运行插桩后的c/c 程序,程序运行过程中会执行插桩代码来比较指针的对象标识符和指针实际指向的当前对象的标识符是否匹配,从而检测是否发生了堆对象use-after-free漏洞。
本发明与现有技术相比,其显著优点为:(1)不受程序执行过程中内存重用的影响,能够有效地检测程序执行过程中发生的堆对象use-after-free漏洞;(2)在提高了检测堆对象use-after-free漏洞的准确性的同时,具有较低的运行时开销和内存开销。
附图说明
图1是基于标识符一致性的堆对象use-after-free漏洞检测方法的流程图。
图2是存在堆对象use-after-free漏洞的c/c 程序的源码示例图。
图3是使用llvm和clang编译源码得到的llvmir形式的代码示例图。
图4是插桩完成后的llvmir形式的代码示例图。
图5是检测到发生堆对象use-after-free漏洞时给出的诊断信息示例图。
具体实施方式
本发明提出了一种高效的基于标识符一致性的堆对象use-after-free漏洞的检测方法,以c/c 程序的源码为输入,以检测出的use-after-free漏洞为输出结果,其整体流程如图1所示。该检测方法具体实现如下:
步骤1,使用llvm和clang将输入的c/c 程序源码转换为llvmir文件,基于llvmir文件来找到输入的c/c 程序中堆对象分配、指针传播以及指针解引用的代码位置,具体步骤如下:
步骤1-1,使用llvm和clang来编译c/c 程序源码,并转换为llvmir文件,得到的llvmir文件是c/c 程序源码的中间表示形式;
步骤1-2,遍历所有llvmir文件,搜索所有的堆对象分配、指针传播以及指针解引用语句,记录下每条语句的代码位置。
步骤2,对输入的c/c 程序进行代码插桩,实现为每个分配的堆对象以及指向该堆对象的所有指针指定相同的唯一标识符和为指针解引用插入内存检查的功能,得到插桩后的c/c 程序,具体包括以下步骤:
步骤2-1,使用llvm提供的代码插桩功能,对输入的c/c 程序进行代码插桩,具体插桩规则包括:
(1)在所有的堆对象分配的代码位置处,将堆对象分配函数替换为自定义的堆对象分配函数,从而实现在堆对象分配时为每个新分配的堆对象指定一个唯一的标识符,在堆对象释放时将堆对象的唯一标识符给无效化;
(2)在所有的指针传播的代码位置处,插入相应的代码从而将堆对象的标识符传播到所有指向该堆对象的指针,即所有指向该堆对象的指针拥有和堆对象相同的唯一标识符;
(3)在所有的指针解引用的代码位置处,插入相应的内存检查从而在指针解引用堆对象时,判断是否发生了堆对象use-after-free漏洞;
步骤2-2,使用llvm提供的代码优化功能,对插桩后的llvmir文件进行优化,具体优化规则包括:
(1)当指针解引用堆栈对象或全局对象时,消除插入的指针解引用内存检查,因为这不涉及堆对象use-after-free漏洞;
(2)当指针在循环中多次指针解引用同一堆对象时,在循环之前插入一个指针解引用内存检查,并消除循环内的指针解引用内存检查;
(3)当顺序执行的代码之间存在访问同一堆对象的多个指针解引用时,只保留一个指针解引用内存检查,消除其余的指针解引用内存检查;
步骤2-3:利用得到的llvmir文件生成插桩后的c/c 程序。
步骤3,运行插桩后的c/c 程序,程序运行过程中会执行插桩代码来比较指针的对象标识符和指针实际指向的当前对象的标识符是否匹配,从而检测是否发生了堆对象use-after-free漏洞,具体步骤如下:
步骤3-1,使用命令行工具将插桩后的c/c 程序运行起来;
步骤3-2,使用c/c 程序附带的测试套件中的测试用例或模糊测试工具生成的测试用例作为插桩后的c/c 程序的输入;
步骤3-3,在程序发生指针解引用时,程序会执行插桩的指针解引用内存检查代码,通过比较指针关联的堆对象的标识符和指针实际指向的当前堆对象的标识符是否匹配来判断是否发生了堆对象use-after-free漏洞;
步骤3-4,如果匹配,则没发生堆对象use-after-free漏洞,程序继续执行;如果不匹配,那么程序崩溃并给出相应的use-after-free漏洞诊断信息,即给出分配堆对象、释放堆对象和访问堆对象时所对应的源代码的位置。
下面结合实施例和附图对本发明进行详细说明。
实施例
本发明为一种基于标识符一致性的堆对象use-after-free漏洞检测方法,以c/c 程序的源码作为输入,以检测出的堆对象use-after-free漏洞作为输出。为了检测堆对象use-after-free漏洞,首先对输入的c/c 程序进行静态分析,找到和堆对象分配、指针传播以及指针解引用相关的代码的位置;之后,在定位到的相关代码位置处,对c/c 程序进行代码插桩,来为每个分配的堆对象以及指向该对象的所有指针指定相同的唯一标识符和为指针解引用插入内存检查;最后,运行插桩后的程序,程序运行过程中会执行插桩代码来比较指针的对象标识符和指针实际指向的当前对象的标识符是否匹配,从而检测程序是否发生了堆对象use-after-free漏洞。
结合实例,该方法包括:
步骤1,对于输入的c/c 程序的源代码,获取程序源码中和堆对象分配、指针传播以及指针解引用相关的代码的位置,具体步骤如下:
步骤1-1,使用llvm和clang将c/c 程序的源码转换为llvmir文件,其中图2、图3分别展示了源码形式和llvmir形式的代码示例;
步骤1-2,遍历所有llvmir文件,搜索所有和堆对象分配、指针传播以及指针解引用相关的语句。搜索完成后,将得到程序中所有和堆对象分配、指针传播以及指针解引用相关的语句的代码位置;
步骤2,在所有的堆对象分配、指针传播以及指针解引用语句的代码位置处,对输入的c/c 程序进行代码插桩,具体步骤如下:
步骤2-1,使用制定的插桩规则,分别对堆对象分配、指针传播以及指针解引用语句进行插桩,以实现为每个分配的堆对象以及指向该对象的所有指针指定相同的唯一标识符和为指针解引用插入内存检查,如图4所示;
步骤2-2,根据制定的优化规则,对插桩后的llvmir文件进行优化;
步骤2-3,利用得到的llvmir文件生成插桩后的c/c 程序;
步骤3,运行插桩后的c/c 程序,程序运行过程中会执行插桩代码来比较指针的对象标识符和指针实际指向的当前对象的标识符是否匹配,从而检测是否发生了堆对象use-after-free漏洞,具体步骤如下:
步骤3-1,使用命令行工具将插装后的c/c 程序运行起来,如./a.out,其中./是要运行的程序所在的路径,a.out是要运行的程序的名称;
步骤3-2,使用c/c 程序附带的测试套件中的测试用例或模糊测试工具生成的测试用例作为插装后的c/c 程序的输入(非必须);
步骤3-3,在程序发生指针解引用时,程序会执行插桩的指针解引用内存检查代码,通过比较指针关联的堆对象的标识符和指针实际指向的当前堆对象的标识符是否匹配来判断是否发生了堆对象use-after-free漏洞;
步骤3-4,如果匹配,则没发生堆对象use-after-free漏洞,程序继续执行;如果不匹配,那么程序崩溃并给出相应的use-after-free漏洞诊断信息,即给出分配堆对象、释放堆对象和访问堆对象时所对应的源代码的位置。如图5所示,第1行指出检测到一个堆对象use-after-free漏洞;第2-7行给出了分配堆对象时所对应的源代码的位置;第8-13行给出了释放堆对象时所对应的源代码的位置;第14-19行给出了访问堆对象时所对应的源代码的位置。
1.一种基于标识符一致性的堆对象use-after-free漏洞检测方法,以c/c 程序的源码为输入,以检测出的use-after-free漏洞为输出结果,其特征在于,具体步骤如下:
步骤1,使用llvm和clang将输入的c/c 程序源码转换为llvmir文件,基于llvmir文件来找到输入的c/c 程序中堆对象分配、指针传播以及指针解引用的代码位置;
步骤2,对输入的c/c 程序进行代码插桩,为每个分配的堆对象以及指向该堆对象的所有指针指定相同的唯一标识符和为指针解引用插入内存检查,得到插桩后的c/c 程序;
步骤3,运行插桩后的c/c 程序,程序运行过程中会执行插桩代码来比较指针的对象标识符和指针实际指向的当前对象的标识符是否匹配,从而检测是否发生堆对象use-after-free漏洞。
2.根据权利要求1所述的基于标识符一致性的堆对象use-after-free漏洞检测方法,其特征在于,步骤1中使用llvm和clang将输入的c/c 程序源码转换为llvmir文件,基于llvmir文件来找到输入的c/c 程序中堆对象分配、指针传播以及指针解引用的代码位置,具体步骤如下:
步骤1-1,使用llvm和clang来编译c/c 程序源码,并转换为llvmir文件,得到的llvmir文件是c/c 程序源码的中间表示形式;
步骤1-2,遍历所有llvmir文件,搜索所有的堆对象分配、指针传播以及指针解引用语句,记录下每条语句的代码位置。
3.根据权利要求1所述的基于标识符一致性的堆对象use-after-free漏洞检测方法,其特征在于,步骤2中所述对输入的c/c 程序进行代码插桩,实现为每个分配的堆对象以及指向该堆对象的所有指针指定相同的唯一标识符和为指针解引用插入内存检查的功能,得到插桩后的c/c 程序,具体包括以下步骤:
步骤2-1,使用llvm提供的代码插桩功能,对输入的c/c 程序进行代码插桩;
步骤2-2,使用llvm提供的代码优化功能,对插桩后的llvmir文件进行优化,具体优化规则包括:
(1)当指针解引用堆栈对象或全局对象时,消除插入的指针解引用内存检查;
(2)当指针在循环中多次指针解引用同一堆对象时,在循环之前插入一个指针解引用内存检查,并消除循环内的指针解引用内存检查;
(3)当顺序执行的代码之间存在访问同一堆对象的多个指针解引用时,只保留一个指针解引用内存检查,消除其余的指针解引用内存检查;
步骤2-3:利用得到的llvmir文件生成插桩后的c/c 程序。
4.根据权利要求3所述的基于标识符一致性的堆对象use-after-free漏洞检测方法,其特征在于,步骤2-1中,具体插桩规则包括:
(1)在所有的堆对象分配的代码位置处,将调用的堆对象分配函数替换为自定义的堆对象分配函数,从而实现在堆对象分配时为每个新分配的堆对象指定一个唯一的标识符,在堆对象释放时将堆对象的唯一标识符给无效化;
(2)在所有的指针传播的代码位置处,插入相应的代码从而将堆对象的标识符传播到指向该堆对象的所有指针,即所有指向该堆对象的指针拥有和堆对象相同的唯一标识符;
(3)在所有的指针解引用的代码位置处,插入相应的内存检查从而在指针解引用堆对象时,判断是否发生了堆对象use-after-free漏洞。
5.根据权利要求1所述的基于标识符一致性的堆对象use-after-free漏洞检测方法,其特征在于,步骤3中所述运行插桩后的c/c 程序,程序运行过程中会执行插桩代码来比较指针的对象标识符和指针实际指向的当前对象的标识符是否匹配,从而检测是否发生了堆对象use-after-free漏洞,具体步骤如下:
步骤3-1,使用命令行工具将插桩后的c/c 程序运行起来;
步骤3-2,使用c/c 程序附带的测试套件中的测试用例或模糊测试工具生成的测试用例作为插桩后的c/c 程序的输入;
步骤3-3,在程序发生指针解引用时,程序会执行插桩的指针解引用内存检查代码,通过比较指针关联的堆对象的标识符和指针实际指向的当前堆对象的标识符是否匹配来判断是否发生了堆对象use-after-free漏洞;
步骤3-4,如果匹配,则没发生堆对象use-after-free漏洞,程序继续执行;如果不匹配,那么程序崩溃并给出相应的use-after-free漏洞诊断信息,即给出分配堆对象、释放堆对象和访问堆对象时所对应的源代码的位置。
技术总结