实验二:klee处理未建模函数和处理error的方式

时间:2021-05-01 23:29:21

首先,能够分析klee源码固然重要。但是目前尚未到那个地步。我按照我的过程,记录和分析我所做的实验。

结论性内容是:

1、klee处理printf传入符号值的情形时,报为error,不会将符号值具体化以后再调用printf进行具体执行。

2、klee处理error的时候,如果多条路径覆盖该error,则只报一次该error,并且只生成一个测试用例。

3、klee符号执行时的posix-runtime选项为命令行建模提供支持,uclibc则对atoi等c标准库的函数进行建模。

(如果使用了Posix-rutime选项,但是没有对命令行建模(--sym-args和--sym-arg选项),则会报错;如果没有使用uclibc选项,那么klee符号执行过程会提示atoi等函数是external,无法处理。)

 

示例代码1:

#include <stdio.h>
#include
<string.h>
#include
<stdlib.h>
#include
<assert.h>
#include
<klee/klee.h>
int main(int argc, char* argv[]) {
int result = argc > 1 ? atoi(argv[1]) : 0;
printf(
"result:%d\n",result);
if (result == 42)
{
printf(
"yes\n");
klee_assert(
0);
}
// printf("result:%d\n",result);
}

示例代码2:

#include <stdio.h>
#include
<string.h>
#include
<stdlib.h>
#include
<assert.h>
#include
<klee/klee.h>
int main(int argc, char* argv[]) {
int result = argc > 1 ? atoi(argv[1]) : 0;
//printf("result:%d\n",result);
if (result == 42)
{
printf(
"yes\n");
klee_assert(
0);
}
printf(
"result:%d\n",result);
}

运行脚本:

clang -I /home/klee/xiaojiework/klee/include/ -emit-llvm -c -g test2.c
klee
--optimize --libc=uclibc --posix-runtime test2.bc --sym-args 0 1 3

运行结果:

修改前

klee@ubuntu:~/kleeexperiment/modeltest$ clang -I /home/klee/xiaojiework/klee/include/ -emit-llvm -c -g test2.c
klee@ubuntu:~/kleeexperiment/modeltest$ klee --optimize --libc=uclibc --posix-runtime test2.bc --sym-args 0 1 3
KLEE: NOTE: Using klee-uclibc : /usr/local/lib/x86_64-linux-gnu/klee/runtime/klee-uclibc.bca
KLEE: NOTE: Using model: /usr/local/lib/x86_64-linux-gnu/klee/runtime/libkleeRuntimePOSIX.bca
KLEE: output directory is "/home/klee/kleeexperiment/modeltest/klee-out-12"
KLEE: Using STP solver backend
KLEE: WARNING: undefined reference to function: puts
KLEE: WARNING ONCE: calling external: syscall(16, 0, 21505, 62967648)
KLEE: WARNING ONCE: Alignment of memory from call "malloc" is not modelled. Using alignment of 8.
KLEE: WARNING ONCE: calling external: printf(51779744, 0)
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:0
KLEE: ERROR: /home/klee/kleeexperiment/modeltest/test2.c:19: external call with symbolic argument: printf
KLEE: NOTE: now ignoring this error at this location
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:-9
result:-9
result:0
result:0
result:-9
result:0
result:9
result:-99
result:9
result:9
result:9
result:9
result:9
result:9
result:9
result:99
result:99
result:99
result:99
result:999

KLEE: done: total instructions = 6238
KLEE: done: completed paths = 68
KLEE: done: generated tests = 36

修改后:

