绕过CBMC检测到的无符号加法溢出

时间:2015-06-26 04:54:56

标签: c integer integer-overflow model-checking cbmc

CBMC在以下行中检测到可能的无符号加法溢出:

l = (t + *b)&(0xffffffffL);
c += (l < t);

我同意第一行有可能出现溢出,但我正在处理CBMC无法查看的下一行的进位。 如果有溢出,我设置进位1.所以,因为我知道这个,这就是我希望我的代码工作的方式,我想继续进行验证过程。 那么,我怎么告诉CBMC忽略这个错误并继续前进呢?

1 个答案:

答案 0 :(得分:3)

TL; DR 这取决于变量的实际类型。在所有情况下,CBMC都会检测到可能导致未定义行为的实际错误。这意味着,您应修复代码而不是在CBMC中禁用该消息。

完整答案:

通常:据我所知,CBMC不允许排除特定属性(另一方面,您只能使用--property标志检查一个特定属性)。如果您想要正式答复/意见或提出功能请求,我建议您在CProver Support group发帖。

(当然,可以使用__CPROVER_assume来使CBMC排除导致错误的痕迹,但这将是非常非常非常糟糕的想法 ,因为这可能使其他问题无法实现。)

变体1:我假设您的代码看起来像(与此相关:请,请发布自包含示例并准确解释问题所在,这很难猜这些东西)

long nondet_long(void);

void main(void) {
  long l = 0;
  int c = 0;
  long t = nondet_long();
  long s = nondet_long();
  long *b = &s;

  l = (t + *b) & (0xffffffffL); 
  c += (l < t);
}

你正在运行

    cbmc --signed-overflow-check test.c

给出类似下面的输出?

    CBMC version 5.1 64-bit x86_64 macos
    Parsing test.c
    Converting
    Type-checking test
    Generating GOTO Program
    Adding CPROVER library
    Function Pointer Removal
    Partial Inlining
    Generic Property Instrumentation
    Starting Bounded Model Checking
    size of program expression: 41 steps
    simple slicing removed 3 assignments
    Generated 2 VCC(s), 2 remaining after simplification
    Passing problem to propositional reduction
    converting SSA
    Running propositional reduction
    Post-processing
    Solving with MiniSAT 2.2.0 with simplifier
    792 variables, 2302 clauses
    SAT checker: negated claim is SATISFIABLE, i.e., does not hold
    Runtime decision procedure: 0.006s
    Building error trace

    Counterexample:

    State 17 file test.c line 4 function main thread 0
    ----------------------------------------------------
      l=0 (0000000000000000000000000000000000000000000000000000000000000000)

    State 18 file test.c line 4 function main thread 0
    ----------------------------------------------------
      l=0 (0000000000000000000000000000000000000000000000000000000000000000)

    State 19 file test.c line 5 function main thread 0
    ----------------------------------------------------
      c=0 (00000000000000000000000000000000)

    State 20 file test.c line 5 function main thread 0
    ----------------------------------------------------
      c=0 (00000000000000000000000000000000)

    State 21 file test.c line 6 function main thread 0
    ----------------------------------------------------
      t=0 (0000000000000000000000000000000000000000000000000000000000000000)

    State 22 file test.c line 6 function main thread 0
    ----------------------------------------------------
      t=-9223372036854775808 (1000000000000000000000000000000000000000000000000000000000000000)

    State 23 file test.c line 7 function main thread 0
    ----------------------------------------------------
      s=0 (0000000000000000000000000000000000000000000000000000000000000000)

    State 24 file test.c line 7 function main thread 0
    ----------------------------------------------------
      s=-9223372036854775807 (1000000000000000000000000000000000000000000000000000000000000001)

    State 25 file test.c line 8 function main thread 0
    ----------------------------------------------------
      b=((long int *)NULL) (0000000000000000000000000000000000000000000000000000000000000000)

    State 26 file test.c line 8 function main thread 0
    ----------------------------------------------------
      b=&s!0@1 (0000001000000000000000000000000000000000000000000000000000000000)

    Violated property:
      file test.c line 10 function main
      arithmetic overflow on signed + in t + *b
      !overflow("+", signed long int, t, *b)

    VERIFICATION FAILED

我认为您不应该禁用此属性检查,即使您可以。正如你所说的那样,原因是这个加法可能会溢出,并且,整数溢出在C 中是未定义的行为,或者this answer对问题How to check integer overflow in C?很好如上所述:

  

