Skip to content

Commit 50a54f4

Browse files
author
Remi Delmas
committed
CONTRACTS: add __CPROVER_obeys_contract predicate
This predicate can be used in requires and ensures clauses to express that a function pointer satisfies a given contract. The predicate is implemented in cprover_contracts.c and turns into an assignment in assumption contexts and to an equality check in assertions contexts. The dfcc_instrumentt methods were updated to report any occurrence of the predicate discovered when instrumenting GOTO functions.
1 parent 8848ad8 commit 50a54f4

Some content is hidden

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

48 files changed

+1266
-119
lines changed
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
// type of functions that manipulate arrays
5+
typedef void (*arr_fun_t)(char *arr, size_t size);
6+
7+
// A contract for the arr_fun_t type
8+
// requires a fresh array and positive size
9+
// resets the first element to zero
10+
void arr_fun_contract(char *arr, size_t size)
11+
// clang-format off
12+
__CPROVER_requires(size > 0 && __CPROVER_is_fresh(arr, size))
13+
__CPROVER_assigns(arr[0])
14+
__CPROVER_ensures(arr[0] == 0)
15+
// clang-format on
16+
;
17+
18+
// Testing pre-conditions constructs
19+
// Takes a function pointer as input, uses it if its preconditions are met
20+
// to establish post-conditions
21+
int foo(char *arr, size_t size, arr_fun_t arr_fun)
22+
// clang-format off
23+
__CPROVER_requires(arr == NULL || __CPROVER_is_fresh(arr, size))
24+
__CPROVER_requires(__CPROVER_obeys_contract(arr_fun, arr_fun_contract))
25+
__CPROVER_assigns(arr && size > 0 : arr[0])
26+
__CPROVER_ensures((__CPROVER_return_value == 0) ==> (arr[0] == 0))
27+
// clang-format on
28+
{
29+
int retval = -1;
30+
if(arr && size > 0)
31+
{
32+
CALL:
33+
arr_fun(arr, size);
34+
retval = 0;
35+
}
36+
return retval;
37+
}
38+
39+
void main()
40+
{
41+
size_t size;
42+
char *arr;
43+
arr_fun_t fun;
44+
foo(arr, size, fun);
45+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--restrict-function-pointer foo.CALL/arr_fun_contract --dfcc main --enforce-contract foo
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Checks that when a function taking a function pointer as input satisfying a given
10+
contract is checked against its contract, the function pointer can be called
11+
like a regular function pointer and establishes the post conditions specified
12+
by its contract.
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
// type of functions that manipulate arrays
5+
typedef void (*arr_fun_t)(char *arr, size_t size);
6+
7+
// A contract for the arr_fun_t type
8+
void arr_fun_contract(char *arr, size_t size)
9+
// clang-format off
10+
__CPROVER_requires((size > 0 && __CPROVER_is_fresh(arr, size)))
11+
__CPROVER_assigns(arr[0])
12+
__CPROVER_ensures(arr[0] == 0)
13+
// clang-format on
14+
;
15+
16+
arr_fun_t foo(arr_fun_t infun, arr_fun_t *outfun)
17+
// clang-format off
18+
__CPROVER_requires(__CPROVER_obeys_contract(infun, arr_fun_contract))
19+
__CPROVER_ensures(__CPROVER_obeys_contract(*outfun, arr_fun_contract))
20+
__CPROVER_ensures(__CPROVER_obeys_contract(__CPROVER_return_value, arr_fun_contract))
21+
// clang-format on
22+
{
23+
*outfun = arr_fun_contract;
24+
return infun;
25+
}
26+
27+
void main()
28+
{
29+
// establish pre-conditions before replacement
30+
arr_fun_t infun = arr_fun_contract;
31+
32+
arr_fun_t outfun1 = NULL;
33+
arr_fun_t outfun2 = foo(infun, &outfun1);
34+
35+
// checking post-conditions after replacement
36+
assert(outfun1 == arr_fun_contract);
37+
assert(outfun2 == arr_fun_contract);
38+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
CORE
2+
main.c
3+
--dfcc main --replace-call-with-contract foo
4+
^\[main.assertion.\d+\].*assertion outfun1 == arr_fun_contract: SUCCESS$
5+
^\[main.assertion.\d+\].*assertion outfun2 == arr_fun_contract: SUCCESS$
6+
^EXIT=0$
7+
^SIGNAL=0$
8+
^VERIFICATION SUCCESSFUL$
9+
--
10+
--
11+
This test checks that, when replacing a call by its contract,
12+
requires_contract clauses are translated to equality checks
13+
and that ensures_contract are translated to assignments of the function pointer
14+
variable to the contract symbol.
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
#include <stdlib.h>
2+
3+
// Type of functions that manipulate arrays of a given size
4+
typedef void (*arr_fun_t)(char *arr, size_t size);
5+
6+
// A contract for the arr_fun_t type
7+
void arr_fun_contract(char *arr, size_t size)
8+
// clang-format off
9+
__CPROVER_requires(size > 0 && __CPROVER_is_fresh(arr, size))
10+
__CPROVER_assigns(arr[0]) __CPROVER_ensures(arr[0] == 0)
11+
// clang-format on
12+
;
13+
14+
// Uses a function pointer
15+
int foo(char *arr, size_t size, arr_fun_t arr_fun)
16+
// clang-format off
17+
__CPROVER_requires(arr ==> __CPROVER_is_fresh(arr, size))
18+
__CPROVER_requires(
19+
arr_fun ==> __CPROVER_obeys_contract(arr_fun, arr_fun_contract))
20+
__CPROVER_assigns(arr && size > 0 && arr_fun: arr[0])
21+
__CPROVER_ensures(__CPROVER_return_value ==> (arr[0] == 0))
22+
// clang-format on
23+
{
24+
if(arr && size > 0 && arr_fun)
25+
{
26+
CALL:
27+
arr_fun(arr, size);
28+
return 1;
29+
}
30+
return 0;
31+
}
32+
33+
// returns function pointer satisfying arr_fun_contract
34+
arr_fun_t get_arr_fun()
35+
// clang-format off
36+
__CPROVER_ensures(
37+
__CPROVER_obeys_contract(__CPROVER_return_value, arr_fun_contract))
38+
// clang-format on
39+
;
40+
41+
// allocates an array and uses get_arr_fun and foo to initialise it
42+
char *bar(size_t size)
43+
// clang-format off
44+
__CPROVER_ensures(
45+
__CPROVER_return_value ==> size > 0 &&
46+
__CPROVER_is_fresh(__CPROVER_return_value, size) &&
47+
__CPROVER_return_value[0] == 0)
48+
// clang-format on
49+
{
50+
if(size > 0)
51+
{
52+
char *arr;
53+
arr = malloc(size);
54+
if(arr && foo(arr, size, get_arr_fun()))
55+
return arr;
56+
57+
return NULL;
58+
}
59+
return NULL;
60+
}
61+
62+
void main()
63+
{
64+
size_t size;
65+
char *arr = bar(size);
66+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
CORE
2+
main.c
3+
--dfcc main --enforce-contract bar --replace-call-with-contract foo --replace-call-with-contract get_arr_fun
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Checks that when a function returning a function pointer satisfying a given
10+
contract is replaced by its contract, the returned function pointer can be
11+
passed to another function requiring the same contract.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
// type of functions that manipulate arrays
5+
typedef void (*fun_t)(char *arr, size_t size);
6+
7+
void contract(char *arr, size_t size);
8+
9+
int foo(char *arr, size_t size, fun_t fun)
10+
__CPROVER_requires(__CPROVER_obeys_contract(fun, contract, contract))
11+
{
12+
return 0;
13+
}
14+
15+
void main()
16+
{
17+
size_t size;
18+
char *arr;
19+
fun_t fun;
20+
foo(arr, size, fun);
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--dfcc main --enforce-contract foo
4+
^.*error: expected 2 arguments for __CPROVER_obeys_contract, found 3$
5+
^CONVERSION ERROR$
6+
^EXIT=1$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that malformed __CPROVER_obeys_contract predicate expressions are rejected.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
// type of functions that manipulate arrays
5+
typedef void (*fun_t)(char *arr, size_t size);
6+
7+
void contract(char *arr, size_t size);
8+
9+
int foo(char *arr, size_t size, fun_t fun)
10+
__CPROVER_requires(__CPROVER_obeys_contract(true ? fun : fun, contract))
11+
{
12+
return 0;
13+
}
14+
15+
void main()
16+
{
17+
size_t size;
18+
char *arr;
19+
fun_t fun;
20+
foo(arr, size, fun);
21+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--dfcc main --enforce-contract foo
4+
^.*error: the first argument of __CPROVER_obeys_contract must have no ternary operator$
5+
^CONVERSION ERROR$
6+
^EXIT=1$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that malformed __CPROVER_obeys_contract predicate expressions are rejected.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
// type of functions that manipulate arrays
5+
typedef void (*fun_t)(char *arr, size_t size);
6+
7+
void contract(char *arr, size_t size);
8+
9+
typedef void (*voidfun_t)(void);
10+
11+
voidfun_t identity(voidfun_t fun)
12+
{
13+
return fun;
14+
}
15+
16+
int foo(char *arr, size_t size, fun_t fun)
17+
__CPROVER_requires(__CPROVER_obeys_contract(identity(fun), contract))
18+
{
19+
return 0;
20+
}
21+
22+
void main()
23+
{
24+
size_t size;
25+
char *arr;
26+
fun_t fun;
27+
foo(arr, size, fun);
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--dfcc main --enforce-contract foo
4+
^.*error: the first argument of __CPROVER_obeys_contract must be a function pointer lvalue expression$
5+
^CONVERSION ERROR$
6+
^EXIT=1$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that malformed __CPROVER_obeys_contract predicate expressions are rejected.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
// type of functions that manipulate arrays
5+
typedef void (*fun_t)(char *arr, size_t size);
6+
7+
void contract(char *arr, size_t size, bool b);
8+
9+
typedef void (*voidfun_t)(void);
10+
11+
voidfun_t identity(voidfun_t fun)
12+
{
13+
return fun;
14+
}
15+
16+
int foo(char *arr, size_t size, fun_t fun)
17+
__CPROVER_requires(__CPROVER_obeys_contract(fun, contract))
18+
{
19+
return 0;
20+
}
21+
22+
void main()
23+
{
24+
size_t size;
25+
char *arr;
26+
fun_t fun;
27+
foo(arr, size, fun);
28+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
main.c
3+
--dfcc main --enforce-contract foo
4+
^.*error: the first and second arguments of __CPROVER_obeys_contract must have the same function pointer type$
5+
^CONVERSION ERROR$
6+
^EXIT=1$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Checks that malformed __CPROVER_obeys_contract predicate expressions are rejected.
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
// type of functions that manipulate arrays
5+
typedef void (*arr_fun_t)(char *arr, size_t size);
6+
7+
// A contract for the arr_fun_t type
8+
// requires a fresh array and positive size
9+
// resets the first element to zero
10+
void arr_fun_contract(char *arr, size_t size)
11+
// clang-format off
12+
__CPROVER_requires(size > 0 && __CPROVER_is_fresh(arr, size))
13+
__CPROVER_assigns(arr[0])
14+
__CPROVER_ensures(arr[0] == 0)
15+
;
16+
17+
// Testing pre-conditions constructs
18+
// Takes a function pointer as input, uses it if its preconditions are met
19+
// to establish post-conditions
20+
int foo(char *arr, size_t size, arr_fun_t arr_fun)
21+
// clang-format off
22+
__CPROVER_requires(arr == NULL || __CPROVER_is_fresh(arr, size))
23+
__CPROVER_requires(__CPROVER_obeys_contract(arr_fun, arr_fun_contract))
24+
__CPROVER_assigns(arr && size > 0 : arr[0])
25+
__CPROVER_ensures((__CPROVER_return_value == 0) ==> (arr[0] == 0))
26+
// clang-format on
27+
{
28+
int retval = -1;
29+
if(arr && size > 0)
30+
{
31+
CALL:
32+
arr_fun(arr, size);
33+
retval = 0;
34+
}
35+
return retval;
36+
}
37+
38+
void main()
39+
{
40+
size_t size;
41+
char *arr;
42+
arr_fun_t fun;
43+
foo(arr, size, fun);
44+
}
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 --restrict-function-pointer foo.CALL/arr_fun_contract --replace-call-with-contract arr_fun_contract
4+
^file main.c line 23 function foo: __CPROVER_obeys_contract is not supported in this version
5+
^EXIT=6$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This test checks uses of the unsupported __CPROVER_obeys_contract predicate
10+
trigger errors.

0 commit comments

Comments
 (0)