klee@ubuntu:~/kleeexperiment/modeltest$ clang -I /home/klee/xiaojiework/klee/include/ -emit-llvm -c -g test2.c
klee@ubuntu:~/kleeexperiment/modeltest$ klee --optimize --libc=uclibc --posix-runtime test2.bc --sym-args 0 1 3
KLEE: NOTE: Using klee-uclibc : /usr/local/lib/x86_64-linux-gnu/klee/runtime/klee-uclibc.bca
KLEE: NOTE: Using model: /usr/local/lib/x86_64-linux-gnu/klee/runtime/libkleeRuntimePOSIX.bca
KLEE: output directory is "/home/klee/kleeexperiment/modeltest/klee-out-11"
KLEE: Using STP solver backend
KLEE: WARNING: undefined reference to function: puts
KLEE: WARNING ONCE: calling external: syscall(16, 0, 21505, 58818256)
KLEE: WARNING ONCE: Alignment of memory from call "malloc" is not modelled. Using alignment of 8.
KLEE: WARNING ONCE: calling external: printf(55045792, 0)
result:0
result:0
result:0
result:0
result:0
result:0
result:0
KLEE: ERROR: /home/klee/kleeexperiment/modeltest/test2.c:25: external call with symbolic argument: printf
KLEE: NOTE: now ignoring this error at this location
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:0
KLEE: WARNING ONCE: calling external: puts(60211056)
yes
KLEE: ERROR: /home/klee/kleeexperiment/modeltest/test2.c:23: ASSERTION FAIL: 0
KLEE: NOTE: now ignoring this error at this location
result:9
result:0
result:-9
result:9
result:0
result:9
result:0
result:99
result:9
result:-9
result:9
yes
yes
yes
result:9
result:9
result:-9
result:9
result:-99
yes
result:99
result:99
result:99
result:999

KLEE: done: total instructions = 6381
KLEE: done: completed paths = 73
KLEE: done: generated tests = 37

可以看到结果不一样:后面比前面多生成了一个测试用例(36到37),并且新增汇报了一个error,就是assertion。为什么会多一个呢?因为:

printf放在了if(result==42)的前面。当符号值传递给printf的时候,klee可能有两种做法,一种是放弃分析,另外一种是将符号值具体化(目前我的猜测,继续往下看)。

如果是放弃分析,klee就不可能沿着该路径往后面走了;

如果是符号值具体化,klee也不可能考虑到后续有if(result==42)的语句,而故意将其具体化为42。因此,无论如何就无法检测到klee_assert的错误,即klee都不可能再对符号result添加==42的约束,从而跳入到后面的if语句中,继续符号执行。

 

针对该现象继续进行解释:即klee处理printf传入符号值的时候,是符号值具体化呢,还是放弃分析呢?我们就结合这个实例来探讨一下:

