Skip to content

Commit 0d242c2

Browse files
author
Remi Delmas
committed
CONTRACTS: support bounded user defined memory-predicates
1 parent 2b40da3 commit 0d242c2

33 files changed

+1387
-110
lines changed
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
void foo(char *arr, size_t size, char *cur)
5+
// clang-format off
6+
__CPROVER_requires(
7+
(0 < size && size < __CPROVER_max_malloc_size) &&
8+
__CPROVER_is_fresh(arr, size) &&
9+
__CPROVER_pointer_in_range_dfcc(arr, cur, arr + size))
10+
__CPROVER_ensures(__CPROVER_pointer_in_range_dfcc(arr, cur, arr + size))
11+
// clang-format on
12+
{
13+
assert(__CPROVER_r_ok(arr, size));
14+
assert(__CPROVER_same_object(arr, cur));
15+
assert(arr <= cur && cur <= arr + size);
16+
}
17+
18+
int main()
19+
{
20+
char *arr;
21+
size_t size;
22+
char *cur;
23+
foo(arr, size, cur);
24+
}
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 _ --pointer-check --pointer-primitive-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Checks that assuming pointer-in-range as preconditions establishes a state
10+
in which the definition of the predicate holds.
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
typedef struct list_t
6+
{
7+
int value;
8+
struct list_t *next;
9+
} list_t;
10+
11+
#define MIN_VALUE -10
12+
#define MAX_VALUE 10
13+
14+
bool is_list(list_t *l, size_t len)
15+
{
16+
if(len == 0)
17+
return l == NULL;
18+
else
19+
return __CPROVER_is_fresh(l, sizeof(*l)) &&
20+
(MIN_VALUE <= l->value && l->value <= MAX_VALUE) &&
21+
is_list(l->next, len - 1);
22+
}
23+
24+
typedef struct buffer_t
25+
{
26+
size_t size;
27+
char *arr;
28+
} buffer_t;
29+
30+
typedef struct double_buffer_t
31+
{
32+
buffer_t *first;
33+
buffer_t *second;
34+
} double_buffer_t;
35+
36+
#define MIN_BUFFER_SIZE 1
37+
#define MAX_BUFFER_SIZE 10
38+
39+
bool is_sized_array(char *arr, size_t size)
40+
{
41+
return (MIN_BUFFER_SIZE <= size && size <= MAX_BUFFER_SIZE) &&
42+
__CPROVER_is_fresh(arr, size);
43+
}
44+
45+
bool is_buffer(buffer_t *b)
46+
{
47+
return __CPROVER_is_fresh(b, sizeof(*b)) && is_sized_array(b->arr, b->size);
48+
}
49+
50+
bool is_double_buffer(double_buffer_t *b)
51+
{
52+
return __CPROVER_is_fresh(b, sizeof(*b)) && is_buffer(b->first) &&
53+
is_buffer(b->second);
54+
}
55+
56+
#define LIST_LEN 3
57+
58+
int foo(list_t *l, double_buffer_t *b)
59+
// clang-format off
60+
__CPROVER_requires(is_list(l, LIST_LEN))
61+
__CPROVER_requires(is_double_buffer(b))
62+
__CPROVER_ensures(
63+
(2 * MIN_BUFFER_SIZE + LIST_LEN * MIN_VALUE) <= __CPROVER_return_value &&
64+
__CPROVER_return_value <= (2 * MAX_BUFFER_SIZE + LIST_LEN * MAX_VALUE))
65+
// clang-format on
66+
{
67+
// access the assumed data structure
68+
return l->value + l->next->value + l->next->next->value + b->first->size +
69+
b->second->size;
70+
}
71+
72+
int main()
73+
{
74+
list_t *l;
75+
double_buffer_t *b;
76+
int return_value = foo(l, b);
77+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
--dfcc main --enforce-contract foo _ --pointer-check --pointer-primitive-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Checks if user-defined predicates work;
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
#include <stdlib.h>
4+
5+
typedef struct list_t
6+
{
7+
int value;
8+
struct list_t *next;
9+
} list_t;
10+
11+
#define MIN_VALUE -10
12+
#define MAX_VALUE 10
13+
14+
bool is_list(list_t *l, size_t len)
15+
{
16+
if(len == 0)
17+
return l == NULL;
18+
else
19+
return __CPROVER_is_fresh(l, sizeof(*l)) &&
20+
(MIN_VALUE <= l->value && l->value <= MAX_VALUE) &&
21+
is_list(l->next, len - 1);
22+
}
23+
24+
typedef struct buffer_t
25+
{
26+
size_t size;
27+
char *arr;
28+
} buffer_t;
29+
30+
typedef struct double_buffer_t
31+
{
32+
buffer_t *first;
33+
buffer_t *second;
34+
} double_buffer_t;
35+
36+
#define MIN_BUFFER_SIZE 1
37+
#define MAX_BUFFER_SIZE 10
38+
39+
bool is_sized_array(char *arr, size_t size)
40+
{
41+
return (MIN_BUFFER_SIZE <= size && size <= MAX_BUFFER_SIZE) &&
42+
__CPROVER_is_fresh(arr, size);
43+
}
44+
45+
bool is_buffer(buffer_t *b)
46+
{
47+
return __CPROVER_is_fresh(b, sizeof(*b)) && is_sized_array(b->arr, b->size);
48+
}
49+
50+
bool is_double_buffer(double_buffer_t *b)
51+
{
52+
return __CPROVER_is_fresh(b, sizeof(*b)) && is_buffer(b->first) &&
53+
is_buffer(b->second);
54+
}
55+
56+
#define LIST_LEN 3
57+
58+
int bar(list_t *l)
59+
// clang-format off
60+
__CPROVER_requires(is_list(l, LIST_LEN))
61+
__CPROVER_ensures(LIST_LEN * MIN_VALUE <= __CPROVER_return_value &&
62+
__CPROVER_return_value <= LIST_LEN * MAX_VALUE)
63+
// clang-format on
64+
{
65+
return l->value + l->next->value + l->next->next->value;
66+
}
67+
68+
int baz(double_buffer_t *b)
69+
// clang-format off
70+
__CPROVER_requires(is_double_buffer(b))
71+
__CPROVER_ensures(2 * MIN_BUFFER_SIZE <= __CPROVER_return_value &&
72+
__CPROVER_return_value <= 2 * MAX_BUFFER_SIZE)
73+
// clang-format on
74+
{
75+
return b->first->size + b->second->size;
76+
}
77+
78+
int foo(list_t *l, double_buffer_t *b)
79+
// clang-format off
80+
__CPROVER_requires(is_list(l, LIST_LEN))
81+
__CPROVER_requires(is_double_buffer(b))
82+
__CPROVER_ensures(
83+
(2 * MIN_BUFFER_SIZE + LIST_LEN * MIN_VALUE) <= __CPROVER_return_value &&
84+
__CPROVER_return_value <= (2 * MAX_BUFFER_SIZE + LIST_LEN * MAX_VALUE))
85+
// clang-format on
86+
{
87+
return bar(l) + baz(b);
88+
}
89+
90+
int main()
91+
{
92+
list_t *l;
93+
double_buffer_t *b;
94+
int return_value = foo(l, b);
95+
}
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 foo --replace-call-with-contract bar --replace-call-with-contract baz _ --pointer-check
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Checks that user defined predicates assumed in requires clauses for checked
10+
contracts build a state such that the same predicates used in requires clauses
11+
for function calls replaced by their contracts hold.

src/ansi-c/c_typecheck_expr.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,6 +2342,17 @@ exprt c_typecheck_baset::do_special_functions(
23422342
// returning nil leaves the call expression in place
23432343
return nil_exprt();
23442344
}
2345+
else if(identifier == CPROVER_PREFIX "pointer_in_range_dfcc")
2346+
{
2347+
// experimental feature for DFCC contracts encodings -- do not use
2348+
if(expr.arguments().size() != 3)
2349+
{
2350+
error().source_location = f_op.source_location();
2351+
error() << "pointer_in_range_dfcc expects three arguments" << eom;
2352+
throw 0;
2353+
}
2354+
return nil_exprt();
2355+
}
23452356
else if(identifier == CPROVER_PREFIX "same_object")
23462357
{
23472358
if(expr.arguments().size()!=2)

src/ansi-c/cprover_builtin_headers.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ __CPROVER_bool __CPROVER_is_freeable(const void *mem);
5050
__CPROVER_bool __CPROVER_was_freed(const void *mem);
5151
__CPROVER_bool __CPROVER_is_fresh(const void *mem, __CPROVER_size_t size);
5252
__CPROVER_bool __CPROVER_obeys_contract(void (*)(void), void (*)(void));
53+
// same as pointer_in_range with experimental support in contracts
54+
__CPROVER_bool __CPROVER_pointer_in_range_dfcc(void *lb, void *ptr, void *ub);
5355
void __CPROVER_old(const void *);
5456
void __CPROVER_loop_entry(const void *);
5557

src/ansi-c/library/cprover_contracts.c

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1283,6 +1283,57 @@ __CPROVER_HIDE:;
12831283
}
12841284
}
12851285

