Skip to content

Commit 3671e58

Browse files
author
Remi Delmas
committed
Function contracts: preliminary support for conditional targets in assigns clauses.
Conditional targets specify memory locations that can be assigned if and only if a condition holds when a function is invoked. - Modify ansi-c parser and typechecker to accept the new target syntax `c ? {t1, ..., tn}`, `c ? t` - Add an explicit expression to the internal representation of assigns clause targets - Translate conditions expressions to clean GOTO instructions using `goto-convert::clean_expr` - Change the validity condition of a CAR to `guard && all_deref_valid(target) && rw_ok(target, size)` for assigns clause checking - Update some tests, add new tests for the feature
1 parent 8cd1447 commit 3671e58

File tree

51 files changed

+938
-195
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

51 files changed

+938
-195
lines changed

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: non-lvalue target in assigns clause$
6+
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
77
^CONVERSION ERROR$
88
--
99
--
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+
bool cond()
6+
{
7+
return true;
8+
}
9+
10+
int foo(int a, int *x, int *y) __CPROVER_assigns(a && cond() ? *x)
11+
{
12+
if(a)
13+
{
14+
if(cond())
15+
*x = 0; // must pass
16+
}
17+
else
18+
{
19+
*x = 0; // must fail
20+
}
21+
return 0;
22+
}
23+
24+
int main()
25+
{
26+
bool a;
27+
int x;
28+
int y;
29+
30+
foo(a, &x, &y);
31+
return 0;
32+
}
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
4+
^main.c function foo$
5+
^\[foo\.\d+\] line 15 Check that \*x is assignable: SUCCESS$
6+
^\[foo\.\d+\] line 19 Check that \*x is assignable: FAILURE$
7+
^VERIFICATION FAILED$
8+
^EXIT=10$
9+
^SIGNAL=0$
10+
--
11+
--
12+
Checks that function calls are allowed in conditions.
13+
Remark: we allow function calls without further checks for now because they
14+
are mandatory for some applications.
15+
The next step must be to check that the called functions have a contract
16+
with an empty assigns clause and that they indeed satisfy their contract
17+
using a CI check.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
int foo(bool a, int *x, int *y) __CPROVER_assigns(a ? *x, !a ? *y)
6+
{
7+
if(a)
8+
{
9+
*x = 0; // must pass
10+
*y = 0; // must fail
11+
}
12+
else
13+
{
14+
*x = 0; // must fail
15+
*y = 0; // must pass
16+
}
17+
18+
*x = 0; // must fail
19+
*y = 0; // must fail
20+
return 0;
21+
}
22+
23+
int main()
24+
{
25+
bool a;
26+
int x;
27+
int y;
28+
29+
foo(a, &x, &y);
30+
return 0;
31+
}
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
4+
main.c function foo
5+
^\[foo\.\d+\] line 9 Check that \*x is assignable: SUCCESS$
6+
^\[foo\.\d+\] line 10 Check that \*y is assignable: FAILURE$
7+
^\[foo\.\d+\] line 14 Check that \*x is assignable: FAILURE$
8+
^\[foo\.\d+\] line 15 Check that \*y is assignable: SUCCESS$
9+
^\[foo\.\d+\] line 18 Check that \*x is assignable: FAILURE$
10+
^\[foo\.\d+\] line 19 Check that \*y is assignable: FAILURE$
11+
^VERIFICATION FAILED$
12+
^EXIT=10$
13+
^SIGNAL=0$
14+
--
15+
--
16+
Check that conditional assigns clause `c ? lvalue`
17+
match with control flow path conditions.

regression/contracts/assigns_enforce_elvis_1/main.c renamed to regression/contracts/assigns_enforce_conditional_lvalue_list/main.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,21 @@
22
#include <stdbool.h>
33
#include <stdlib.h>
44

