Skip to content

Commit 58a15d6

Browse files
authored
Merge pull request #6148 from feliperodri/is-fresh
Adds is_fresh predicate for function contracts
2 parents 9539a51 + dd27ec0 commit 58a15d6

File tree

35 files changed

+1278
-37
lines changed

35 files changed

+1278
-37
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stddef.h>
4+
#include <stdint.h>
5+
#include <stdlib.h>
6+
7+
bool dummy_for_definitions(int *n)
8+
{
9+
assert(__CPROVER_is_fresh(&n, sizeof(int)));
10+
int *x = malloc(sizeof(int));
11+
}
12+
13+
bool ptr_ok(int *x)
14+
{
15+
return *x < 5;
16+
}
17+
18+
/*
19+
Here are the meanings of the predicates:
20+
21+
static _Bool __foo_memory_map[__CPROVER_constant_infinity_uint];
22+
23+
bool __foo_requires_is_fresh(void **elem, size_t size) {
24+
*elem = malloc(size);
25+
if (!*elem || (__foo_memory_map[__CPROVER_POINTER_OBJECT(*elem)] != 0)) return false;
26+
27+
__foo_memory_map[__CPROVER_POINTER_OBJECT(*elem)] = 1;
28+
return true;
29+
}
30+
31+
bool __foo_ensures_is_fresh(void *elem, size_t size) {
32+
bool ok = (__foo_memory_map[__CPROVER_POINTER_OBJECT(elem)] == 0 &&
33+
__CPROVER_r_ok(elem, size));
34+
__foo_memory_map[__CPROVER_POINTER_OBJECT(elem)] = 1;
35+
return ok;
36+
}
37+
38+
39+
_Bool __call_foo_requires_is_fresh(void *elem, size_t size) {
40+
_Bool r_ok = __CPROVER_r_ok(elem, size);
41+
if (!__CPROVER_r_ok(elem, size) ||
42+
__foo_memory_map[__CPROVER_POINTER_OBJECT(elem)]) return 0;
43+
__foo_memory_map[__CPROVER_POINTER_OBJECT(elem)] = 1;
44+
return 1;
45+
}
46+
47+
// In the calling context, we assume freshness means new
48+
// allocation from within the function.
49+
bool __call_foo_ensures_is_fresh(void **elem, size_t size) {
50+
*elem = malloc(size);
51+
return (*elem != NULL);
52+
}
53+
*/
54+
55+
bool return_ok(int ret_value, int *x)
56+
{
57+
int a;
58+
a = *x;
59+
return ret_value == *x + 5;
60+
}
61+
62+
// The 'ensures' __CPROVER_is_fresh test is unnecessary, but left in just to test the function is working correctly.
63+
// If you remove the negation, the program will fail, because 'x' is not fresh.
64+
65+
int foo(int *x, int y) __CPROVER_assigns(*x)
66+
__CPROVER_requires(__CPROVER_is_fresh(x, sizeof(int)) && *x > 0 && ptr_ok(x))
67+
__CPROVER_ensures(
68+
!ptr_ok(x) && !__CPROVER_is_fresh(x, sizeof(int)) &&
69+
return_ok(__CPROVER_return_value, x))
70+
{
71+
*x = *x + 4;
72+
int y = *x + 5;
73+
return *x + 5;
74+
}
75+
76+
int main()
77+
{
78+
int *n = malloc(sizeof(int));
79+
assert(__CPROVER_r_ok(n, sizeof(int)));
80+
*n = 3;
81+
int o = foo(n, 10);
82+
assert(o >= 10 && o == *n + 5);
83+
return 0;
84+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--replace-all-calls-with-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
This test checks that verification succeeds if only expressions inside the assigns clause are assigned within the function.
10+
11+
Note: For all 'enforce' tests, nothing can be assumed about the return value of the function (as the function call is not replaced at this point).
12+
13+
To make such assumptions would cause verification to fail.

regression/contracts/quantifiers-loop-02/test.desc

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ main.c
33
--enforce-all-contracts _ --z3
44
^EXIT=0$
55
^SIGNAL=0$
6-
^\[main.1\] line .* Check loop invariant before entry: SUCCESS
7-
^\[main.2\] line .* Check that loop invariant is preserved: SUCCESS
8-
^\[main.assertion.1\] line .* assertion .*: SUCCESS
6+
^\[main.\d+\] line .* Check loop invariant before entry: SUCCESS
7+
^\[main.\d+\] line .* Check that loop invariant is preserved: SUCCESS
8+
^\[main.assertion.\d+\] line .* assertion .*: SUCCESS
99
^VERIFICATION SUCCESSFUL$
1010
--
1111
^warning: ignoring
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
int z;
6+
7+
// clang-format off
8+
int foo(int *x, int *y)
9+
__CPROVER_assigns(z, *x, *y)
10+
__CPROVER_requires(
11+
__CPROVER_is_fresh(x, sizeof(int)) &&
12+
__CPROVER_is_fresh(y, sizeof(int)) &&
13+
*x > 0 &&
14+
*x < 4)
15+
__CPROVER_ensures(
16+
x != NULL &&
17+
y != NULL &&
18+
x != y &&
19+
__CPROVER_return_value == *x + 5)
20+
// clang-format on
21+
{
22+
*x = *x + 4;
23+
*y = *x;
24+
z = *y;
25+
return (*x + 5);
26+
}
27+
28+
int main()
29+
{
30+
int n = 4;
31+
n = foo(&n, &n);
32+
assert(!(n < 4));
33+
return 0;
34+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
\[postcondition.\d+\] file main.c line \d+ Check ensures clause: SUCCESS
7+
\[foo.\d+\] line \d+ Check that \*x is assignable: SUCCESS
8+
\[foo.\d+\] line \d+ Check that \*y is assignable: SUCCESS
9+
\[foo.\d+\] line \d+ Check that z is assignable: SUCCESS
10+
\[main.assertion.\d+\] line \d+ assertion \!\(n \< 4\): SUCCESS
11+
^VERIFICATION SUCCESSFUL$
12+
--
13+
--
14+
Checks whether assuming __CPROVER_is_fresh will guarantee a new freshly
15+
allocated pointer (no aliasing) for structs.
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
int z;
6+
7+
// clang-format off
8+
int foo(int *x, int *y)
9+
__CPROVER_assigns(z, *x, y)
10+
__CPROVER_requires(
11+
__CPROVER_is_fresh(x, sizeof(int)) &&
12+
*x > 0 &&
13+
*x < 4)
14+
__CPROVER_ensures(
15+
x != NULL &&
16+
!__CPROVER_is_fresh(x, sizeof(int)) &&
17+
__CPROVER_is_fresh(y, sizeof(int)) &&
18+
x != y &&
19+
__CPROVER_return_value == *x + 5)
20+
// clang-format on
21+
{
22+
*x = *x + 4;
23+
y = malloc(sizeof(*y));
24+
*y = *x;
25+
z = *y;
26+
return (*x + 5);
27+
}
28+
29+
int main()
30+
{
31+
int n = 4;
32+
n = foo(&n, &n);
33+
assert(!(n < 4));
34+
return 0;
35+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
\[postcondition.\d+\] file main.c line \d+ Check ensures clause: SUCCESS
7+
\[foo.\d+\] line \d+ Check that \*x is assignable: SUCCESS
8+
\[foo.\d+\] line \d+ Check that \*y is assignable: SUCCESS
9+
\[foo.\d+\] line \d+ Check that z is assignable: SUCCESS
10+
\[main.assertion.\d+\] line \d+ assertion \!\(n \< 4\): SUCCESS
11+
^VERIFICATION SUCCESSFUL$
12+
--
13+
--
14+
Checks whether ensures(is_fresh()) pass on functions that perform allocation
15+
directly and return a new object.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
void bar(int *z) __CPROVER_assigns(z)
6+
__CPROVER_ensures(__CPROVER_is_fresh(z, sizeof(int)))
7+
{
8+
z = malloc(sizeof(*z));
9+
z = 10;
10+
}
11+
12+
// clang-format off
13+
int foo(int *x, int *y)
14+
__CPROVER_assigns(*x, y)
15+
__CPROVER_requires(
16+
__CPROVER_is_fresh(x, sizeof(int)) &&
17+
*x > 0 &&
18+
*x < 4)
19+
__CPROVER_ensures(
20+
x != NULL &&
21+
!__CPROVER_is_fresh(x, sizeof(int)) &&
22+
__CPROVER_is_fresh(y, sizeof(int)) &&
23+
x != y &&
24+
__CPROVER_return_value == *x + 5)
25+
// clang-format on
26+
{
27+
*x = *x + 4;
28+
bar(y);
29+
return (*x + 5);
30+
}
31+
32+
int main()
33+
{
34+
int n = 4;
35+
n = foo(&n, &n);
36+
assert(!(n < 4));
37+
return 0;
38+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
CORE
2+
main.c
3+
--enforce-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
\[postcondition.\d+\] file main.c line \d+ Check ensures clause: SUCCESS
7+
\[postcondition.\d+\] file main.c line \d+ Check ensures clause: SUCCESS
8+
\[bar.\d+\] line \d+ Check that z is assignable: SUCCESS
9+
\[foo.\d+\] line \d+ Check that \*x is assignable: SUCCESS
10+
\[foo.\d+\] line \d+ Check compatibility of assigns clause with the called function: SUCCESS
11+
\[main.assertion.\d+\] line \d+ assertion \!\(n \< 4\): SUCCESS
12+
^VERIFICATION SUCCESSFUL$
13+
--
14+
--
15+
Checks whether CBMC propagates freshness correctly across contracts.
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
int z;
6+
7+
// clang-format off
8+
int foo(int *x, int *y)
9+
__CPROVER_assigns(z, *x, *y)
10+
__CPROVER_requires(
11+
__CPROVER_is_fresh(x, sizeof(int)) &&
12+
__CPROVER_is_fresh(y, sizeof(int)) &&
13+
*x > 0
14+
&& *x <= 4)
15+
__CPROVER_ensures(
16+
x != NULL &&
17+
y != NULL &&
18+
x != y &&
19+
__CPROVER_return_value == *x + 5)
20+
// clang-format on
21+
{
22+
*x = *x + 4;
23+
return (*x + 5);
24+
}
25+
26+
int main()
27+
{
28+
int n = 4;
29+
n = foo(&n, &n);
30+
assert(!(n < 4));
31+
return 0;
32+
}
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=10$
5+
^SIGNAL=0$
6+
\[precondition.\d+\] file main.c line \d+ Check requires clause: FAILURE
7+
\[main.assertion.\d+\] line \d+ assertion \!\(n \< 4\): SUCCESS
8+
^VERIFICATION FAILED$
9+
--
10+
--
11+
Checks whether asserting __CPROVER_is_fresh will guarantee that objects
12+
mapped to distinct objetcs.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#include <stdbool.h>
2+
3+
bool ptr_ok(int *x)
4+
{
5+
return (*x < 5);
6+
}
7+
8+
bool return_ok(int ret_value, int *x)
9+
{
10+
int a;
11+
a = *x;
12+
return (ret_value == *x + 5);
13+
}
14+
15+
// clang-format off
16+
int foo(int *x)
17+
__CPROVER_assigns(*x)
18+
__CPROVER_requires(
19+
__CPROVER_is_fresh(x, sizeof(int) * 10) &&
20+
x[0] > 0 &&
21+
ptr_ok(x))
22+
__CPROVER_ensures(
23+
!ptr_ok(x) &&
24+
!__CPROVER_is_fresh(x, sizeof(int) * 10) && /* `x` is not fresh anymore. */
25+
x[9] == 113 &&
26+
return_ok(__CPROVER_return_value, x))
27+
// clang-format on
28+
{
29+
*x = *x + 4;
30+
x[5] = 12;
31+
x[9] = 113;
32+
int y = *x + 5;
33+
return *x + 5;
34+
}
35+
36+
int main()
37+
{
38+
int *n;
39+
int o = foo(n);
40+
return 0;
41+
}
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-all-contracts
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
\[postcondition.\d+\] file main.c line \d+ Check ensures clause: SUCCESS
7+
\[foo.\d+\] line \d+ Check that \*x is assignable: SUCCESS
8+
\[foo.\d+\] line \d+ Check that x\[\(.* int\)5\] is assignable: SUCCESS
9+
\[foo.\d+\] line \d+ Check that x\[\(.* int\)9\] is assignable: SUCCESS
10+
\[foo.\d+\] line \d+ Check that y is assignable: SUCCESS
11+
^VERIFICATION SUCCESSFUL$
12+
--
13+
--
14+
Checks whether __CPROVER_is_fresh behaves correctly for arrays both in
15+
requires and ensures clauses. In the ensures clause, the __CPROVER_is_fresh
16+
is unnecessary. By negating the predicate in the ensures clause, the test
17+
proves that `x` is not fresh at end of the function.

0 commit comments

Comments
 (0)