Skip to content

Commit 08d5056

Browse files
authored
Merge pull request #6564 from remi-delmas-3000/check-target-validity-simplify-snapshots
Function contracts: check target validity, simplify snapshot instrumentation
2 parents 90bcd52 + ec3902c commit 08d5056

File tree

22 files changed

+278
-105
lines changed

22 files changed

+278
-105
lines changed

regression/contracts/assigns-enforce-malloc-zero/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ int foo(char *a, int size)
66
// clang-format off
77
__CPROVER_requires(0 <= size && size <= __CPROVER_max_malloc_size)
88
__CPROVER_requires(a == NULL || __CPROVER_is_fresh(a, size))
9-
__CPROVER_assigns(__CPROVER_POINTER_OBJECT(a))
9+
__CPROVER_assigns(a: __CPROVER_POINTER_OBJECT(a))
1010
__CPROVER_ensures(
1111
a && __CPROVER_return_value >= 0 ==> a[__CPROVER_return_value] == 0)
1212
// clang-format on

regression/contracts/assigns_enforce_free_dead/main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#include <assert.h>
22
#include <stdlib.h>
33

4-
int foo(int *x, int **p) __CPROVER_assigns(*x, *p, **p)
4+
int foo(int *x, int **p) __CPROVER_assigns(*x; p : *p; p && *p : **p)
55
{
66
if(p && *p)
77
**p = 0;
@@ -44,12 +44,12 @@ int foo(int *x, int **p) __CPROVER_assigns(*x, *p, **p)
4444
// q goes DEAD here, unconditionally
4545
}
4646

47-
free(z);
47+
free(z); // should not fail because free(NULL) is allowed in C
4848

4949
z = malloc(sizeof(int));
5050
if(nondet_bool())
5151
{
52-
free(z);
52+
free(z); // should not fail because free(NULL) is allowed in C
5353
// here z is deallocated, conditionally
5454
}
5555

regression/contracts/assigns_enforce_free_dead/test.desc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
CORE
22
main.c
33
--enforce-contract foo _ --malloc-may-fail --malloc-fail-null --pointer-primitive-check
4-
^\[foo.\d+\] line \d+ Check that \*\(\*p\) is assignable: SUCCESS$
5-
^\[foo.\d+\] line \d+ Check that \*\(\*p\) is assignable: FAILURE$
4+
^\[foo.\d+\] line 7 Check that \*\(\*p\) is assignable: SUCCESS$
5+
^\[foo.\d+\] line 24 Check that \*\(\*p\) is assignable: FAILURE$
66
^\[foo.\d+\] line \d+ Check that \*p is assignable: SUCCESS$
77
^\[foo.\d+\] line \d+ Check that \*q is assignable: SUCCESS$
88
^\[foo.\d+\] line \d+ Check that \*w is assignable: SUCCESS$
Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
KNOWNBUG
1+
CORE
22
main.c
3-
--enforce-contract foo
3+
--enforce-contract foo _ --pointer-check
4+
^\[.*\d+\].* line 5 Check assigns target validity 'TRUE: \*x': SUCCESS$
5+
^\[.*\d+\].* line 8 Check that x\[\(.*\)1\] is assignable: (SUCCESS|FAILURE)$
6+
^\[.*\d+\].* line 8 Check assigns target validity 'TRUE: x\[\(.*\)1\]': FAILURE$
47
^EXIT=10$
58
^SIGNAL=0$
69
^VERIFICATION FAILED$
710
--
811
--
9-
Check that a write at *x fails when the assigns clause specifies *(x + 1) and
10-
the actual underlying object is of size 1.
12+
Check that a write at *x+1 fails when the assigns clause specifies a valid *x
13+
and the actual underlying object is of size 1.
14+
In this case the specified target is valid, the lhs of the assignment is invalid
15+
so the inclusion check passes, but the pointer check must fail with an OOB.

regression/contracts/assigns_enforce_offsets_4/main.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ int main()
1313
{
1414
int *x = malloc(2 * sizeof(int));
1515
*x = 0;
16-
*(x + 1) == 0
17-
// write should fail because x points to a size 2 object and the contracts expects size 10 at least.
18-
foo(x);
16+
*(x + 1) == 0;
17+
// write should fail because x points to a size 2 object and the contracts expects size 10 at least.
18+
foo(x);
1919
return 0;
2020
}
Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
1-
KNOWNBUG
1+
CORE
22
main.c
3-
--enforce-contract foo
3+
--enforce-contract foo _ --pointer-check
4+
^\[.*\d+\].* line 8 Check assigns target validity 'TRUE: x\[\(.*\)10\]': FAILURE$
45
^EXIT=10$
56
^SIGNAL=0$
67
^VERIFICATION FAILED$
78
--
89
--
9-
Check that a write at *x fails when the assigns clause specifies *(x + 1) and the actual underlying object is of size 1.
10+
Check that a write at *(x+10) fails when the assigns clause specifies *(x + 10)
11+
and the actual underlying object is too small.
12+
In that case the target inclusion succeeds because the LHS is in an invalid
13+
state, but the target validity check must fail.

