1.Introduction
测试用例生成的核心问题是:how to achieve higher testing coverage。constraint-based方法(符号执行和混合执行)是一种有效方法,但是受制于路径爆炸、外部函数(系统调用、网络通信)。比如下面示例希望klee来生成让 ret
为 0
的 s
比较困难。而ChatGPT可以生成 s = "ls"
来让 ret
为 0
。
int execute(char* s) {
int ret = system(s);
if ( ret == 0) {
// target line
}
return
}
LLM提供了新的思路,不过貌似也不是万能药。下面示例中,理想的测试用例是 s == "4"
,ChatGPT和CodeLlama没能理解:(1) 模运算, (2) 数组索引, (3) 算数运算。
int arr(char *s){
int symvar = s[0] - 48;
int ary[] ={1 ,2 ,3 ,4 ,5};
if (ary[symvar % 5] == 5) {
// target line
}
}
为了更近一步评估符号执行和LLM的差别,作者采用(1).logic_bombs [ 2 ] ^{[2]} [2] 和 (2).基于HumanEval和HumanEval-X构建的PatchEval数据集合。作者发现:
-
1.对于directed input generation. LLM的有时候性能超过constraint-based tools。ChatGPT在PathEval中通过了51.21%-58.57%的测试用例。在logic_bombs中,ChatGPT的通过率分别超过Angr和Klee 15.10%以及18.87%。
-
2.进一步比对LLM和constraint-based tools需要分场景,ChatGPT在处理并发程序通信等需要外部知识的场景具有优势,而符号执行工具在处理精确计算(比如array index)等场景具有优势。
作者随后构造了工具LLMsym,结合LLM和constraint-based tools,通过率达到85.83%,分别超过Klee 44.22%,GPT 46.54%。
2.Experimental Setup
-
RQ1: LLM在directed input generation方面表现如何?
-
RQ2: LLM能否超越传统traditional constraint-based tools?
-
RQ3: LLM能否与传统工具整合更好地进行input generation?
benchmark信息如下
作者用通过率测试性能,一个dataset包含
N
N
N 个sample,作者对每个sample尝试
M
M
M 次,只要有1次成功就算该sample通过,最后计算通过的sample占比为通过率。
作者选择的LLM包括:GPT-3.5(gpt-3.5-turbo-0125)、StarCoder2(7B)、CodeLlama(7B)、CodeQwen(7b-chat)。用GPT-3.5时作者选择了默认参数,对于开源模型,作者follow guideline采用(temperature = 0.1
, top_p = 0.95
, max_new_token = 100
)
作者选择的constraint-based tool包括:klee (LLVM IR)、angr (Binary)、CrossHair (python)、EvoSuite (Java)。
3.Evaluation Result
1.RQ1: Effectiveness of LLM
下表为4种LLM的通过率,Att表示尝试次数 M M M 分别为1和5,3个开源LLM的第二列为相比ChatGPT性能的差值。可以看到GPT表现最好,以及多次尝试效果比单次好, M = 5 M = 5 M=5 时通过率在43.40%-58.57%。
2.RQ2: Comparison with Traditional Tools
可以观察到GPT-3.5在数据集上表现与Klee相当,并显著优于Python和Java上的CrossHair和EvoSuite。
不过为了深入探索,作者分析了PathEval(C++)数据集中通过样本的覆盖情况:下图展示了LLMs与KLEE在PathEval(C++)数据集上的成功样本分布与差异。
-
Klee:有最多的独占成功样本,达116个,占总样本的15.7%。
-
GPT-3.5:次之,占7.02%。
-
CodeLlama:再次,占2.29%。
这些数据表明,符号执行工具(如KLEE)和LLMs可能各有优势与劣势,其结果存在显著差异。
样本分类:为了直观分析两类工具在不同场景下的表现,作者尝试按代码场景对样本进行分类。由于手动详细分类2000多个样本难度较大,作者借助了logic_bombs数据集。该数据集在构建时已基于符号执行技术的挑战场景进行了分类。
分类示例:cover_propagation
类别:代表符号执行技术在探索过程中存在约束传播中断的问题(如与操作系统交互)。symbolic_memory
类别:涉及内存操作的样本(如内存索引和分配)。
下图为结果,可以看到在11个类别中,GPT-3.5在5个类别中表现优于符号执行工具。而Klee和Angr在另外3个类别中表现更好。
LLMs的优势:1.parallel_program
- GPT-3.5能够识别常见的并行编程模式,例如使用管道(pipe)进行进程间通信的代码。这种隐式数据流未被KLEE或Angr建模,因此它们无法处理此类样本。2.external_functions
、cover_propagation
:这些样本涉及系统调用、外部函数调用或源代码之外的约束(如有效IP地址字符串)。3.floating_point
: Klee不支持符号浮点运算(除非通过扩展),而LLM能更好处理。
符号执行的优势:1.精确约束处理:在如“栈溢出”示例中,输入需满足严格约束(如字符串长度为9,末尾为特定字符\x01)。符号执行工具能够精确生成满足这些条件的字符串,完成目标覆盖。而LLMs虽然理解了需要生成长字符串引发溢出的要求,但无法精确控制长度或生成特定字符,导致失败。2.integer_overflow
, symbolic_jumps
- 需要精确计算,LLM表现不佳。
作者随后通过输入和输出的特性对样本进行分类,重点关注非基本类型(如浮点数和字符串)以及聚合类型(如向量)的样本,因为这些类型使用广泛且更具挑战性。以下是主要发现:
-
1.性能波动:
-
相较于GPT-3.5和CodeLlama表现的稳定性,Klee、StarCoder2和CodeQwen在不同输入/输出类型的样本上表现波动更大。
-
除了Klee在浮点数处理上的明显性能下降外,聚合类型的处理表现也有显著差异。
-
-
2.聚合类型的差异:
-
Klee:在处理聚合输入和聚合输出时表现明显下降,但对于仅有聚合输入而无聚合输出的样本,性能没有显著下降。
-
LLMs(如GPT-3.5和CodeLlama):表现与Klee相反,聚合输入和聚合输出的样本表现更好,但在聚合类型转换为简单类型(如统计操作:求和、选择数据集等)时,表现不佳。
-
-
3.原因推测:
-
聚合类型的转换涉及大量复杂约束和逻辑控制流(如向量排序),这对符号执行技术构成巨大挑战,特别是在提前声明符号变量时难以确定聚合输入的成员数量。
-
与之相比,LLMs通过生成更高抽象层次的代码,避免了底层复杂逻辑的限制。
-
-
4.符号执行工具的优势:
-
对于非聚合类型的输入,Klee表现卓越(达82.80%),展示了符号执行技术在理想条件下的高效性。
-
但在实际复杂场景中,其局限性显著,尤其在面对聚合类型的复杂转换时表现受限。
-
下图为一个示例
3.RQ3: Integrating LLMs with Traditional Tools for Better Performance
作者基于前面的insight实现了LLMsym工具,workflow如下:图中间directed input部分为最终生成的input,即输出。
- 1.首先使用GPT-3.5生成候选解决方案(即directed input)
-
2.具体执行逐一验证这些候选方案的正确性
-
如果候选方案通过验证(图中绿线),则直接返回作为最终解决方案,流程结束
-
如果所有候选方案都未通过,则进入下一步
-
-
3.将验证过程的反馈(如LLMs生成输入的实际输出)返回给LLMs,用于进一步改进解决方案
- 如果仍未生成合适的解决方案,则识别为LLMs不适合解决的问题(如需要精确计算的问题)
-
4.此时用符号执行工具进行测试用例生成。
实验结果如下,可以看到LLMsym相比Baseline有所提升。