[O]你已经   执行x + y,如果它溢出,你已经被冲洗了。太晚了   做任何检查 - 你的程序可能已经崩溃了。考虑到   它就像检查除零一样 - 如果你等到之后   分裂已被执行检查,已经太晚了。

另请参阅Integer overflow and undefined behaviorHow disastrous is integer overflow in C++?

因此,这是一个实际的错误,CBMC有充分的理由告诉你。你实际应该做的是调整你的代码,以便没有潜在的溢出!上面提到的答案表明了(记得包括limits.h):

if ((*b > 0 && t > LONG_MAX - *b) 
    || (*b < 0 && LONG_MIN < *b && t < LONG_MIN - *b) 
    || (*b==LONG_MIN && t < 0))
{
    /* Overflow will occur, need to do maths in a more elaborate, but safe way! */
    /* ... */
}
else
{
    /* No overflow, addition is safe! */
    l = (t + *b) & (0xffffffffL);
    /* ... */
}

变体2:在此,我假设您的代码类似于:

unsigned int nondet_uint(void);

void main(void) {
  unsigned int l = 0;
  unsigned int c = 0;
  unsigned int t = nondet_uint();
  unsigned int s = nondet_uint();
  unsigned int *b = &s;

  l = (t + *b) & (0xffffffffL);
  c += (l < t);
}

你正在运行

    cbmc --unsigned-overflow-check  test.c

给出类似下面的输出?

CBMC version 5.1 64-bit x86_64 macos
Parsing test.c
Converting
Type-checking test
Generating GOTO Program
Adding CPROVER library
Function Pointer Removal
Partial Inlining
Generic Property Instrumentation
Starting Bounded Model Checking
size of program expression: 42 steps
simple slicing removed 3 assignments
Generated 3 VCC(s), 3 remaining after simplification
Passing problem to propositional reduction
converting SSA
Running propositional reduction
Post-processing
Solving with MiniSAT 2.2.0 with simplifier
519 variables, 1306 clauses
SAT checker: negated claim is SATISFIABLE, i.e., does not hold
Runtime decision procedure: 0.01s
Building error trace

Counterexample:

State 17 file test.c line 4 function main thread 0
----------------------------------------------------
  l=0 (00000000000000000000000000000000)

State 18 file test.c line 4 function main thread 0
----------------------------------------------------
  l=0 (00000000000000000000000000000000)

State 19 file test.c line 5 function main thread 0
----------------------------------------------------
  c=0 (00000000000000000000000000000000)

State 20 file test.c line 5 function main thread 0
----------------------------------------------------
  c=0 (00000000000000000000000000000000)

State 21 file test.c line 6 function main thread 0
----------------------------------------------------
  t=0 (00000000000000000000000000000000)

State 22 file test.c line 6 function main thread 0
----------------------------------------------------
  t=4187126263 (11111001100100100111100111110111)

State 23 file test.c line 7 function main thread 0
----------------------------------------------------
  s=0 (00000000000000000000000000000000)

State 24 file test.c line 7 function main thread 0
----------------------------------------------------
  s=3329066504 (11000110011011011000011000001000)

State 25 file test.c line 8 function main thread 0
----------------------------------------------------
  b=((unsigned int *)NULL) (0000000000000000000000000000000000000000000000000000000000000000)

State 26 file test.c line 8 function main thread 0
----------------------------------------------------
  b=&s!0@1 (0000001000000000000000000000000000000000000000000000000000000000)

Violated property:
  file test.c line 10 function main
  arithmetic overflow on unsigned + in t + *b
  !overflow("+", unsigned int, t, *b)

VERIFICATION FAILED

同样,这是一个实际的错误,CBMC有充分的理由告诉你。

可以解决这个问题
l = ((unsigned long)t + (unsigned long)*b) & (0xffffffffL);
c += (l < t);

给出了

CBMC version 5.1 64-bit x86_64 macos
Parsing test.c
Converting
Type-checking test
Generating GOTO Program
Adding CPROVER library
Function Pointer Removal
Partial Inlining
Generic Property Instrumentation
Starting Bounded Model Checking
size of program expression: 42 steps
simple slicing removed 3 assignments
Generated 3 VCC(s), 3 remaining after simplification
Passing problem to propositional reduction
converting SSA
Running propositional reduction
Post-processing
Solving with MiniSAT 2.2.0 with simplifier
542 variables, 1561 clauses
SAT checker inconsistent: negated claim is UNSATISFIABLE, i.e., holds
Runtime decision procedure: 0.002s
VERIFICATION SUCCESSFUL

