Skip to content

Commit 1ace3d4

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

29 files changed

+1244
-110
lines changed
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+
typedef struct list_t
5+
{
6+
int value;
7+
struct list_t *next;
8+
} list_t;
9+
10+
// true iff list of len max_len with values in [-10,10]
11+
bool is_list(list_t *l, size_t len)
12+
{
13+
if(len == 0)
14+
return l == NULL;
15+
else
16+
return __CPROVER_is_fresh(l, sizeof(*l)) &&
17+
(-10 <= l->value && l->value <= 10) && is_list(l->next, len - 1);
18+
}
19+
20+
typedef struct buffer_t
21+
{
22+
size_t size;
23+
char *arr;
24+
char *cursor;
25+
} buffer_t;
26+
27+
typedef struct double_buffer_t
28+
{
29+
buffer_t *first;
30+
buffer_t *second;
31+
} double_buffer_t;
32+
33+
bool is_sized_array(char *arr, size_t size)
34+
{
35+
return __CPROVER_is_fresh(arr, size);
36+
}
37+
38+
bool is_ranged_cursor(char *cursor, char *arr, size_t size)
39+
{
40+
void *lb = arr;
41+
void *ub = arr + size;
42+
__CPROVER_pointer_in_range_dfcc(lb, cursor, ub);
43+
}
44+
45+
bool is_buffer(buffer_t *b)
46+
{
47+
return __CPROVER_is_fresh(b, sizeof(*b)) && (0 < b->size && b->size <= 10) &&
48+
is_sized_array(b->arr, b->size) &&
49+
is_ranged_cursor(b->cursor, b->arr, b->size);
50+
}
51+
52+
bool is_double_buffer(double_buffer_t *b)
53+
{
54+
return __CPROVER_is_fresh(b, sizeof(*b)) && is_buffer(b->first) &&
55+
is_buffer(b->second);
56+
}
57+
58+
int foo(list_t *l, double_buffer_t *b)
59+
// clang-format off
60+
__CPROVER_requires(is_list(l, 3))
61+
__CPROVER_requires(is_double_buffer(b))
62+
__CPROVER_ensures(-28 <= __CPROVER_return_value &&
63+
__CPROVER_return_value <= 50)
64+
// clang-format on
65+
{
66+
// access the assumed data structure
67+
return l->value + l->next->value + l->next->next->value + b->first->size +
68+
b->second->size;
69+
}
70+
71+
int main()
72+
{
73+
list_t *l;
74+
double_buffer_t *b;
75+
int return_value = foo(l, b);
76+
assert(-28 <= return_value && return_value <= 50);
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
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
--
9+
Checks if user-defined predicates work;

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)