Page 51 - 《软件学报》2021年第9期
P. 51
姜佳君 等:软件缺陷自动修复技术综述 2675
两方面进行了优化:首先,Angelix 依赖测试验证生成补丁的正确性容易产生似真补丁,SemGraft 借助于相似功
能的正确代码作为引用来推导缺陷程序的规约,辅助补丁的验证,提升补丁的质量;其次,SemGraft 结合了基于
反例的程序综合技术,当程序综合器生成的修复补丁不能满足约束时,将反例返回给综合器用于优化下一次的
搜索.该方法通过上述两方面优化可以有效提升补丁的质量,但由于其依赖于存在相同功能的其他代码实现作
为参考,其应用场景受到较大的制约.
2018 年,Lee 等人 [79] 提出了 MemFix,修复 C 语言中的内存释放错误.MemFix 通过类型状态静态分析
(typestate static analysis)技术获取程序中每个位置处所有可以被访问的内存对象的状态,该状态可以通过〈o,
must,mustNot,patch,patchNot〉来表示,其中,o 表示特定内存对象的编号或内存地址,must 指在当前位置一定指向
内存位置 o 的指针集合,mustNot 指在当前位置一定不指向内存位置 o 的指针集合,patch 指在当前位置的安全
修复集合,而 patchNot 指在当前位置不一定安全的修复集合.patch 和 patchNot 中的每个修复是一个二元组(n,e),
表示在第 n 行插入 free(e)语句.根据程序的执行流,动态更新程序的每个位置状态信息.最后,在程序的出口处可
以得到所有的内存状态信息,将其转化为覆盖问题,即:寻找一个最小覆盖,通过插入 free(⋅)语句,使得所有内存状
态最终均得以被释放,且不存在重复释放(double-free).该求解过程属于优化问题,依赖于约束求解器进行求解.
(2) 基于语义的代码搜索和复用
上述介绍的补丁生成方法通过重新组合项目中的已有代码片段生成修复补丁.近期的一些研究通过编码
程序的语义信息,搜索可以直接复用的代码片段用于生成补丁代码.与 MemFix 类似,2018 年,van Tonder 和 Le
Goues [80] 提出了 FootPatch,针对 Java 语言的资源泄漏、内存溢出和空指针引用等缺陷.在该方法中,FootPatch
将程序的语义约束信息用分离逻辑(saparation logic)描述作为程序的规约.在最后的补丁生成阶段,FootPatch 在
项目中搜索满足上述规约的代码段,直接复用其作为正确修复补丁.
2017 年,Ke 等人 [81] 提出了基于代码语义搜索的修复技术 SearchRepair,该方法首先建立了一个代码片段数
据库.与之前方法类似,对于缺陷代码,SearchRepair 首先使用符号执行技术搜集满足程序测试的规约,然后在代
码库中搜索满足对应程序规约的代码段.在搜索的过程中,SearchRepair 利用约束求解器求解代码段中变量与
出错代码中的变量映射关系.当找到满足要求的代码段,则可以根据约束求解结果,使用缺陷代码中的变量替换
搜索得到代码段中的变量,然后直接替换缺陷位置代码即可.上述方法一方面涉及到符号执行在大项目上延展
性差的问题,另一方面,搜索代码库过程需要频繁的约束求解导致其效率比较低.目前,SearchRepair 将代码段的
粒度规定为 1~5 行代码.如果降低代码段的粒度,例如表达式级别,其修复效率将难以接受.
2019 年,Afzal 等人 [82] 针对 SearchRepair 存在的表达力与效率等问题,提出了改进方法 SOSRepair.该方法采
用了一种新的代码编码方式来存储代码,以加速在修复过程中代码的搜索过程.SOSRepair 在代码库的建立阶
段记录每个代码段中使用的变量名和变量类型信息,同时通过符号执行搜索程序所有的可能执行路径并编码
成为约束.在代码搜索阶段,可以根据变量类型以及代码段的约束验证其是否满足所有测试的输入输出约束.除
此之外,SOSRepair 对复杂的代码结构有了更强的兼容性,例如结构体、循环以及库函数等.不仅如此,为了提升
代码搜索效率,SOSRepair 会根据不同变量位置指导变量的映射过程(输入变量映射输入变量),避免约束求解枚
举所有可能的映射关系.最后,SOSRepair 通过使用反例制导的程序规约推断方法降低了对测试的要求,即使只
有未通过测试覆盖修复代码,该方法也可以根据未通过测试推导程序的规约,并在验证补丁失败时动态更新规
约指导之后的补丁搜索过程.
(3) 本节小结
基于语义约束的修复技术通过将补丁生成过程转换成约束求解问题,可以充分利用已有的优化求解算法,
避免反复执行测试来验证候选补丁的正确性.但是该类方法由于依赖于符号执行和约束求解技术,在比较复杂
的大规模程序上难以适用.此外,基于组件的补丁综合方法容易产生过拟合以及可阅读性差的代码片段,从而降
低代码可维护性.虽然最新的研究通过复用已有代码可以在一定程度上提升补丁的可读性,但大量的求解过程
会导致修复过程的低效,限制了可复用代码片段的粒度,进而约束了修复方法所能修复的缺陷数量.