变体3:如果事情与前一个相同,但你有signed int而不是unsigned int,事情会变得复杂一些。在这里,假设您使用(以更精细的方式编写,以便更好地了解正在发生的事情)

int nondet_int(void);

void main(void) {
  int l = 0;
  int c = 0;
  int t = nondet_int();
  int s = nondet_int();

  long longt = (long)t;
  long longs = (long)s;
  long temp1 = longt + longs;
  long temp2 = temp1 & (0xffffffffL);

  l = temp2;
  c += (l < t);
}

并运行

    cbmc --signed-overflow-check test.c

你会得到

CBMC version 5.1 64-bit x86_64 macos
Parsing test.c
Converting
Type-checking test
Generating GOTO Program
Adding CPROVER library
Function Pointer Removal
Partial Inlining
Generic Property Instrumentation
Starting Bounded Model Checking
size of program expression: 48 steps
simple slicing removed 3 assignments
Generated 3 VCC(s), 3 remaining after simplification
Passing problem to propositional reduction
converting SSA
Running propositional reduction
Post-processing
Solving with MiniSAT 2.2.0 with simplifier
872 variables, 2430 clauses
SAT checker: negated claim is SATISFIABLE, i.e., does not hold
Runtime decision procedure: 0.008s
Building error trace

Counterexample:

State 17 file test.c line 4 function main thread 0
----------------------------------------------------
  l=0 (00000000000000000000000000000000)

State 18 file test.c line 4 function main thread 0
----------------------------------------------------
  l=0 (00000000000000000000000000000000)

State 19 file test.c line 5 function main thread 0
----------------------------------------------------
  c=0 (00000000000000000000000000000000)

State 20 file test.c line 5 function main thread 0
----------------------------------------------------
  c=0 (00000000000000000000000000000000)

State 21 file test.c line 6 function main thread 0
----------------------------------------------------
  t=0 (00000000000000000000000000000000)

State 22 file test.c line 6 function main thread 0
----------------------------------------------------
  t=-2147483648 (10000000000000000000000000000000)

State 23 file test.c line 7 function main thread 0
----------------------------------------------------
  s=0 (00000000000000000000000000000000)

State 24 file test.c line 7 function main thread 0
----------------------------------------------------
  s=1 (00000000000000000000000000000001)

State 25 file test.c line 9 function main thread 0
----------------------------------------------------
  longt=0 (0000000000000000000000000000000000000000000000000000000000000000)

State 26 file test.c line 9 function main thread 0
----------------------------------------------------
  longt=-2147483648 (1111111111111111111111111111111110000000000000000000000000000000)

State 27 file test.c line 10 function main thread 0
----------------------------------------------------
  longs=0 (0000000000000000000000000000000000000000000000000000000000000000)

State 28 file test.c line 10 function main thread 0
----------------------------------------------------
  longs=1 (0000000000000000000000000000000000000000000000000000000000000001)

State 29 file test.c line 11 function main thread 0
----------------------------------------------------
  temp1=0 (0000000000000000000000000000000000000000000000000000000000000000)

State 31 file test.c line 11 function main thread 0
----------------------------------------------------
  temp1=-2147483647 (1111111111111111111111111111111110000000000000000000000000000001)

State 32 file test.c line 12 function main thread 0
----------------------------------------------------
  temp2=0 (0000000000000000000000000000000000000000000000000000000000000000)

State 33 file test.c line 12 function main thread 0
----------------------------------------------------
  temp2=2147483649 (0000000000000000000000000000000010000000000000000000000000000001)

Violated property:
  file test.c line 14 function main
  arithmetic overflow on signed type conversion in (signed int)temp2
  temp2 = -2147483648l

VERIFICATION FAILED

或者,写得更简洁,如果你有

t == -2147483648 (0b10000000000000000000000000000000)
s == 1           (0b00000000000000000000000000000001)

然后

temp2 == 2147483649 (0b0000000000000000000000000000000010000000000000000000000000000001)

并尝试将其转换为signed int很麻烦,因为它超出了范围(另请参阅Does cast between signed and unsigned int maintain exact bit pattern of variable in memory?)。

正如您所看到的,这个反例也是一个真正的错误,CBMC再一次告诉您这一点。这尤其意味着您的屏蔽/数学不能按预期工作(您的屏蔽将负数转换为超出界限的正数)并且您需要修复代码,以便结果在必要范围内。 (为了确保你得到正确的结果,仔细考虑你真正想做的事情可能是值得的。)