一,修改前和修改后的测试用例情况和路径覆盖情况对比:(说明针对一个error只产生一个测试用例

KLEE: done: completed paths = 68

KLEE: done: generated tests = 36

KLEE: done: completed paths = 73

KLEE: done: generated tests = 37

此外,在符号执行过程中,输出了五次yes,说明有五条路径覆盖了if(result==42)。printf(“yes”)的参数不是符号值,因此可以直接执行。

现象说明:将printf从if(result==42)之前移动到之后以后,原有的68条路径中,有五条路径的约束还能够满足result==42,因此新增了五条路径(从68变为73)。

但是为什么测试用例只增加了一个呢?这是因为,五条路径都报错的话,klee只汇报一次error,同时只从产生一个覆盖该error的测试用例。

你若不信的话,我预言,你把klee_assert去掉,那么路径肯定是从68增加到73,然后测试用例也增加五个。我们试一试。

验证代码是:

修改前:

int main(int argc, char* argv[]) {

    int result = argc > 1 ? atoi(argv[1]) : 0;

    printf("result:%d\n",result);

    if (result == 42)

    {

       printf("yes\n");

       //klee_assert(0);

    }

    //printf("result:%d\n",result);

}

 

修改后:

int main(int argc, char* argv[]) {

    int result = argc > 1 ? atoi(argv[1]) : 0;

    //printf("result:%d\n",result);

    if (result == 42)

    {

       printf("yes\n");

       //klee_assert(0);

    }

    printf("result:%d\n",result);

}

输出信息:

  • 修改前:

klee@ubuntu:~/kleeexperiment/modeltest$ clang -I /home/klee/xiaojiework/klee/include/ -emit-llvm -c -g test2.c

klee@ubuntu:~/kleeexperiment/modeltest$ klee --libc=uclibc --posix-runtime test2.bc --sym-args 0 1 3

KLEE: NOTE: Using klee-uclibc : /usr/local/lib/x86_64-linux-gnu/klee/runtime/klee-uclibc.bca

KLEE: NOTE: Using model: /usr/local/lib/x86_64-linux-gnu/klee/runtime/libkleeRuntimePOSIX.bca

KLEE: output directory is "/home/klee/kleeexperiment/modeltest/klee-out-17"

KLEE: Using STP solver backend

KLEE: WARNING ONCE: calling external: syscall(16, 0, 21505, 47881104)

KLEE: WARNING ONCE: calling __user_main with extra arguments.

KLEE: WARNING ONCE: Alignment of memory from call "malloc" is not modelled. Using alignment of 8.

KLEE: WARNING ONCE: calling external: printf(65297728, 0)

result:0

result:0

result:0

result:0

result:0

result:0

result:0

result:0

KLEE: ERROR: /home/klee/kleeexperiment/modeltest/test2.c:19: external call with symbolic argument: printf

KLEE: NOTE: now ignoring this error at this location

result:0

result:0

result:0

result:0

result:0

result:0

result:0

result:0

result:-9

result:-9

result:0

result:0

result:-9

result:9

result:9

result:9

result:9

result:-99

result:9

result:9

result:9

result:99

result:9

result:99

result:99

result:99

result:999

 

KLEE: done: total instructions = 11514

KLEE: done: completed paths = 68

KLEE: done: generated tests = 36

 

  • 修改后:

klee@ubuntu:~/kleeexperiment/modeltest$ clang -I /home/klee/xiaojiework/klee/include/ -emit-llvm -c -g test2.c

klee@ubuntu:~/kleeexperiment/modeltest$ klee --libc=uclibc --posix-runtime test2.bc --sym-args 0 1 3

KLEE: NOTE: Using klee-uclibc : /usr/local/lib/x86_64-linux-gnu/klee/runtime/klee-uclibc.bca

KLEE: NOTE: Using model: /usr/local/lib/x86_64-linux-gnu/klee/runtime/libkleeRuntimePOSIX.bca

KLEE: output directory is "/home/klee/kleeexperiment/modeltest/klee-out-16"

KLEE: Using STP solver backend

KLEE: WARNING ONCE: calling external: syscall(16, 0, 21505, 31737040)

KLEE: WARNING ONCE: calling __user_main with extra arguments.

KLEE: WARNING ONCE: Alignment of memory from call "malloc" is not modelled. Using alignment of 8.

KLEE: WARNING ONCE: calling external: printf(44028960, 0)

result:0

result:0

result:0

result:0

result:0

KLEE: ERROR: /home/klee/kleeexperiment/modeltest/test2.c:25: external call with symbolic argument: printf

KLEE: NOTE: now ignoring this error at this location

result:0

result:0

result:0

result:0

result:0

result:0

result:0

result:0

result:0

result:-9

result:0

result:0

result:0

result:9

result:-9

result:0

result:9

result:-99

result:-9

result:9

result:9

yes

result:42

result:9

yes

result:42

result:9

result:9

yes

result:42

result:9

result:99

result:99

yes

result:42

result:99

yes

result:42

result:99

result:999

 

KLEE: done: total instructions = 11648

KLEE: done: completed paths = 73

KLEE: done: generated tests = 41

验证了我的推断。

问题: 为什么路径数目和测试用例不一致呢?可能是由于求解器的原因,一些路径的约束无法计算出来(继续下面看,会发现该程序不是因为这个原因。

二、为什么路径数目和测试用例不一致呢?klee处理printf传入符号值,究竟是具体化符号值,还是直接放弃分析,报出error?

  • 进一步修改程序:
int main(int argc, char* argv[]) {
int result = argc > 1 ? atoi(argv[1]) : 0;
//printf("result:%d\n",result);
if (result == 42)
{
printf(
"yes,");//修改的地方是,加了一个逗号。
klee_assert(0);
}
printf(
"result:%d\n",result);
}
  • klee符号执行结果:
klee@ubuntu:~/kleeexperiment/modeltest$ clang -I /home/klee/xiaojiework/klee/include/ -emit-llvm -c -g test2.c
klee@ubuntu:~/kleeexperiment/modeltest$ klee --libc=uclibc --posix-runtime test2.bc --sym-args 0 1 3
KLEE: NOTE: Using klee-uclibc : /usr/local/lib/x86_64-linux-gnu/klee/runtime/klee-uclibc.bca
KLEE: NOTE: Using model: /usr/local/lib/x86_64-linux-gnu/klee/runtime/libkleeRuntimePOSIX.bca
KLEE: output directory is "/home/klee/kleeexperiment/modeltest/klee-out-18"
KLEE: Using STP solver backend
KLEE: WARNING ONCE: calling external: syscall(16, 0, 21505, 75566416)
KLEE: WARNING ONCE: calling __user_main with extra arguments.
KLEE: WARNING ONCE: Alignment of memory from call "malloc" is not modelled. Using alignment of 8.
KLEE: WARNING ONCE: calling external: printf(66063584, 0)
result:0
result:0
result:0
result:0
result:0
KLEE: ERROR: /home/klee/kleeexperiment/modeltest/test2.c:25: external call with symbolic argument: printf
KLEE: NOTE: now ignoring this error at this location
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:-9
result:0
result:9
result:0
result:-9
result:9
result:-99
result:-9
result:9
KLEE: ERROR: /home/klee/kleeexperiment/modeltest/test2.c:23: ASSERTION FAIL: 0
KLEE: NOTE: now ignoring this error at this location
yes,result:9
result:9
result:9
yes,yes,result:9
result:9
result:99
result:99
yes,yes,result:99
result:99
result:999

KLEE: done: total instructions = 11623
KLEE: done: completed paths = 73
KLEE: done: generated tests = 37

首先,输出的信息都是符号执行的过程中输出的。产生的测试用例都是符号执行结束以后产生的。根据我前面的论证,当覆盖同一个错误的时候,只报一次error,只产生一个测试用例。所以,五个yes,也就说明实际能够产生测试用例的路径是41条。那么也就说明,符号执行过程中应该输出41组值。我们数一下。一共是40组值(注意,yes,单独算一组)。为什么???

这个时候我进一步摸索和修改(也是误打误撞出来)

版本一程序 版本二程序 版本三程序
int main(int argc, char* argv[]) {
int result = argc > 1 ? atoi(argv[1]) : 0;
//printf("result:%d\n",result);
if (result == 42)
{
printf(
"yes,");
//klee_assert(0);
}
//printf("result:%d\n",result);
}

 

int main(int argc, char* argv[]) {
int result = argc > 1 ? atoi(argv[1]) : 0;
//printf("result:%d\n",result);
if (result == 42)
{
printf(
"yes,");
klee_assert(
0);
}
//printf("result:%d\n",result);
}

 

int main(int argc, char* argv[]) {
int result = argc > 1 ? atoi(argv[1]) : 0;
printf(
"result:%d\n",result);
if (result == 42)
{
printf(
"yes,");
//klee_assert(0);
}
//printf("result:%d\n",result);
}

 

输出结果:

 版本一  版本二
实验二:klee处理未建模函数和处理error的方式

 

实验二:klee处理未建模函数和处理error的方式

 

 

 版本三:

klee@ubuntu:~/kleeexperiment/modeltest$ clang -I /home/klee/xiaojiework/klee/include/ -emit-llvm -c -g test2.c
klee@ubuntu:~/kleeexperiment/modeltest$ klee --libc=uclibc --posix-runtime test2.bc --sym-args 0 1 3
KLEE: NOTE: Using klee-uclibc : /usr/local/lib/x86_64-linux-gnu/klee/runtime/klee-uclibc.bca
KLEE: NOTE: Using model: /usr/local/lib/x86_64-linux-gnu/klee/runtime/libkleeRuntimePOSIX.bca
KLEE: output directory is "/home/klee/kleeexperiment/modeltest/klee-out-21"
KLEE: Using STP solver backend
KLEE: WARNING ONCE: calling external: syscall(16, 0, 21505, 60923696)
KLEE: WARNING ONCE: calling __user_main with extra arguments.
KLEE: WARNING ONCE: Alignment of memory from call "malloc" is not modelled. Using alignment of 8.
KLEE: WARNING ONCE: calling external: printf(76818928, 0)
result:0
result:0
result:0
result:0
result:0
result:0
result:0
KLEE: ERROR: /home/klee/kleeexperiment/modeltest/test2.c:19: external call with symbolic argument: printf
KLEE: NOTE: now ignoring this error at this location
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:0
result:-9
result:9
result:0
result:0
result:-9
result:9
result:9
result:9
result:9
result:99
result:-9
result:9
result:9
result:9
result:-99
result:99
result:99
result:99
result:999

KLEE: done: total instructions = 11514
KLEE: done: completed paths = 68
KLEE: done: generated tests = 36

比较一下版本一和版本二,可以看出,版本二由于assert原因,73条路径中五条覆盖assert,所以只生成69个测试用例,汇报一次error,error对应一个测试用例。

比较一下版本二和版本三,可以看出,版本三由于printf放在了if(result==42)的前面,无论是printf传入具体值,还是printf传入符号值(符号值具体化或者klee放弃分析,见前述分析),后续覆盖if(result==42)的路径都不会被满足和分析,没有yes被输出。同时,覆盖的路径条数为68。这也说明,if点前一共有68条路径,如果没有printf影响,其中只有五条的约束在添加(result==42)以后,还能够满足,从而覆盖到if(result==42)分支内部内容。

比较一下版本一和版本三,可以看出,符号执行过程中的输出为35组数据(数一数输出的result信息的个数),但是生成了36个测试用例,报了一个error,我们看了一下,error的具体内容是:

实验二:klee处理未建模函数和处理error的方式

同时配套生成了一个error的测试用例。但是符号执行过程中输出为35组数据,这也就说明了,klee处理printf传入符号值的时候,没有将其具体化以后再调用printf,而是直接报error,并且放弃分析。

这也说明了:版本三程序的一共68条路径中,有(68-35)条路径因为执行printf时传入符号值,均为error,但是只报出一个error,最后也只产生一个测试用例覆盖该条路径。这也是前述41个测试用例和40组输出数据的原因。

我们查看一下测试用例的内容和用该测试用例重新运行程序,可以看出,由于printf传入符号值的这种error类型,是klee自身不能处理的情况,而不是我们一般程序运行时的内存错误等,所以该测试用例下重新运行该程序,不会报错。result输出值为0。那为什么还要生成该测试用例呢,因为需要覆盖该error所在路径。

klee@ubuntu:~/kleeexperiment/modeltest$ export LD_LIBRARY_PATH=/home/klee/xiaojiework/klee-xiaojie/build/debug/lib/:$LD_LIBRARY_PATH
klee@ubuntu:~/kleeexperiment/modeltest$ gcc -L /home/klee/xiaojiework/klee-xiaojie/build/debug/lib/ test2.c -lkleeRuntest
klee@ubuntu:~/kleeexperiment/modeltest$ ktest-tool klee-last/test000010.ktestktest file : 'klee-last/test000010.ktest'
args : ['test2.bc', '--sym-args', '0', '1', '3']
num objects: 3
object 0: name: 'n_args'
object 0: size: 4
object 0: data: '\x01\x00\x00\x00'
object 1: name: 'arg0'
object 1: size: 4
object 1: data: '+0\x00\x00'
object 2: name: 'model_version'
object 2: size: 4
object 2: data: '\x01\x00\x00\x00'
klee@ubuntu:~/kleeexperiment/modeltest$ KTEST_FILE=klee-last/test000010.ktest ./a.out
result:0

结论性的东西往往不容易获得。分析完毕!