作者 | 王祺昌 华东师范大学软件工程学院硕士研究生
苏亭 华东师范大学软件工程学院教授
版块 | 鉴源论坛 · 观模
引言:测试用例自动生成,简称测试生成(Test Generation),是指针对给定的被测对象,例如代码单元、接口、系统等,使用相关算法生成测试用例集合的方法。其本质是测试用例设计自动化,无需开发者手动设计测试用例。测试生成可分为黑盒和白盒,前者在不考虑程序本身的情况下为程序生成测试用例,而后者分析程序的源代码或二进制代码以生成测试用例,基于符号执行的测试生成是一种典型的白盒测试生成方法。
符号执行(symbolic execution)是一种经典的程序分析技术,使用抽象的符号值(symbolic value)而不是精确的具体值(concrete value)作为程序输入,以此将程序变量的值表示为这些输入的符号表达式。在符号执行期间的任何一点,符号执行引擎可以获取到达该点的路径约束,并通过约束求解器(constraint solver)求解约束以得到可以到达该点的具体值。
下面的简单代码片段将给出一个例子,假设ERROR语句对应程序中的一个漏洞,我们使用符号执行判断是否有达到该语句的可能。符号执行会在给定的时间内,生成一组输入来尽可能多的探索所有的执行路径,程序的输入包括两个变量 x 和 y,因此符号执行会将它们都绑定到对应的符号值α和β,最终程序中的每个点都对应一组α和β组成的约束。
图 1 符号执行代码示例
符号执行结束后生成的计算树如图 2 所示,树中的每个节点代表程序中的一个条件语句,每个边代表一组非条件语句的执行,每条路径代表从程序开始到路径终点的一条执行路径,通过为每条路径求解对应的路径约束,就可以判断该路径是否可行,并为可行路径生成对应的程序输入。
ERROR 语句对应的路径约束为(2*β==α ^ α<=β+10),求解该约束可得到一个可行解(α=4 ^ β=2),则(x =4, y=2)就是到达 ERROR 语句对应的程序输入。
图 2 程序对应的计算树(computation tree)
符号执行的思想最早由 James C. King 在 1976 年发表的一篇论文[1]中提出,文中提出的“解析程序的路径后,用符号模拟通过路径并获得输出”的方法如今被称为“经典符号执行”。
虽然符号执行技术最早在 70 年代就被提出,但未受到研究者的广泛关注,直到 21 世纪才重新回到人们的视野中,这主要受两个原因的限制。首先,符号执行在大型现实世界程序中的应用需要求解复杂而庞大的约束,而当时的约束求解器的求解能力限制了符号执行技术推广,在过去十年中,涌现了了许多强大的约束求解器如 Z3 (de Moura and Bjørner, 2008)[2], Yices (Dutertre and de Moura, 2006)[3], STP (Ganesh and Dill, 2007)[4]。其次,老一代计算机的计算能力有限,无法符号地执行大型程序,而今天的计算机比八十年代强大得多,这减少了符号执行应用于大型真实世界程序的障碍。
2006年,Cristian Cadar 设计了一种“先进行符号执行,后根据符号执行结果生成测试用例”的“执行生成测试”技术[5],并随后将其发展为应用在GNU/Linux 内核错误检查中的 KLEE[6]。
2007年,Koushik Sen 提出将符号执行和实际执行结合的混合执行(Concolic Execution)[7]。
2009 年,Vitaly Chipounov 提出“选择性符号执行”,通过选择 “对程序设计者有意义”的执行分支进行符号执行测试来提高对大型程序应用符号执行测试的可行性。
如今符号执行已经被广泛用于测试领域,其中最著名的用途是进行测试生成以提高代码覆盖率并发现程序错误,此外还被用于安全漏洞自动生成、负载测试、故障定位和回归测试等。
3.1 混合执行(Concolic Execution)
混合执行(Concolic Execution)已成为一种流行的符号执行方法,又称为动态符号执行(Dynamic Symbolic Execution)或动态测试生成(Dynamic Test Generation) [8]。与经典符号执行不同,混合执行使用一个具体值作为输入驱动程序运行,沿途收集路径约束,当程序执行结束后通过对路径约束上的不同分支取反来生成新路径上的约束,交由约束求解器求解得到新的输入,重复上述策略以覆盖更多的路径。
通过这种输入迭代产生变种输入的方法,理论上所有可行的路径都可以被计算并分析。混合执行相较于经典符号执行的优势在于每次执行都是基于具体值的而非模拟符号值的执行,从而显著降低了符号执行的开销,使得符号执行技术有能力处理更大规模的现实世界程序。
3.2 符号反向执行(Symbolic Backward Execution)
符号反向执行(SBE)是符号执行的一种变体[9],它探索从程序中的特定目标点到程序的入口的路径,因此其分析方向和传统的正向符号执行相反。符号反向执行的主要目标是快速寻找可以到达程序中特定目标点(例如 assert 和 throw 语句)的测试用例,这对开发人员在对程序进行调试或回归测试时非常有用。
符号执行理论上可以对程序可能的执行路径进行详尽的探索,也因此在处理现实世界的程序时遇到了一些挑战:
(1) 路径爆炸
大多数符号执行方法不适用于处理大型程序:随着程序规模的扩大,程序中有意义的路径数量成指数级增长。许多程序中还存在无限循环(infinite-loop)或递归调用,这大大增加了路径条数,提高了符号执行的难度。
(2) 复杂约束
符号执行中的重要部分是对路径约束的求解,但现实中存在一些复杂约束使得约束求解器难以求解(例如非线性算术运算、第三方库函数等),这会显示符号执行系统可以探索的路径数量。
(3) 内存
符号执行引擎难以处理指针、数组等复杂对象,另外,由于符号执行根据内存地址分析变量及其变化,对于有内存地址别名的程序,符号执行引擎将难以区分不同别名,因此执行结果可能有偏差。
符号执行作为一个经典的程序分析技术,在 21 世纪受到了研究者的广泛重视,并为软件测试提供了一个在白盒情况下精准和详尽地测试程序的全新思路,近年来不断出现新的符号执行技术和相关工具,被广泛应用于测试生成、负载测试、故障定位以及回归测试等场景。尽管取得了巨大进展,但符号执行仍然面临现实世界大型程序中存在的许多挑战,学术界和工业界也在不断探索符号执行和其他技术相结合以提升执行性能的方式,例如将符号执行与模糊测试相结合以提升测试生成的精确性和可扩展性。
参考文献:
[1] J. C. King, “Symbolic execution and program testing,” Commun. ACM, vol. 19, no. 7, pp. 385–394, 1976.
[2] de Moura, L. M., Bjørner, N., 2008. Z3: An efficient SMT solver. In: Proc. of the 14th International Conference on Tools and Algorithms for the Construction and Analysis of Systems (TACAS’08), pp. 337–340.
[3] Dutertre B, De Moura L. The yices smt solver[J]. Tool paper at http://yices. csl. sri. com/tool-paper. pdf, 2006, 2(2): 1-2.
[4] Ganesh, V., Dill, D.L. (2007). A Decision Procedure for Bit-Vectors and Arrays. In: Damm, W., Hermanns, H. (eds) Computer Aided Verification. CAV 2007. Lecture Notes in Computer Science, vol 4590. Springer, Berlin, Heidelberg.
https://doi.org/10.1007/978-3-540-73368-3_52.
[5] Cristian Cadar, Vijay Ganesh, Peter M. Pawlowski, David L. Dill, Dawson R. Engler: EXE: automatically generating inputs of death. CCS 2006: 322-335.
[6] Cristian Cadar, Daniel Dunbar, Dawson R. Engler: KLEE: Unassisted and Automatic Generation of High-Coverage Tests for Complex Systems Programs. OSDI 2008: 209-224.
[7] Sen K. Concolic testing[C]//Proceedings of the twenty-second IEEE/ACM international conference on Automated software engineering. 2007: 571-572.
[8] Patrice Godefroid, Nils Klarlund, and Koushik Sen. 2005. DART: Directed Automated Random Testing. In Proc. ACM SIGPLAN Conf. on Prog. Lang. Design and Impl. (PLDI’05). 213–223. https://doi.org/10.1145/1065010.1065036.
[9] Peter Dinges and Gul Agha. 2014. Targeted Test Input Generation Using Symbolicconcrete Backward Execution. In Proc. 29th ACM/IEEE Int. Conf. on Automated Software Engineering (ASE’14). 31–36. https://doi.org/10.1145/2642937. 2642951.