Skip to content

Commit 098898c

Browse files
authored
Merge pull request #6814 from remi-delmas-3000/contracts-object-slice
CONTRACTS: Support object slices in assigns clauses
2 parents 74a6796 + 0bb6993 commit 098898c

File tree

21 files changed

+419
-50
lines changed

21 files changed

+419
-50
lines changed

doc/cprover-manual/contracts-assigns.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,30 @@ design is based on the former. This means that memory not captured by an
1313
_assigns_ clause must not be written within the given function, even if the
1414
value(s) therein are not modified.
1515
16+
### Object slice expressions
17+
18+
The following functions can be used in assigns clause to specify ranges of
19+
assignable addresses.
20+
21+
Given a pointer `ptr` pointing into some object `o`, `__CPROVER_object_from(ptr)`
22+
specifies that all bytes starting from the given pointer and until the end of
23+
the object are assignable:
24+
```c
25+
__CPROVER_size_t __CPROVER_object_from(void *ptr);
26+
```
27+
28+
Given a pointer `ptr` pointing into some object `o`, `__CPROVER_object_from(ptr, size)`
29+
specifies that `size` bytes starting from the given pointer and until the end of the object are assignable.
30+
The `size` value must such that `size <= __CPROVER_object_size(ptr) - __CPROVER_pointer_offset(ptr)` holds:
31+
32+
```c
33+
__CPROVER_size_t __CPROVER_object_slice(void *ptr, __CPROVER_size_t size);
34+
```
35+
36+
Caveats and limitations: The slices in question must *not*
37+
be interpreted as pointers by the program. During call-by-contract replacement,
38+
`__CPROVER_havoc_slice(ptr, size)` is used to havoc these targets,
39+
and `__CPROVER_havoc_slice` does not support havocing pointers.
1640
### Parameters
1741
1842
An _assigns_ clause currently supports simple variable types and their pointers,
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
struct st
2+
{
3+
int a;
4+
char arr1[10];
5+
int b;
6+
char arr2[10];
7+
int c;
8+
};
9+
10+
void foo(struct st *s)
11+
// clang-format off
12+
__CPROVER_requires(__CPROVER_is_fresh(s, sizeof(*s)))
13+
__CPROVER_assigns(__CPROVER_object_slice(s->arr1, 5))
14+
__CPROVER_assigns(__CPROVER_object_from(s->arr2 + 5))
15+
// clang-format on
16+
{
17+
// PASS
18+
s->arr1[0] = 0;
19+
s->arr1[1] = 0;
20+
s->arr1[2] = 0;
21+
s->arr1[3] = 0;
22+
s->arr1[4] = 0;
23+
24+
// FAIL
25+
s->arr1[5] = 0;
26+
s->arr1[6] = 0;
27+
s->arr1[7] = 0;
28+
s->arr1[8] = 0;
29+
s->arr1[9] = 0;
30+
31+
// FAIL
32+
s->arr2[0] = 0;
33+
s->arr2[1] = 0;
34+
s->arr2[2] = 0;
35+
s->arr2[3] = 0;
36+
s->arr2[4] = 0;
37+
38+
// PASS
39+
s->arr2[5] = 0;
40+
s->arr2[6] = 0;
41+
s->arr2[7] = 0;
42+
s->arr2[8] = 0;
43+
s->arr2[9] = 0;
44+
}
45+
46+
int main()
47+
{
48+
struct st s;
49+
50+
foo(&s);
51+
52+
return 0;
53+
}
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
struct st
2+
{
3+
int a;
4+
char arr1[10];
5+
int b;
6+
char arr2[10];
7+
int c;
8+
};
9+
10+
void foo(struct st *s)
11+
// clang-format off
12+
__CPROVER_requires(__CPROVER_is_fresh(s, sizeof(*s)))
13+
__CPROVER_assigns(__CPROVER_object_slice(s->arr1, 5))
14+
__CPROVER_assigns(__CPROVER_object_from(s->arr2 + 5))
15+
// clang-format on
16+
{
17+
s->arr1[0] = 0;
18+
s->arr1[1] = 0;
19+
s->arr1[2] = 0;
20+
s->arr1[3] = 0;
21+
s->arr1[4] = 0;
22+
23+
s->arr2[5] = 0;
24+
s->arr2[6] = 0;
25+
s->arr2[7] = 0;
26+
s->arr2[8] = 0;
27+
s->arr2[9] = 0;
28+
}
29+
30+
int main()
31+
{
32+
struct st s;
33+
s.a = 0;
34+
s.arr1[0] = 0;
35+
s.arr1[1] = 0;
36+
s.arr1[2] = 0;
37+
s.arr1[3] = 0;
38+
s.arr1[4] = 0;
39+
s.arr1[5] = 0;
40+
s.arr1[6] = 0;
41+
s.arr1[7] = 0;
42+
s.arr1[8] = 0;
43+
s.arr1[9] = 0;
44+
45+
s.arr2[0] = 0;
46+
s.arr2[1] = 0;
47+
s.arr2[2] = 0;
48+
s.arr2[3] = 0;
49+
s.arr2[4] = 0;
50+
s.arr2[5] = 0;
51+
s.arr2[6] = 0;
52+
s.arr2[7] = 0;
53+
s.arr2[8] = 0;
54+
s.arr2[9] = 0;
55+
s.c = 0;
56+
57+
foo(&s);
58+
59+
// PASS
60+
assert(s.a == 0);
61+
62+
// FAIL
63+
assert(s.arr1[0] == 0);
64+
assert(s.arr1[1] == 0);
65+
assert(s.arr1[2] == 0);
66+
assert(s.arr1[3] == 0);
67+
assert(s.arr1[4] == 0);
68+
69+
// PASS
70+
assert(s.arr1[5] == 0);
71+
assert(s.arr1[6] == 0);
72+
assert(s.arr1[7] == 0);
73+
assert(s.arr1[8] == 0);
74+
assert(s.arr1[9] == 0);
75+
76+
// PASS
77+
assert(s.b == 0);
78+
79+
// PASS
80+
assert(s.arr2[0] == 0);
81+
assert(s.arr2[1] == 0);
82+
assert(s.arr2[2] == 0);
83+
assert(s.arr2[3] == 0);
84+
assert(s.arr2[4] == 0);
85+
86+
// FAIL
87+
assert(s.arr2[5] == 0);
88+
assert(s.arr2[6] == 0);
89+
assert(s.arr2[7] == 0);
90+
assert(s.arr2[8] == 0);
91+
assert(s.arr2[9] == 0);
92+
93+
// PASS
94+
assert(s.c == 0);
95+
return 0;
96+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
CORE
2+
main-enforce.c
3+
--enforce-contract foo
4+
^\[foo.assigns.\d+\].* Check that __CPROVER_object_slice\(\(void \*\)s->arr1, \(.*\)5\) is valid: SUCCESS$
5+
^\[foo.assigns.\d+\].* Check that __CPROVER_object_from\(\(void \*\)\(s->arr2 \+ \(.*\)5\)\) is valid: SUCCESS$
6+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)0\] is assignable: SUCCESS$
7+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)1\] is assignable: SUCCESS$
8+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)2\] is assignable: SUCCESS$
9+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)3\] is assignable: SUCCESS$
10+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)4\] is assignable: SUCCESS$
11+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)5\] is assignable: FAILURE$
12+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)6\] is assignable: FAILURE$
13+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)7\] is assignable: FAILURE$
14+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)8\] is assignable: FAILURE$
15+
^\[foo.assigns.\d+\].* Check that s->arr1\[\(.*\)9\] is assignable: FAILURE$
16+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)0\] is assignable: FAILURE$
17+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)1\] is assignable: FAILURE$
18+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)2\] is assignable: FAILURE$
19+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)3\] is assignable: FAILURE$
20+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)4\] is assignable: FAILURE$
21+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)5\] is assignable: SUCCESS$
22+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)6\] is assignable: SUCCESS$
23+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)7\] is assignable: SUCCESS$
24+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)8\] is assignable: SUCCESS$
25+
^\[foo.assigns.\d+\].* Check that s->arr2\[\(.*\)9\] is assignable: SUCCESS$
26+
^VERIFICATION FAILED$
27+
^EXIT=10$
28+
^SIGNAL=0$
29+
--
30+
--
31+
Checks that assigns clause checking of slice expressions works as expected when
32+
enforcing a contract.
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
CORE
2+
main-replace.c
3+
--replace-call-with-contract foo
4+
^\[main.assertion.\d+\].*assertion s.a == 0: SUCCESS$
5+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)0\] == 0: FAILURE$
6+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)1\] == 0: FAILURE$
7+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)2\] == 0: FAILURE$
8+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)3\] == 0: FAILURE$
9+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)4\] == 0: FAILURE$
10+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)5\] == 0: SUCCESS$
11+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)6\] == 0: SUCCESS$
12+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)7\] == 0: SUCCESS$
13+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)8\] == 0: SUCCESS$
14+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr1\[\(.*\)9\] == 0: SUCCESS$
15+
^\[main.assertion.\d+\].*assertion s.b == 0: FAILURE$
16+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)0\] == 0: SUCCESS$
17+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)1\] == 0: SUCCESS$
18+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)2\] == 0: SUCCESS$
19+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)3\] == 0: SUCCESS$
20+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)4\] == 0: SUCCESS$
21+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)5\] == 0: FAILURE$
22+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)6\] == 0: FAILURE$
23+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)7\] == 0: FAILURE$
24+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)8\] == 0: FAILURE$
25+
^\[main.assertion.\d+\].*assertion \(.*\)s.arr2\[\(.*\)9\] == 0: FAILURE$
26+
^\[main.assertion.\d+\].*assertion s.c == 0: FAILURE$
27+
^VERIFICATION FAILED$
28+
^EXIT=10$
29+
^SIGNAL=0$
30+
--
31+
--
32+
Checks that havocking of slice expressions works as expected when
33+
replacing a call by a contract. We manually express frame conditions as
34+
assertions in the main function.