1286+
__CPROVER_size_t __VERIFIER_nondet_size();
1287+
1288+
__CPROVER_bool __CPROVER_contracts_pointer_in_range_dfcc(
1289+
void *lb,
1290+
void **ptr,
1291+
void *ub,
1292+
__CPROVER_contracts_write_set_ptr_t write_set)
1293+
{
1294+
__CPROVER_HIDE:;
1295+
__CPROVER_assert(
1296+
(write_set != 0) & ((write_set->assume_requires_ctx == 1) |
1297+
(write_set->assert_requires_ctx == 1) |
1298+
(write_set->assume_ensures_ctx == 1) |
1299+
(write_set->assert_ensures_ctx == 1)),
1300+
"__CPROVER_pointer_in_range_dfcc is used only in requires or ensures "
1301+
"clauses");
1302+
__CPROVER_assert(__CPROVER_r_ok(lb, 0), "lb pointer must be valid");
1303+
__CPROVER_assert(__CPROVER_r_ok(ub, 0), "ub pointer must be valid");
1304+
__CPROVER_assert(
1305+
__CPROVER_same_object(lb, ub),
1306+
"lb and ub pointers must have the same object");
1307+
__CPROVER_ssize_t lb_offset = __CPROVER_POINTER_OFFSET(lb);
1308+
__CPROVER_ssize_t ub_offset = __CPROVER_POINTER_OFFSET(ub);
1309+
__CPROVER_assert(
1310+
lb_offset <= ub_offset, "lb and ub pointers must be ordered");
1311+
if(write_set->assume_requires_ctx | write_set->assume_ensures_ctx)
1312+
{
1313+
if(__VERIFIER_nondet_CPROVER_bool())
1314+
{
1315+
// add nondet offset
1316+
__CPROVER_size_t offset = __VERIFIER_nondet_size();
1317+
1318+
// this cast is safe because we prove that ub and lb are ordered
1319+
__CPROVER_size_t max_offset = (__CPROVER_size_t)(ub_offset - lb_offset);
1320+
__CPROVER_assume(offset <= max_offset);
1321+
*ptr = (char *)lb + offset;
1322+
return 1;
1323+
}
1324+
else
1325+
{
1326+
return 0;
1327+
}
1328+
}
1329+
else /* write_set->assert_requires_ctx | write_set->assert_ensures_ctx */
1330+
{
1331+
__CPROVER_ssize_t offset = __CPROVER_POINTER_OFFSET(*ptr);
1332+
return __CPROVER_same_object(lb, *ptr) && lb_offset <= offset &&
1333+
offset <= ub_offset;
1334+
}
1335+
}
1336+
12861337
/// \brief Returns the start address of the conditional address range found at
12871338
/// index \p idx in \p set->contract_assigns.
12881339
void *__CPROVER_contracts_write_set_havoc_get_assignable_target(

src/goto-instrument/Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,14 @@ SRC = accelerate/accelerate.cpp \
2222
contracts/dynamic-frames/dfcc_is_fresh.cpp \
2323
contracts/dynamic-frames/dfcc_is_freeable.cpp \
2424
contracts/dynamic-frames/dfcc_obeys_contract.cpp \
25+
contracts/dynamic-frames/dfcc_lift_memory_predicates.cpp \
2526
contracts/dynamic-frames/dfcc_instrument.cpp \
2627
contracts/dynamic-frames/dfcc_spec_functions.cpp \
2728
contracts/dynamic-frames/dfcc_contract_functions.cpp \
2829
contracts/dynamic-frames/dfcc_wrapper_program.cpp \
2930
contracts/dynamic-frames/dfcc_contract_handler.cpp \
3031
contracts/dynamic-frames/dfcc_swap_and_wrap.cpp \
32+
contracts/dynamic-frames/dfcc_pointer_in_range.cpp \
3133
contracts/dynamic-frames/dfcc.cpp \
3234
contracts/havoc_assigns_clause_targets.cpp \
3335
contracts/instrument_spec_assigns.cpp \

src/goto-instrument/contracts/doc/developer/contracts-dev-arch.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ Each of these translation passes is implemented in a specific class:
2828
:-------------------------------|:---------------------------------------
2929
@ref dfcc_instrumentt | Implements @ref contracts-dev-spec-dfcc for @ref goto_functiont, @ref goto_programt, or subsequences of instructions of @ref goto_programt
3030
@ref dfcc_is_fresht | Implements @ref contracts-dev-spec-is-fresh
31+
@ref dfcc_pointer_in_ranget | Implements @ref contracts-dev-spec-pointer-in-range
32+
@ref dfcc_lift_memory_predicatest | Implements @ref contracts-dev-spec-memory-predicates-rewriting
3133
@ref dfcc_is_freeablet | Implements @ref contracts-dev-spec-is-freeable
3234
@ref dfcc_obeys_contractt | Implements @ref contracts-dev-spec-obeys-contract
3335
@ref dfcc_spec_functionst | Implements @ref contracts-dev-spec-spec-rewriting

src/goto-instrument/contracts/doc/developer/contracts-dev-spec-dfcc-instrument.md

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,12 +162,17 @@ skip_target: SKIP;
162162
CALL __CPROVER_deallocate(ptr);
163163
```
164164
165-
Calls to `__CPROVER_was_freed` or `__CPROVER_is_freeable` are rewritten as described
166-
in @subpage contracts-dev-spec-is-freeable
165+
Calls to `__CPROVER_was_freed` or `__CPROVER_is_freeable` are rewritten as
166+
described in @subpage contracts-dev-spec-is-freeable
167167
168-
Calls to `__CPROVER_is_fresh` are rewritten as described in @subpage contracts-dev-spec-is-fresh
168+
Calls to `__CPROVER_is_fresh` are rewritten as described in
169+
@subpage contracts-dev-spec-is-fresh
169170
170-
Calls to `__CPROVER_obeys_contract` are rewritten as described in @subpage contracts-dev-spec-obeys-contract
171+
Calls to `__CPROVER_obeys_contract` are rewritten as described in
172+
@subpage contracts-dev-spec-obeys-contract
173+
174+
Calls to `__CPROVER_pointer_in_range_dfcc` are rewritten as described in
175+
@subpage contracts-dev-spec-pointer-in-range
171176
172177
For all other function or function pointer calls, we proceed as follows.
173178

0 commit comments

Comments
 (0)