regression/contracts/assigns_enforce_structs_07/test.desc

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,14 @@ main.c
33
--enforce-contract f1 --enforce-contract f2 _ --malloc-may-fail --malloc-fail-null --pointer-check
44
^EXIT=10$
55
^SIGNAL=0$
6-
^\[f1.\d+\] line \d+ Check that p->buf\[\(.*\)0\] is assignable: SUCCESS$
7-
^\[f1.pointer\_dereference.\d+\] line \d+ dereference failure: pointer invalid in p->buf\[\(.*\)0\]: FAILURE$
8-
^\[f1.pointer\_dereference.\d+\] line \d+ dereference failure: pointer NULL in p->buf: FAILURE$
9-
^\[f2.\d+\] line \d+ Check that pp->p->buf\[\(.*\)0\] is assignable: SUCCESS$
10-
^\[f2.pointer\_dereference.\d+\] line \d+ dereference failure: pointer invalid in pp->p->buf\[\(.*\)0\]: FAILURE$
6+
^\[f1.*\d+\].*line 18 Check assigns target validity 'TRUE: p->buf\[\(.*\)0\]': FAILURE$
7+
^\[f2.*\d+\].*line 23 Check assigns target validity 'TRUE: pp->p->buf\[\(.*\)0\]': FAILURE$
118
^VERIFICATION FAILED$
129
--
1310
--
14-
Checks whether CBMC properly evaluates write set of members
15-
from invalid objects. In this case, they are not writable.
11+
In f1, the assigns clause specifies `*(p->buf)` as target (which can be invalid)
12+
and assigns `p->buf[0]` unconditionally. When both target and lhs are invalid,
13+
its inclusion check can be trivially satisfied (or not) but we expect the target
14+
validity check to fail.
15+
16+
In f2 tests this behaviour with chained dereferences.
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
#include <stdlib.h>
4+
5+
struct pair
6+
{
7+
uint8_t *buf;
8+
size_t size;
9+
};
10+
11+
struct pair_of_pairs
12+
{
13+
struct pair *p;
14+
};
15+
16+
void f1(struct pair *p) __CPROVER_assigns(p && p->buf : *(p->buf))
17+
{
18+
if(p && p->buf)
19+
p->buf[0] = 0;
20+
}
21+
22+
void f2(struct pair_of_pairs *pp)
23+
// clang-format off
24+
__CPROVER_assigns(pp && pp->p && pp->p->buf: *(pp->p->buf))
25+
// clang-format on
26+
{
27+
if(pp && pp->p && pp->p->buf)
28+
pp->p->buf[0] = 0;
29+
}
30+
31+
int main()
32+
{
33+
struct pair *p = malloc(sizeof(*p));
34+
if(p)
35+
p->buf = malloc(100 * sizeof(uint8_t));
36+
f1(p);
37+
38+
struct pair_of_pairs *pp = malloc(sizeof(*pp));
39+
if(pp)
40+
{
41+
pp->p = malloc(sizeof(*(pp->p)));
42+
if(pp->p)
43+
pp->p->buf = malloc(100 * sizeof(uint8_t));
44+
}
45+
f2(pp);
46+
47+
return 0;
48+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
CORE
2+
main.c
3+
--enforce-contract f1 --enforce-contract f2 _ --malloc-may-fail --malloc-fail-null --pointer-check
4+
^\[f1.\d+\] line \d+ Check that p->buf\[\(.*\)0\] is assignable: SUCCESS$
5+
^\[f2.\d+\] line \d+ Check that pp->p->buf\[\(.*\)0\] is assignable: SUCCESS$
6+
^VERIFICATION SUCCESSFUL$
7+
^EXIT=0$
8+
^SIGNAL=0$
9+
--
10+
^\[f1.pointer\_dereference.\d+\] line \d+ dereference failure: pointer NULL in p->buf: FAILURE$
11+
^\[f1.pointer\_dereference.\d+\] line \d+ dereference failure: pointer invalid in p->buf\[\(.*\)0\]: FAILURE$
12+
^\[f2.pointer\_dereference.\d+\] line \d+ dereference failure: pointer NULL in pp->p->buf\[\(.*\)0\]: FAILURE$
13+
^\[f2.pointer\_dereference.\d+\] line \d+ dereference failure: pointer invalid in pp->p->buf\[\(.*\)0\]: FAILURE$
14+
--
15+
In f1, the assigns clause specifies `*(p->buf)` as target (which can be invalid)
16+
and assigns `p->buf[0]` unconditionally. When both target and lhs are invalid,
17+
its inclusion check can be trivially satisfied or not but we expect in all
18+
cases a null pointer failure and an invalid pointer error to occur
19+
on the assignment.
20+
21+
In f2 tests this behaviour with chained dereferences.

regression/contracts/assigns_enforce_subfunction_calls/test.desc

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,13 @@ main.c
44
^EXIT=10$
55
^SIGNAL=0$
66
^main.c function baz$
7-
^\[baz.1\] line \d+ Check that \*x is assignable: SUCCESS$
8-
^\[baz.2\] line \d+ Check that \*x is assignable: SUCCESS$
9-
^\[baz.3\] line \d+ Check that \*x is assignable: FAILURE$
10-
^\[baz.4\] line \d+ Check that \*x is assignable: FAILURE$
7+
^\[baz.\d+\] line 6 Check that \*x is assignable: SUCCESS$
8+
^\[baz.\d+\] line 6 Check that \*x is assignable: FAILURE$
119
^main.c function foo$
12-
^\[foo.assertion.1\] line \d+ foo.x.set: SUCCESS$
13-
^\[foo.assertion.2\] line \d+ foo.local.set: SUCCESS$
14-
^\[foo.assertion.3\] line \d+ foo.y.set: SUCCESS$
15-
^\[foo.assertion.4\] line \d+ foo.z.set: SUCCESS$
10+
^\[foo.assertion.\d+\] line 20 foo.x.set: SUCCESS$
11+
^\[foo.assertion.\d+\] line 25 foo.local.set: SUCCESS$
12+
^\[foo.assertion.\d+\] line 29 foo.y.set: SUCCESS$
13+
^\[foo.assertion.\d+\] line 33 foo.z.set: SUCCESS$
1614
^VERIFICATION FAILED$
1715
--
1816
--

0 commit comments

Comments
 (0)