假设我们有以下 C 代码:
int my_main(int x){
if (x > 5){
x++;
if (x > 8){
x++;
if (x < 15){
//@(x >= 9 && x <= 14);
}
}
}
return 0;
}
我想在初始化时使用静态分析计算变量 x 的边界,从而得到满足的谓词。在这个例子中,main 开头的 x 的间隔应该是 [8, 12]。
TL;DR:给定代码中某处的断言,计算这些范围的最佳方法是什么?
到目前为止我尝试了什么:
我认为解决这个问题的最佳方法是使用最弱的前提条件计算器。我尝试过使用 frama-c 的 wp 插件,但由于它是为验证目的而构建的,我不确定它在这个用例中有多有用。在以下代码上应用插件时:
int main(void){
int n = 0;
int x;
if (x > 5){
x++;
if (x > 8){
x++;
if (x < 15){
n = x;
}
}
}
//@ assert p: n >= 9 && n <= 14;
return 0;
}
我收到以下谓词发送到 alt-ergo 求解器:
goal main_assert_p:
forall i_1,i : int.
is_sint32(i) ->
is_sint32(i_1) ->
(((i < 6) -> (0 = i_1)) and
(**(6 <= i)** ->
(((i < 8) -> (0 = i_1)) and
(**(8 <= i)** ->
(((12 < i) -> (0 = i_1)) and (**(i <= 12)** -> (i_1 = (2 + i)))))))) ->
((9 <= i_1) and (i_1 <= 14))
如果您仔细观察,可以通过遵循变量 i 上不会导致 (i_1 = 0) 的边界来识别输入所需的间隔。我已经标记了这些界限。这不是很健壮,例如,如果断言更改为&& n <= 13,则隐含谓词的“左侧”保持不变,因为没有任何条件改变。另外我不确定这在其他情况下有多大用处,例如在调用函数时将我的断言更改为 requires 语句,我无法理解生成的谓词:
if (x < 15){
sum(x);
}
并向函数添加 requires 语句:
//@requires (n >= 6 && n <= 11);
int sum(int n){
我得到:
goal main_call_sum_pre:
forall i : int.
(6 <= i) ->
(8 <= i) ->
(i <= 12) ->
is_sint32(i) ->
is_sint32(1 + i) ->
is_sint32(2 + i) ->
((4 <= i) and (i <= 9))