regression/contracts/assigns_enforce_address_of/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
6+
^.*error: assigns clause target must be an lvalue or a __CPROVER_POINTER_OBJECT, __CPROVER_object_from, __CPROVER_object_slice expression$
77
^CONVERSION ERROR$
88
--
99
--

regression/contracts/assigns_enforce_conditional_non_lvalue_target/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
CORE
22
main.c
33
--enforce-contract foo
4-
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
4+
^.*error: assigns clause target must be an lvalue or a __CPROVER_POINTER_OBJECT, __CPROVER_object_from, __CPROVER_object_slice expression$
55
^CONVERSION ERROR
66
^EXIT=(1|64)$
77
^SIGNAL=0$

regression/contracts/assigns_enforce_conditional_non_lvalue_target_list/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
CORE
22
main.c
33
--enforce-contract foo
4-
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
4+
^.*error: assigns clause target must be an lvalue or a __CPROVER_POINTER_OBJECT, __CPROVER_object_from, __CPROVER_object_slice expression$
55
^CONVERSION ERROR
66
^EXIT=(1|64)$
77
^SIGNAL=0$

regression/contracts/assigns_enforce_function_calls/test.desc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
7-
^.*error: side-effects not allowed in assigns clause targets$
6+
^.*error: function calls in assigns clause targets must be to __CPROVER_object_from, __CPROVER_object_slice$
87
^CONVERSION ERROR$
98
--
109
--