5-
int foo(bool a, int *x, int *y, int *z) __CPROVER_assigns(*(a ? x : y))
5+
int foo(bool a, int *x, int *y) __CPROVER_assigns(a ? {*x, *y})
66
{
77
if(a)
8+
{
89
*x = 0; // must pass
9-
else
1010
*y = 0; // must pass
11+
}
12+
else
13+
{
14+
*x = 0; // must fail
15+
*y = 0; // must fail
16+
}
17+
18+
*x = 0; // must fail
19+
*y = 0; // must fail
1120
return 0;
1221
}
1322

@@ -16,8 +25,7 @@ int main()
1625
bool a;
1726
int x;
1827
int y;
19-
int z;
2028

21-
foo(a, &x, &y, &z);
29+
foo(a, &x, &y);
2230
return 0;
2331
}
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
4+
main.c function foo
5+
^\[foo\.\d+\] line 9 Check that \*x is assignable: SUCCESS$
6+
^\[foo\.\d+\] line 10 Check that \*y is assignable: SUCCESS$
7+
^\[foo\.\d+\] line 14 Check that \*x is assignable: FAILURE$
8+
^\[foo\.\d+\] line 15 Check that \*y is assignable: FAILURE$
9+
^\[foo\.\d+\] line 18 Check that \*x is assignable: FAILURE$
10+
^\[foo\.\d+\] line 19 Check that \*y is assignable: FAILURE$
11+
^VERIFICATION FAILED$
12+
^EXIT=10$
13+
^SIGNAL=0$
14+
--
15+
--
16+
Check that conditional assigns clause `c ? {lvalue, ..}`
17+
match with control flow path conditions.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
int foo(bool a, int *x, int *y) __CPROVER_assigns(a ? *x + *y)
6+
{
7+
return 0;
8+
}
9+
10+
int main()
11+
{
12+
bool a;
13+
int x;
14+
int y;
15+
16+
foo(a, &x, &y);
17+
return 0;
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--enforce-contract foo
4+
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
5+
^CONVERSION ERROR
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that non-lvalue targets are rejected from conditional targets.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
int foo(bool a, int *x, int *y) __CPROVER_assigns(a ? {*x + *y, *x})
6+
{
7+
return 0;
8+
}
9+
10+
int main()
11+
{
12+
bool a;
13+
int x;
14+
int y;
15+
16+
foo(a, &x, &y);
17+
return 0;
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--enforce-contract foo
4+
^.*error: assigns clause target must be an lvalue or __CPROVER_POINTER_OBJECT expression$
5+
^CONVERSION ERROR
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that non-lvalue targets are rejected from conditional targets.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
int foo(bool a, char *x, char *y)
6+
// clang-format off
7+
__CPROVER_assigns(
8+
a ? __CPROVER_POINTER_OBJECT(x),
9+
!a ? __CPROVER_POINTER_OBJECT(y)
10+
)
11+
// clang-format on
12+
{
13+
if(a)
14+
{
15+
x[0] = 0; // must pass
16+
y[0] = 0; // must fail
17+
}
18+
else
19+
{
20+
x[0] = 0; // must fail
21+
y[0] = 0; // must pass
22+
}
23+
24+
x[0] = 0; // must fail
25+
y[0] = 0; // must fail
26+
return 0;
27+
}
28+
29+
int main()
30+
{
31+
bool a;
32+
char x[2];
33+
char y[2];
34+
35+
foo(a, &x, &y);
36+
return 0;
37+
}
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
4+
main.c function foo
5+
^\[foo\.\d+\] line 15 Check that x\[\(signed (long )?long int\)0\] is assignable: SUCCESS$
6+
^\[foo\.\d+\] line 16 Check that y\[\(signed (long )?long int\)0\] is assignable: FAILURE$
7+
^\[foo\.\d+\] line 20 Check that x\[\(signed (long )?long int\)0\] is assignable: FAILURE$
8+
^\[foo\.\d+\] line 21 Check that y\[\(signed (long )?long int\)0\] is assignable: SUCCESS$
9+
^\[foo\.\d+\] line 24 Check that x\[\(signed (long )?long int\)0\] is assignable: FAILURE$
10+
^\[foo\.\d+\] line 25 Check that y\[\(signed (long )?long int\)0\] is assignable: FAILURE$
11+
^VERIFICATION FAILED$
12+
^EXIT=10$
13+
^SIGNAL=0$
14+
--
15+
--
16+
Check that conditional assigns clause `c ? __CPROVER_POINTER_OBJECT((p)`
17+
match with control flow path conditions.
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
int foo(bool a, char *x, char *y)
6+
// clang-format off
7+
__CPROVER_assigns(
8+
a ? {__CPROVER_POINTER_OBJECT(x), __CPROVER_POINTER_OBJECT(y)}
9+
)
10+
// clang-format on
11+
{
12+
if(a)
13+
{
14+
x[0] = 0; // must pass
15+
y[0] = 0; // must pass
16+
}
17+
else
18+
{
19+
x[0] = 0; // must fail
20+
y[0] = 0; // must fail
21+
}
22+
23+
x[0] = 0; // must fail
24+
y[0] = 0; // must fail
25+
return 0;
26+
}
27+
28+
int main()
29+
{
30+
bool a;
31+
char x[2];
32+
char y[2];
33+
34+
foo(a, &x, &y);
35+
return 0;
36+
}
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
4+
main.c function foo
5+
^\[foo\.\d+\] line 14 Check that x\[\(signed (long )?long int\)0\] is assignable: SUCCESS$
6+
^\[foo\.\d+\] line 15 Check that y\[\(signed (long )?long int\)0\] is assignable: SUCCESS$
7+
^\[foo\.\d+\] line 19 Check that x\[\(signed (long )?long int\)0\] is assignable: FAILURE$
8+
^\[foo\.\d+\] line 20 Check that y\[\(signed (long )?long int\)0\] is assignable: FAILURE$
9+
^\[foo\.\d+\] line 23 Check that x\[\(signed (long )?long int\)0\] is assignable: FAILURE$
10+
^\[foo\.\d+\] line 24 Check that y\[\(signed (long )?long int\)0\] is assignable: FAILURE$
11+
^VERIFICATION FAILED$
12+
^EXIT=10$
13+
^SIGNAL=0$
14+
--
15+
--
16+
Check that conditional assigns clause `c ? {__CPROVER_POINTER_OBJECT((p), .. }`
17+
match with control flow path conditions.

regression/contracts/assigns_enforce_elvis_5/main.c renamed to regression/contracts/assigns_enforce_conditional_side_effect_condition/main.c

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,16 +2,12 @@
22
#include <stdbool.h>
33
#include <stdlib.h>
44

5-
int foo(bool a, int *x, int *y) __CPROVER_assigns((a ? *x : *y))
5+
int foo(int a, int *x, int *y) __CPROVER_assigns(a++ ? *x)
66
{
77
if(a)
88
{
99
*x = 0;
1010
}
11-
else
12-
{
13-
*y = 0;
14-
}
1511
return 0;
1612
}
1713

@@ -21,6 +17,6 @@ int main()
2117
int x;
2218
int y;
2319

24-
foo(true, &x, &y);
20+
foo(a, &x, &y);
2521
return 0;
2622
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--enforce-contract foo
4+
^.* error: side-effects not allowed in assigns clause conditions$
5+
^CONVERSION ERROR$
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that side-effect expressions in target conditions cause hard errors.
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
int foo(bool a, int *x, int *y) __CPROVER_assigns(a ? *x++)
6+
{
7+
return 0;
8+
}
9+
10+
int main()
11+
{
12+
bool a;
13+
int x;
14+
int y;
15+
16+
foo(a, &x, &y);
17+
return 0;
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--enforce-contract foo
4+
^.* error: side-effects not allowed in assigns clause targets$
5+
^CONVERSION ERROR$
6+
^EXIT=(1|64)$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that side-effect expressions are rejected from conditional targets.

0 commit comments

Comments
 (0)