KLEE入门教程

本文1.首先介绍创建KLEE Docker容器的方法;2.然后介绍官网给出的一个指导实例;3.最后给出程序测试实例。

创建KLEE Docker容器

相关Dokcer的知识可以参见官网:https://docs.docker.com/get-started/

1.创建临时KLEE Docker容器

$ docker run --rm -ti --ulimit='stack=-1:-1' klee/klee:2.0

成功之后,shell提示符发生改变,变成klee用户,可以在容器中继续使用shell命令

$ whoami
$ klee --version
$ clang --version

通过这3个命令,可以查看到类似以下的信息:

最后,退出容器

$ exit

注意,因为我们创建画像的时候用了--rm参数,在我们退出后容器会销毁,因此是临时的。

2.创建永久容器

上一个例子简单的介绍了如何创建临时容器,接下来介绍如何创建永久容器,并用来做一些有意义的事。

首先,创建并进入容器:

$ docker run -ti --name=my_first_klee_container --ulimit='stack=-1:-1' klee/klee:2.0

这里我们没有用--rm参数,因此创建了永久容器。同时,这里用--name参数指定了容器的名字。--ulimit参数是为了防止堆栈溢出问题,这里不做深究,按照官网推荐书写。

接下来我们退出容器,检查容器是否依然存在

$ exit
$ docker ps -a

可以看到,容器依然是存在的,我们可以重新进入容器

$ docker start -ai my_first_klee_container

可以看到我们的容器依然存在。最后,如果想删除容器,可以用命令

$ docker rm my_first_klee_container

官方指导实例

官方教程网址:http://klee.github.io/tutorials/testing-function/

这个实例完整程序如下,在 examples/get_sign 目录下,用来判断一个整数的正,负,或者为0.

/*
 * First KLEE tutorial: testing a small function
 */

#include <klee/klee.h>

int get_sign(int x) {
  if (x == 0)
     return 0;
  
  if (x < 0)
     return -1;
  else 
     return 1;
} 

int main() {
  int a;
  klee_make_symbolic(&a, sizeof(a), "a");
  return get_sign(a);
} 

其中,klee_make_sybolic是KLEE自带的函数,用来产生符号化的输入。

因为KLEE是在LLVM字节码上进行工作,所以我们首先需要将.c编译为LLVM字节码。首先,我们进入到该文件目录(~/klee_src/examples/get_sign)下执行命令

clang -I ../../include -emit-llvm -c -g -O0 -Xclang -disable-O0-optnone get_sign.c

其中,参数-I是为了编译器找到头文件klee/klee.h,-g是为了在字节码文件中添加debug信息,还有后面的,具体不深究,按照官网推荐来。

同目录下我们会生成一个get-sign.bc的字节码文件,然后进行测试:

$ klee get_sign.bc

可以看到结果中KLEE给出了总指令数,完整路径和生成的测试案例数。

最后,我们看当前目录下多生成了两个文件:klee-last 和 klee-out-0。其中klee-out-0是本次测试结果,klee-last是最新测试结果,每次测试后覆盖。

程序测试实例

实例来源:https://blog.csdn.net/vincent_nkcs/article/details/85224491

源程序代码如下:

#include<stdio.h>
#include<stdlib.h>

void kleeTest(int a){
  int arr[10];
  int d[10];

  for (int i = 0; i < 10; i++){ //赋初始值
    arr[i] = i;
  }

  if (a < -50){  //求余分母为0
    for (int i = 0; i < 10; i++){
      int num = i;
      d[i] = arr[i] % num;
    }
  }
  else if(a < -25){  //除法分母为0
    for (int i = 0; i <= 10; i++){
      int num = i ;
      d[i] = arr[i] / num;
    }
  }
  else if (a < 0){  //数组越界
    for(int i = 0; i<= 11; i++){
      arr[i] = i;
    }
  }
  else if (a < 25){  //空指针
    int *a = NULL;
    int b = *a + 1;
  }
  else if(a < 50){  //内存泄漏
    free(arr);
  }
}

int main(){
  int n;
  klee_make_symbolic(&n, sizeof(n), "n");
  kleeTest(n);
  return 0;
}

首先,我们需要把实例拷贝到Docker容器中去,需要用到Docker cp命令

命令格式为:$ docker cp /host/path/target <containerId>:/file/path/within/container

详细可以参考:https://www.runoob.com/docker/docker-cp-command.html

以我为例:

$ docker cp /Users/lichao/Desktop/mytest.c my_first_klee_container:/home/klee/klee_src/examples/get_sign

/Users/lichao/Desktop/mytest.c 为我的实例程序路径

/home/klee/klee_src/examples/get_sign为目标路径

my_first_klee_container为容器名字

完成拷贝后,我们可以进入到容器中查看

$ docker start -ai my_first_klee_container
$ ls /home/klee/klee_src/examples/get_sign/

可以看到,mytest.c已经成功拷贝至容器目标路径下。

接下来,按照我们之前官方教程一样操作:1.进入到文件目录;2.clang编译为字节码;3.klee编译

$ cd /home/klee/klee_src/examples/get_sign/
$ clang -I ../../include -emit-llvm -c -g -O0 -Xclang -disable-O0-optnone mytest.c
$ klee mytest.bc 

可以看到结果,给出了错误类型及对应的源程序位置以及指令、路径、生成的用例数。

我们看下得到的测试文件

$ ls klee-last

可以看到,对应每个test主要有两种后缀文件。其中1.

.ktest文件是KLEE生成的测试用例,是二进制文件,可以用ktest-tool打开

$ ktest-tool klee-last/test000001.ktest

可以看到结果,对于第一个用例,给的输入是25。同理可以查看第二个和第三个测试用例,不再累述。

2. 其他后缀结尾的,包括.free,err、.div,err等,则是对应错误的相关信息,我们可以将他们复制到自己主机上进行查看。

命令格式:$ docker cp <containerId>:/file/path/within/container   /host/path/target

然后用文本格式打开,可以看到类似以下信息,包括错误的位置以及原因:

至此,KLEE的基本使用方法已经习得。

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值