klee与循环奇怪的行为与类似的代码

时间:2012-10-17 12:25:59

标签: llvm-gcc klee symbolic-computation

对于带符号参数的循环,我有一个关于如何使用KLEE(符号执行工具)的问题:

int loop(int data) {
  int i, result=0;
  for (i=0;i<data ;i++){   
     result+= 1;
     //printf("result%d\n", result); //-- With this line klee give different values for data
}
   return result;
}
void main() {
  int n;
  klee_make_symbolic(&n, sizeof(int),"n");  
  int result=loop(n) ;
}

如果我们用这段代码执行klee,它只给出一个测试用例。 但是,如果我们取出printf(...)的注释,klee将需要某种类型的控制来停止执行,因为它将产生n的值: --max-depth = 200

我想理解为什么克莱有这种不同的行为,这对我没有意义。为什么如果我在这段代码中没有printf,它将不会产生相同的值。

我发现在使用选项--optimize时会发生这种情况 什么时候不存在相同的行为。有人知道Klee的工作是什么?

另一个关于同样的问题是,如果在论文中他们已经发表了,据我所知他们说他们的启发式搜索将使它不是无限的(他们使用避免饥饿)  因为我有它运行不会停止,在这个循环的情况下应该完成klee执行是真的吗?

提前致谢

1 个答案:

答案 0 :(得分:0)

  

我想知道的是为什么这个选项有不同的行为   --optimize。感谢

在C / C ++中有“未定义行为”的概念(参见:C和C ++中未定义行为指南,Part1Part 2Part 3,每个C程序员都是什么应该了解未定义的行为[#1/3][#2/3][#3/3])。

signed整数的溢出被定义为未定义的行为,以便允许编译器优化这样的东西:

bool f(int x){ return x+1>x ? true : false ; }

让我们认为...正常代数中的x + 1> x总是正确的,在这个模代数中它几乎总是正确的(除了一个溢出的情况),所以让我们把它变成:

true

这种做法可以实现大量的优化。 (顺便说一句。如果你想在溢出时定义行为,使用unsigned整数 - 这个特性在加密算法实现中被广泛使用。)

另一方面,有时会导致惊人的结果, 喜欢这段代码:

int main(){
    int s=1, i=0;
    while (s>0) {
        ++i;
        s=2*s;
    }
    return i;
} 

优化为无限循环。这不是错误!这是强大的功能! (再次..对于已定义的行为,请使用unsigned)。

让我们为上面的例子生成汇编代码:

$ g++ -O1 -S -o overflow_loop-O1.s overflow_loop.cpp
$ g++ -O2 -S -o overflow_loop-O2.s overflow_loop.cpp

循环部分的检查编译方式不同:

overflow_loop-O1.s:

(...)
.L2:
    addl    $1, %eax
    cmpl    $31, %eax
    jne    .L2
(...)

overflow_loop-O2.s:

(...)
.L2:
    jmp    .L2
(...)

我建议您检查代码在不同优化级别上的汇编(gcc -S -O0 vs gcc -S -O1 ... -O3)。 关于主题的好帖子: [1][2][3][4][5][6]