KLEE学习——实例2

该例子通过符号化输入字符串来完成正则式匹配,实例来源见官网:Testing a Simple Regular Expression Library


1.编译生成LLVM位码
源码所在目录为example/regexp,到该目录下执行:

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

参数含义在第一个例子中有描述,不再累述。


2.用KLEE执行代码
执行命令:

$ klee --only-output-states-covering-new Regexp.bc

得到结果如下:
在这里插入图片描述
可以看到KLEE一共执行了4848113条指令,探索了7438条路径并生成了15个测试用例。除此之外,还报了2个错误。
其中,命令参数 –only-output-states-covering-new 用来限制生成测试用例,只有在覆盖了新的代码时才生成。如果不使用这个参数,将会生成6677个测试用例,结果如下:
在这里插入图片描述

这里注意为什么没有和路径数量一样呢?****因为klee并不会为每一条路径生成测试用例,每当它发现一个bug,只为到达bug的第一次创建一个测试用例,在同一位置到达错误的其他路径都会终止。
如果想报告所有的错误,可以使用参数 –emit-all-errors,这个时候用例数和路径数就会一样了,结果会如下:
在这里插入图片描述
由于真实程序可能会更复杂,有无数的路径,导致klee无法终止(只能以Control-C方式)。klee提供了几个选项限制运行时间和内存使用。

-max-time=<time span>:Halt execution after the given amount of time, e.g. 10min or 1h5s.
-max-forks=N:Stop forking after N symbolic branches, and run the remaining paths to termination.
-max-memrory=N:Try to limit memory consumption to N megabytes.

这里,我们用设置分支数量进行示例:
在这里插入图片描述可以看到,路径只有101条了(1条主路径和100条分支)。


3.KLEE错误报告
当klee检测到正在执行的程序中的错误时,它将生成一个显示错误的测试用例,并将有关该错误的一些附加信息写入文件testN.TYPE.err,N是测试用例的标号,TYPE显示错误的类型,如图所示:
在这里插入图片描述
例如test0000010.ptr.err即表明这是用例10触发了指针错误。
其中,错误类型包括以下:

  • ptr: Stores or loads of invalid memory locations.
  • free: Double or invalid free().
  • abort: The program called abort().
  • assert: Anassertion failed.
  • div: A division or modulus by zero was detected.
  • user: There is a problem with the input (invalid klee intrinsic calls) or the way KLEE is being used.
  • exec: There was a problem which prevented KLEE from executing the program; for example an unknown instruction, a call to an invalid
    function pointer, or inline assembly.
  • model: KLEE was unable to keep full precision and is only exploring parts of the program state. For example, symbolic sizes to
    malloc are not currently supported, in such cases KLEE will concretize
    the argument.

我们可以直接打开错误文件查看里面的内容:
在这里插入图片描述
显示了错误发生的文件、源代码中的行、汇编文件中的行、回溯信息和额外信息。


4.改变测试工具
klee发现程序中2个内存错误不是因为正则函数中存在bug,二是我们的测试驱动中存在一个问题。我们将输入完全符号化了,但是匹配函数期待的是以null为结尾的字符串。
因此,最简单的方法是我们在这个buffer的结尾添加’\0’。于是,我们的main函数改成如下:

int main() {
  // The input regular expression.
  char re[SIZE];


  // Make the input symbolic.
  klee_make_symbolic(re, sizeof re, "re");
  re[SIZE - 1] = '\0';


  // Try to match against a constant string "hello".
  match(re, "hello");


  return 0;}

然后,我们重新编译并用klee运行:

$ clang -I …/…/include -emit-llvm -c -g -O0 -Xclang -disable-O0-optnone Regexp.c
$ klee --only-output-states-covering-new Regexp.bc

结果如下:
在这里插入图片描述
错误已经没有了。
这里主要介绍的是另外一种方式:klee_assumen内部函数。该函数只接受一个无符号整数的参数,通常是某种条件表达式,只要满足这个条件的路径才会被探索。

int main() {
  // The input regular expression.
  char re[SIZE];


  // Make the input symbolic.
  klee_make_symbolic(re, sizeof re, "re");
  klee_assume(re[SIZE - 1] == '\0');


  // Try to match against a constant string "hello".
  match(re, "hello");


  return 0;}

比较这两种方式:
第一种显示的方式强制最后一个字符为’\0’,这样不管klee生成的是什么都没关系,在一些需要手工检查测试用例的情况下,测试用例更方便地显示所有重要的值。
第二种方式能够用于编码更复杂的条件约束,比如说能用klee_assume(re[0]!=’^’)来使klee是搜索第一个字符不为’^'的路径。

注意,如果要使用多个条件的话,不要使用"&&“和”||"这样的布尔条件,尽可能使用简单表达式(例如将单个调用拆分为多个)。因为在编译之后可能会产生一些分支,具体原因以后再详述,现在先记着。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值