regression/contracts/assigns_enforce_literal/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
6+
^.*error: assigns clause target must be an lvalue or a __CPROVER_POINTER_OBJECT, __CPROVER_object_from, __CPROVER_object_slice expression$
77
^CONVERSION ERROR$
88
--
99
--

regression/contracts/assigns_enforce_side_effects_1/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CORE
22
main.c
33
--enforce-contract foo
44
activate-multi-line-match
5-
.*error: (dereferencing void pointer|void-typed expressions not allowed as assigns clause targets\n.*\n.*error: side-effects not allowed in assigns clause targets\n.*\n.*error: ternary expressions not allowed in assigns clause targets\n)
5+
.*error: (dereferencing void pointer|void-typed expressions not allowed as assigns clause targets)
66
^CONVERSION ERROR$
77
^EXIT=(1|64)$
88
^SIGNAL=0$

regression/contracts/assigns_enforce_side_effects_2/test.desc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
7-
^.*error: side-effects not allowed in assigns clause targets$
6+
^.*error: assigns clause target must be an lvalue or a __CPROVER_POINTER_OBJECT, __CPROVER_object_from, __CPROVER_object_slice expression$
87
^CONVERSION ERROR$
98
--
109
--

regression/contracts/assigns_enforce_side_effects_3/test.desc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ main.c
33
--enforce-contract foo
44
^EXIT=(1|64)$
55
^SIGNAL=0$
6-
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
7-
^.*error: side-effects not allowed in assigns clause targets$
6+
^.*error: assigns clause target must be an lvalue or a __CPROVER_POINTER_OBJECT, __CPROVER_object_from, __CPROVER_object_slice expression$
87
^CONVERSION ERROR$
98
--
109
--

regression/contracts/assigns_type_checking_invalid_case_01/test.desc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,6 @@ main.c
44
^EXIT=(1|64)$
55
^SIGNAL=0$
66
^CONVERSION ERROR$
7-
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
7+
^.*error: assigns clause target must be an lvalue or a __CPROVER_POINTER_OBJECT, __CPROVER_object_from, __CPROVER_object_slice expression$
88
--
99
Checks whether type checking rejects literal constants in assigns clause.

0 commit comments

Comments
 (0)