Skip to content

Commit 9bc4ee7

Browse files
committed
Adds support for pointer history variables in function contracts
This adds support for history variables in function contracts. History variables are (1) declared and (2) assigned to the value that their corresponding variable has at function call time. Currently, only pointers are supported.
1 parent afd8a60 commit 9bc4ee7

File tree

34 files changed

+600
-17
lines changed

34 files changed

+600
-17
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <stdio.h>
2+
3+
void bar(int *l) __CPROVER_assigns(*l) __CPROVER_requires(l != NULL)
4+
__CPROVER_ensures(__CPROVER_old(*l) == *l)
5+
{
6+
}
7+
8+
void foo(int *n) __CPROVER_assigns(*n) __CPROVER_requires(n != NULL)
9+
__CPROVER_ensures(__CPROVER_old(*n) == *n)
10+
{
11+
bar(n);
12+
}
13+
14+
int main()
15+
{
16+
int m;
17+
foo(&m);
18+
19+
return 0;
20+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CORE
2+
main.c
3+
--enforce-contract foo --replace-call-with-contract bar
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSUME tmp_cc\$\d != \(\(signed int \*\)NULL\)
8+
ASSERT n != \(\(signed int \*\)NULL\)
9+
ASSUME tmp_cc == \*n
10+
ASSERT tmp_cc\$\d == \*tmp_cc\$\d
11+
--
12+
--
13+
Verification:
14+
This test checks that history variables are supported for parameters of the
15+
the function under test. By using the --enforce-all-contracts flag,
16+
the post-condition (which contains the history variable) is asserted.
17+
In this case, this assertion should pass.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void foo(int *x) __CPROVER_assigns(*x)
2+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 5)
3+
{
4+
*x = *x + 5;
5+
}
6+
7+
int main()
8+
{
9+
int n;
10+
foo(&n);
11+
12+
return 0;
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT \*tmp_cc\$\d == tmp_cc\$\d \+ 5
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported for parameters of the
12+
the function under test. By using the --enforce-all-contracts flag,
13+
the post-condition (which contains the history variable) is asserted.
14+
In this case, this assertion should pass.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void foo(int *x) __CPROVER_assigns(*x)
2+
__CPROVER_ensures(*x < __CPROVER_old(*x) + 5)
3+
{
4+
*x = *x + 5;
5+
}
6+
7+
int main()
8+
{
9+
int n;
10+
foo(&n);
11+
12+
return 0;
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
ASSERT \*tmp_cc\$\d < tmp_cc\$\d \+ 5
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported for parameters of the
12+
the function under test. By using the --enforce-all-contracts flag,
13+
the post-condition (which contains the history variable) is asserted.
14+
In this case, this assertion should fail.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <limits.h>
2+
3+
void foo(int *x) __CPROVER_assigns(*x)
4+
__CPROVER_requires(*x > 0 && *x < INT_MAX - 5) __CPROVER_ensures(
5+
*x >= __CPROVER_old(*x) + 4 && *x <= __CPROVER_old(*x) + 6)
6+
{
7+
*x = *x + 5;
8+
}
9+
10+
int main()
11+
{
12+
int n;
13+
foo(&n);
14+
15+
return 0;
16+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT tmp_if_expr\$\d
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported in the case where a
12+
history variable is referred to multiple times within an ensures clause.
13+
By using the --enforce-all-contracts flag, the post-condition (which contains
14+
the history variable) is asserted. In this case, this assertion should pass.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
void foo(int *x, int *y) __CPROVER_assigns(*x, *y)
2+
__CPROVER_ensures(*x == __CPROVER_old(*y) + 1 && *y == __CPROVER_old(*x) + 2)
3+
{
4+
int x_initial = *x;
5+
*x = *y + 1;
6+
*y = x_initial + 2;
7+
}
8+
9+
int main()
10+
{
11+
int x, y;
12+
foo(&x, &y);
13+
14+
return 0;
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT tmp_if_expr
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported in the case where the
12+
function under test has multiple parameters. By using the
13+
--enforce-all-contracts flag, the post-condition (which contains the history
14+
variables) is asserted. In this case, this assertion should pass.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
void foo(int *x, int *y) __CPROVER_assigns(*x, *y)
2+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 2 || *y == __CPROVER_old(*y) + 3)
3+
{
4+
*x = *x + 1;
5+
*y = *y + 2;
6+
}
7+
8+
int main()
9+
{
10+
int x, y;
11+
foo(&x, &y);
12+
13+
return 0;
14+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
ASSERT tmp_if_expr
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported in the case where the
12+
function under test has multiple parameters. By using the
13+
--enforce-all-contracts flag, the post-condition (which contains the history
14+
variables) is asserted. In this case, this assertion should fail.
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <assert.h>
2+
3+
void foo(int *x) __CPROVER_assigns(*x)
4+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 5)
5+
{
6+
assert(__CPROVER_old(*x) == *x);
7+
*x = *x + 5;
8+
}
9+
10+
int main()
11+
{
12+
int n;
13+
foo(&n);
14+
15+
return 0;
16+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
^VERIFICATION FAILED$
7+
warning: ignoring old
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are not supported when referred to from
12+
a function body. In such a case, verification should fail.
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
void foo(int *x) __CPROVER_assigns(*x)
2+
__CPROVER_ensures(*x == __CPROVER_old(*y) + 5)
3+
{
4+
*x = *x + 5;
5+
}
6+
7+
int main()
8+
{
9+
int n;
10+
foo(&n);
11+
12+
return 0;
13+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=(1|64)$
5+
^SIGNAL=0$
6+
^CONVERSION ERROR$
7+
error: failed to find symbol 'y'
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables may only be used with existing
12+
symbols. In other words, including a new symbol as part of __CPROVER_old()
13+
is not alowed. In such a case, the program should not parse and there
14+
should be a conversion error.
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <stdlib.h>
2+
3+
struct pair
4+
{
5+
int *x;
6+
int *y;
7+
};
8+
9+
void foo(struct pair p) __CPROVER_assigns(*(p.y))
10+
__CPROVER_ensures(*(p.y) == __CPROVER_old(*(p.y)) + 5)
11+
{
12+
*(p.y) = *(p.y) + 5;
13+
}
14+
15+
int main()
16+
{
17+
struct pair p;
18+
p.x = malloc(sizeof(int));
19+
p.y = malloc(sizeof(int));
20+
21+
foo(p);
22+
23+
return 0;
24+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT \*tmp_cc\$\d\.y == tmp_cc\$\d \+ 5
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables are supported for dereferences over
12+
pointers to struct members. By using the --enforce-all-contracts flag, the
13+
post-condition (which contains the history variable) is asserted. In this
14+
case, this assertion should pass.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <assert.h>
2+
#include <limits.h>
3+
4+
void foo(int *x) __CPROVER_assigns(*x) __CPROVER_requires(*x > 0)
5+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 2)
6+
{
7+
*x = *x + 2;
8+
}
9+
10+
int main()
11+
{
12+
int n;
13+
__CPROVER_assume(n > 0 && n < INT_MAX - 2);
14+
foo(&n);
15+
16+
assert(n > 2);
17+
18+
return 0;
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT \*\(&n\) > 0
8+
ASSUME \*\(&n\) == tmp_cc[\$\d]? \+ 2
9+
--
10+
--
11+
Verification:
12+
This test checks that history variables are supported with the use of the
13+
--replace-all-calls-with-contracts flag. In this case, the pre-condition
14+
becomes an assertion and the post-condition (which contains the history
15+
variable) becomes an assumption.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include <assert.h>
2+
3+
void foo(int *x) __CPROVER_assigns(*x) __CPROVER_requires(*x == 0)
4+
__CPROVER_ensures(*x >= __CPROVER_old(*x) + 2)
5+
{
6+
*x = *x + 2;
7+
}
8+
9+
int main()
10+
{
11+
int n = 0;
12+
foo(&n);
13+
14+
assert(n >= 2);
15+
16+
return 0;
17+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
ASSERT \*\(&n\) == 0
8+
ASSUME \*\(&n\) >= tmp_cc[\$\d]? \+ 2
9+
--
10+
--
11+
Verification:
12+
This test checks that history variables are supported with the use of the
13+
--replace-all-calls-with-contracts flag. In this case, the pre-condition
14+
becomes an assertion and the post-condition (which contains the history
15+
variable) becomes an assumption.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <assert.h>
2+
3+
void foo(int *x) __CPROVER_assigns(*x)
4+
__CPROVER_requires(*x == __CPROVER_old(*x))
5+
__CPROVER_ensures(*x == __CPROVER_old(*x) + 2)
6+
{
7+
*x = *x + 2;
8+
}
9+
10+
int main()
11+
{
12+
int n = 0;
13+
foo(&n);
14+
15+
assert(n == 2);
16+
17+
return 0;
18+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=(1|64)$
5+
^SIGNAL=0$
6+
^CONVERSION ERROR$
7+
error: __CPROVER_old expressions are not allowed in __CPROVER_requires clauses
8+
--
9+
--
10+
Verification:
11+
This test checks that history variables cannot be used as part of the
12+
pre-condition contract. In this case, verification should fail.

scripts/expected_doxygen_warnings.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,6 @@ warning: Included by graph for 'pointer_expr.h' not generated, too many nodes (1
3535
warning: Included by graph for 'prefix.h' not generated, too many nodes (85), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
3636
warning: Included by graph for 'simplify_expr.h' not generated, too many nodes (79), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
3737
warning: Included by graph for 'std_code.h' not generated, too many nodes (79), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
38-
warning: Included by graph for 'std_expr.h' not generated, too many nodes (241), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
38+
warning: Included by graph for 'std_expr.h' not generated, too many nodes (242), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
3939
warning: Included by graph for 'std_types.h' not generated, too many nodes (96), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.
4040
warning: Included by graph for 'symbol_table.h' not generated, too many nodes (95), threshold is 60. Consider increasing DOT_GRAPH_MAX_NODES.

0 commit comments

Comments
 (0)