函数:klee_assume(condition)
用法:该函数中的condition利用symbolic variables构造一个约束,也就是一个判定,从而让该语句之后的程序只需要考虑该条件满足情况,所以该函数类似于if(condition){ remain statements after this condition}。我觉得从这一点来看,klee_assume有点类似于assert,用于确保当前节点下条件的满足性。
Interaction with short-circuit operators
short-circuit operator是指and 或者or连接符,因为当and左边为假,右边无需执行,or则是在左边为真的情况下右边无需执行,这样会产生在特定情况下跳过右边部分的情况,所以称为short-circuit operator。真的很形象。
当条件包含short-circuit operators,会导致klee_assume的结果不可预测,考虑如下的程序,在不同情况下输出可能不同:
int main()
{
int c,d;
klee_make_symbolic(&c, sizeof(c), "c");
klee_make_symbolic(&d, sizeof(d), "d");
klee_assume((c==2)||(d==3));
return 0;
}
编译,并且用klee分析如下(不太理解这里由short-circuit operator导致的问题在哪里?和下面改进相比,我觉得赶紧后根据a,b,c也会有三个不同的path,所以这里的path含义还是不够清楚。亦或是看似顺序的产生三个path是不合理的??):
$ llvm-gcc -c -emit-llvm p.c -o p.bc
$ klee p.bc
KLEE: output directory = "klee-out-0"
KLEE: ERROR: invalid klee_assume call (provably false)
KLEE: NOTE: now ignoring this error at this location
KLEE: done: total instructions = 53
KLEE: done: completed paths = 3
KLEE: done: generated tests = 3
为了消除上面结果中的error report,也就是保证只有 single path。当KLEE在上面找到三个path时,其中之一会出发一个error,原因在于compiler处理short-circuit operator的方式上。改进后如下:
int main()
{
int c,d;
klee_make_symbolic(&c, sizeof(c), "c");
klee_make_symbolic(&d, sizeof(d), "d");
int tmp;
if (c == 2) {
tmp = 1;
} else if (d == 3) {
tmp = 1;
} else {
tmp = 0;
}
klee_assume(tmp);
return 0;
}
Since the program contains 3 paths and all are feasible, klee_assume will be called 3 times with trivial arguments:
klee_assume(1);
klee_assume(1);
klee_assume(0);
所有condition中的这些问题都可以按照上面方法处理,消除error report,确保所有操作只有boolean值,并且没有副作用。