学习驱动的复杂软件符号执行

原文来自微信公众号“编程语言Lab”: 学习驱动的复杂软件符号执行
搜索关注“编程语言Lab”公众号(HW-PLLab)获取编程语言更多技术内容!
欢迎加入编程语言社区 SIG-编程语言测试,了解更多 编程语言测试相关的技术内容。
加入方式:添加文末小助手微信,备注“加入 SIG-编程语言测试”。

作者简介

卜磊,南京大学计算机科学与技术系教授,博士生导师。研究领域是软件工程与形式化方法,包括模型检验技术,实时混成系统,信息物理融合系统等。

个人主页:https://cs.nju.edu.cn/bulei/

视频回顾

SIG-编程语言测试技术沙龙回顾|学习驱动的复杂软件符号执行

研究背景

软件深入我们生活的方方面面,在软件定义一切的时代,软件的正确运行是非常重要的。软件测试是保障软件正确运行的一种有效手段。而测试用例生成是影响软件测试效率的关键因素。那么,我们应该怎样去做测试用例生成呢?

测试用例自动生成技术基本分为以下两类:

随机测试技术

包括适应性随机测试,模糊测试等等。给定一段代码,我们“随机乱来”,“大力出奇迹”,看测试结果如何。这种方式生成的测试用例基本上有两个特点,一是覆盖度较低,这是因为很多用例都走了相同的主干流程,程序中的 Corner Case 没有被执行到;此外,会有大量的冗余测试用例

基于符号执行的测试技术

基于符号执行的测试技术,当前学术界经常在提,但还未在工业界完全落地。符号执行的基本概念是,我不知道哪个具体输入可以往下执行,但是我知道代码里面有这样一条路径还没有走到,那么我们会尝试将这条路径中的约束符号化抽取出来,通过对约束进行求解,从而生成能执行这条路径的具体用例。

鉴于每个路径生成出来的用例都不一样,所以相对来讲,这种方式自动化程度较高,覆盖度也很高,也解决了用例冗余的问题。

符号执行

符号执行具体怎么做呢,它会使用符号化的变量去代替一个具体的输入去执行程序。与随机测试生成不同的是,符号执行不会给变量一个具体的值,而是让变量带着约束往下执行(比如,随机测试会给定 ,然后往下走,而符号化是 往左走, 往右走之类)。

符号执行会时刻维护每个分支路径下的路径约束,然后调用求解器对路径约束求解以生成测试用例。

代表工具

包括大名鼎鼎的 KLEE(面向 C 代码),SPF(面向 Java 代码),Pex(后续集成到了 Visual Studio)等,都是比较有代表性的符号执行的工具。

示例

我们看如下示例。初始时 result = 0,如果满足 x > y 那么 result = x - y,否则执行 y - x,这是两个简单的分支。然后再执行一个 assert(result > 0)。

static void example(int x, int y) {
  int result = 0;
  if (x> y)
    result = x - y;
  else
    result = y - x;
  assert(result> 0);
}

假设我们对上述示例代码做测试,符号执行是将对应的 CFG(控制流图)画出来(如下图示),自动地生成代码中的约束。基于 CFG 分析,要使得测试用例一定走到某个路径分支下,我们的约束应该维持什么样的形式。

比如,要走到最左边这个分支,路径需要满足约束是:

当我们有了对应约束后,可以丢给 SMT 等求解器去求解。解出来的就是一个可走该路径的测试用例。解不出来,则表示这条路径不可达。

然而,当我们将符号执行应用到真实的大系统中时,会面临几个主要的挑战:

挑战 1:复杂数值计算

传统的符号执行会先收集完所有的路径约束后,再去做求解,因此非常依赖于底层求解器的能力。我们来看下面的示例,double a = x - y * 2,double b = x * y / (x + y),double c = sin(x) + cos(sqrt(y)) 都是在数值计算中十分常见的,尤其像我们组一直合作的航天、列控等领域中存在大量的类似代码,比如会有角速度等计算,用现有的 SMT 去求解是不可能的。在这种情况下,传统的符号执行基本上会无法求解。

static void example(double x, double y) {
  double a = x - y * 2;
  double b = x * y / (x + y);
  double c = sin(x) + cos(sqrt(y));
  if(a > y) assert(false);
  if(b < x) assert(false);
  if(c > 0) assert(false);
}
挑战 2:黑和函数调用

符号执行另外一个挑战就是函数的调用使得我们难以获取函数内部的实现细节,特别是大规模的系统中,背后一定会调用很多库函数或者第三方的包。比如说下面这段代码,有一个函数调用 Long.numberOfLeadingZeros(x),传了 x 做参数。这时,我们需要生成一个测试用例,可以保证走下去这个函数调用,这对符号执行是不可能的事情。如果这个函数的实现我们不知道,这种情况下要对其白盒化符号执行是无法完成的。

static void example(long x, double y) {
  double z = Long.numberOfLeadingZeros(x);
  if(z> x) assert(false);
}

相关工作

接下来简单介绍一下业界有关解决上述挑战问题的相关工作。

基于 SMT 约束求解器的符号执行工具

代表工作是 Pex [1],底层基于 Z3 [2] 实现,可以处理线性的约束,但对非线性约束支持有限。

基于简化的约束求解技术的符号执行工具

代表工作有 jCUTE [3],SPF-Mixed [4] 等,这些工具可以简化约束做抽象,但降低了复杂约束求解的可能性,效果也不是特别好。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值