Skip to content

Commit f5cbc22

Browse files
author
Remi Delmas
committed
Use demonic tracking for fresh ID set, check pointer predicate uniqueness
Replaces the array used to track fresh object IDs with a single nondet variable. Add another nondet variable to track locations that store pointers on which some pointer predicate was assumed or successfully asserted, and ensures that at most one pointer predicate is established at any time. Allows ensures clauses to establish different predicates wrt ensures, by resetting the nondet tracker between requires and ensures clauses evaluation in the contract wrapper.
1 parent 0b1231c commit f5cbc22

File tree

5 files changed

+180
-59
lines changed

5 files changed

+180
-59
lines changed

src/ansi-c/library/cprover_contracts.c

+153-59
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ typedef struct
6262
/// \brief Array of void *pointers, indexed by their object ID
6363
/// or some other order
6464
void **elems;
65+
/// \brief Equal to 1 if `fresh_id` is empty, 0 otherwise.
66+
unsigned char fresh_id_empty;
67+
/// \brief Demonic nondet variable ranging over the set of fresh object IDs.
68+
__CPROVER_size_t fresh_id;
69+
/// \brief Equal to 1 if `ptr_pred` is empty, 0 otherwise.
70+
unsigned char ptr_pred_empty;
71+
/// \brief Demonic nondet variable ranging over the set of locations storing
72+
/// pointers on which predicates were assumed/asserted.
73+
void **ptr_pred;
6574
} __CPROVER_contracts_obj_set_t;
6675

6776
/// \brief Type of pointers to \ref __CPROVER_contracts_car_set_t.
@@ -261,7 +270,11 @@ __CPROVER_HIDE:;
261270
.nof_elems = 0,
262271
.is_empty = 1,
263272
.indexed_by_object_id = 1,
264-
.elems = __CPROVER_allocate(nof_objects * sizeof(*(set->elems)), 1)};
273+
.elems = __CPROVER_allocate(nof_objects * sizeof(*(set->elems)), 1),
274+
.fresh_id_empty = 1,
275+
.fresh_id = 0,
276+
.ptr_pred_empty = 1,
277+
.ptr_pred = 0};
265278
}
266279

267280
/// \brief Initialises a \ref __CPROVER_contracts_obj_set_t object to use it
@@ -285,7 +298,74 @@ __CPROVER_HIDE:;
285298
.nof_elems = 0,
286299
.is_empty = 1,
287300
.indexed_by_object_id = 0,
288-
.elems = __CPROVER_allocate(max_elems * sizeof(*(set->elems)), 1)};
301+
.elems = __CPROVER_allocate(max_elems * sizeof(*(set->elems)), 1),
302+
.fresh_id_empty = 1,
303+
.fresh_id = 0,
304+
.ptr_pred_empty = 1,
305+
.ptr_pred = 0};
306+
}
307+
308+
__CPROVER_bool __CPROVER_contracts_not_seen_is_fresh(
309+
__CPROVER_contracts_obj_set_ptr_t set,
310+
void *ptr)
311+
{
312+
__CPROVER_HIDE:;
313+
return set->fresh_id_empty || __CPROVER_POINTER_OBJECT(ptr) != set->fresh_id;
314+
}
315+
316+
void __CPROVER_contracts_record_fresh_id(
317+
__CPROVER_contracts_obj_set_ptr_t set,
318+
void *ptr)
319+
{
320+
__CPROVER_HIDE:;
321+
if(set->fresh_id_empty)
322+
{
323+
set->fresh_id_empty = 0;
324+
set->fresh_id = __CPROVER_POINTER_OBJECT(ptr);
325+
}
326+
else
327+
{
328+
set->fresh_id = __VERIFIER_nondet___CPROVER_bool()
329+
? __CPROVER_POINTER_OBJECT(ptr)
330+
: set->fresh_id;
331+
}
332+
}
333+
334+
__CPROVER_bool __CPROVER_contracts_check_ptr_not_in_ptr_pred(
335+
__CPROVER_contracts_obj_set_ptr_t set,
336+
void **ptr)
337+
{
338+
__CPROVER_HIDE:;
339+
return set->ptr_pred_empty || ptr != set->ptr_pred;
340+
}
341+
342+
/// \brief Resets the nondet tracker for pointer predicates in \p set.
343+
/// Invoked right before evaluating contract ensures clauses. Since functions
344+
/// are allowed to modify the program state, resetting the tracker allows
345+
/// ensures clauses to establish a different pointer predicate on a pointer that
346+
/// was already targeted by the requires clause.
347+
void __CPROVER_contracts_obj_set_reset_ptr_pred(
348+
__CPROVER_contracts_obj_set_ptr_t set)
349+
{
350+
__CPROVER_HIDE:;
351+
set->ptr_pred_empty = 1;
352+
set->ptr_pred = 0;
353+
}
354+
355+
void __CPROVER_contracts_record_ptr_in_ptr_pred(
356+
__CPROVER_contracts_obj_set_ptr_t set,
357+
void **ptr)
358+
{
359+
__CPROVER_HIDE:;
360+
if(set->ptr_pred_empty)
361+
{
362+
set->ptr_pred_empty = 0;
363+
set->ptr_pred = ptr;
364+
}
365+
else
366+
{
367+
set->ptr_pred = __VERIFIER_nondet___CPROVER_bool() ? ptr : set->ptr_pred;
368+
}
289369
}
290370

291371
/// @brief Releases resources used by \p set.
@@ -1202,7 +1282,13 @@ __CPROVER_HIDE:;
12021282
}
12031283
__CPROVER_assert(
12041284
(ptr2 == 0) || __CPROVER_r_ok(ptr2, 0),
1205-
"pointer_equals is only called with valid pointers");
1285+
"__CPROVER_pointer_equals is only called with valid pointers");
1286+
__CPROVER_assert(
1287+
__CPROVER_contracts_check_ptr_not_in_ptr_pred(
1288+
write_set->linked_is_fresh, ptr1),
1289+
"__CPROVER_pointer_equals does not conflict with other predicate");
1290+
__CPROVER_contracts_record_ptr_in_ptr_pred(
1291+
write_set->linked_is_fresh, ptr1);
12061292
*ptr1 = ptr2;
12071293
return 1;
12081294
}
@@ -1211,11 +1297,21 @@ __CPROVER_HIDE:;
12111297
void *derefd = *ptr1;
12121298
__CPROVER_assert(
12131299
(derefd == 0) || __CPROVER_r_ok(derefd, 0),
1214-
"pointer_equals is only called with valid pointers");
1300+
"__CPROVER_pointer_equals is only called with valid pointers");
12151301
__CPROVER_assert(
12161302
(ptr2 == 0) || __CPROVER_r_ok(ptr2, 0),
1217-
"pointer_equals is only called with valid pointers");
1218-
return derefd == ptr2;
1303+
"__CPROVER_pointer_equals is only called with valid pointers");
1304+
if(derefd != ptr2)
1305+
{
1306+
return 0;
1307+
}
1308+
__CPROVER_assert(
1309+
__CPROVER_contracts_check_ptr_not_in_ptr_pred(
1310+
write_set->linked_is_fresh, ptr1),
1311+
"__CPROVER_pointer_equals does not conflict with other predicate");
1312+
__CPROVER_contracts_record_ptr_in_ptr_pred(
1313+
write_set->linked_is_fresh, ptr1);
1314+
return 1;
12191315
}
12201316
}
12211317

@@ -1294,9 +1390,15 @@ __CPROVER_HIDE:;
12941390
__CPROVER_contracts_make_invalid_pointer(elem);
12951391
return 0;
12961392
}
1297-
12981393
void *ptr = __CPROVER_allocate(size, 0);
12991394
*elem = ptr;
1395+
__CPROVER_assert(
1396+
__CPROVER_contracts_check_ptr_not_in_ptr_pred(
1397+
write_set->linked_is_fresh, elem),
1398+
"__CPROVER_is_fresh does not conflict with other predicate");
1399+
__CPROVER_contracts_record_ptr_in_ptr_pred(
1400+
write_set->linked_is_fresh, elem);
1401+
__CPROVER_contracts_record_fresh_id(write_set->linked_is_fresh, ptr);
13001402

13011403
// record the object size for non-determistic bounds checking
13021404
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
@@ -1308,18 +1410,6 @@ __CPROVER_HIDE:;
13081410
// __CPROVER_bool record_may_leak = __VERIFIER_nondet___CPROVER_bool();
13091411
// __CPROVER_memory_leak = record_may_leak ? ptr : __CPROVER_memory_leak;
13101412

1311-
#ifdef __CPROVER_DFCC_DEBUG_LIB
1312-
// manually inlined below
1313-
__CPROVER_contracts_obj_set_add(write_set->linked_is_fresh, ptr);
1314-
#else
1315-
__CPROVER_size_t object_id = __CPROVER_POINTER_OBJECT(ptr);
1316-
write_set->linked_is_fresh->nof_elems =
1317-
(write_set->linked_is_fresh->elems[object_id] != 0)
1318-
? write_set->linked_is_fresh->nof_elems
1319-
: write_set->linked_is_fresh->nof_elems + 1;
1320-
write_set->linked_is_fresh->elems[object_id] = ptr;
1321-
write_set->linked_is_fresh->is_empty = 0;
1322-
#endif
13231413
return 1;
13241414
}
13251415
else if(write_set->assume_ensures_ctx)
@@ -1357,6 +1447,13 @@ __CPROVER_HIDE:;
13571447

13581448
void *ptr = __CPROVER_allocate(size, 0);
13591449
*elem = ptr;
1450+
__CPROVER_assert(
1451+
__CPROVER_contracts_check_ptr_not_in_ptr_pred(
1452+
write_set->linked_is_fresh, elem),
1453+
"__CPROVER_is_fresh does not conflict with other predicate");
1454+
__CPROVER_contracts_record_ptr_in_ptr_pred(
1455+
write_set->linked_is_fresh, elem);
1456+
__CPROVER_contracts_record_fresh_id(write_set->linked_is_fresh, ptr);
13601457

13611458
// record the object size for non-determistic bounds checking
13621459
__CPROVER_bool record_malloc = __VERIFIER_nondet___CPROVER_bool();
@@ -1369,17 +1466,6 @@ __CPROVER_HIDE:;
13691466
__CPROVER_bool record_may_leak = __VERIFIER_nondet___CPROVER_bool();
13701467
__CPROVER_memory_leak = record_may_leak ? ptr : __CPROVER_memory_leak;
13711468

1372-
#ifdef __CPROVER_DFCC_DEBUG_LIB
1373-
__CPROVER_contracts_obj_set_add(write_set->linked_allocated, ptr);
1374-
#else
1375-
__CPROVER_size_t object_id = __CPROVER_POINTER_OBJECT(ptr);
1376-
write_set->linked_allocated->nof_elems =
1377-
(write_set->linked_allocated->elems[object_id] != 0)
1378-
? write_set->linked_allocated->nof_elems
1379-
: write_set->linked_allocated->nof_elems + 1;
1380-
write_set->linked_allocated->elems[object_id] = ptr;
1381-
write_set->linked_allocated->is_empty = 0;
1382-
#endif
13831469
return 1;
13841470
}
13851471
else if(write_set->assert_requires_ctx | write_set->assert_ensures_ctx)
@@ -1390,34 +1476,22 @@ __CPROVER_HIDE:;
13901476
(write_set->assume_ensures_ctx == 0),
13911477
"only one context flag at a time");
13921478
#endif
1393-
__CPROVER_contracts_obj_set_ptr_t seen = write_set->linked_is_fresh;
13941479
void *ptr = *elem;
1395-
// null pointers or already seen pointers are not fresh
1396-
#ifdef __CPROVER_DFCC_DEBUG_LIB
1397-
// manually inlined below
1398-
if((ptr == 0) || (__CPROVER_contracts_obj_set_contains(seen, ptr)))
1399-
return 0;
1400-
#else
1401-
if(ptr == 0)
1402-
return 0;
1403-
1404-
__CPROVER_size_t object_id = __CPROVER_POINTER_OBJECT(ptr);
1405-
1406-
if(seen->elems[object_id] != 0)
1407-
return 0;
1408-
#endif
1409-
1410-
#ifdef __CPROVER_DFCC_DEBUG_LIB
1411-
// manually inlined below
1412-
__CPROVER_contracts_obj_set_add(seen, ptr);
1413-
#else
1414-
seen->nof_elems =
1415-
(seen->elems[object_id] != 0) ? seen->nof_elems : seen->nof_elems + 1;
1416-
seen->elems[object_id] = ptr;
1417-
seen->is_empty = 0;
1418-
#endif
1419-
// check size
1420-
return __CPROVER_r_ok(ptr, size);
1480+
if(
1481+
ptr != (void *)0 &&
1482+
__CPROVER_contracts_not_seen_is_fresh(write_set->linked_is_fresh, ptr) &&
1483+
__CPROVER_r_ok(ptr, size))
1484+
{
1485+
__CPROVER_assert(
1486+
__CPROVER_contracts_check_ptr_not_in_ptr_pred(
1487+
write_set->linked_is_fresh, elem),
1488+
"__CPROVER_is_fresh does not conflict with other predicate");
1489+
__CPROVER_contracts_record_ptr_in_ptr_pred(
1490+
write_set->linked_is_fresh, elem);
1491+
__CPROVER_contracts_record_fresh_id(write_set->linked_is_fresh, ptr);
1492+
return 1;
1493+
}
1494+
return 0;
14211495
}
14221496
else
14231497
{
@@ -1484,13 +1558,33 @@ __CPROVER_HIDE:;
14841558
__CPROVER_size_t max_offset = ub_offset - lb_offset;
14851559
__CPROVER_assume(offset <= max_offset);
14861560
*ptr = (char *)lb + offset;
1561+
__CPROVER_assert(
1562+
__CPROVER_contracts_check_ptr_not_in_ptr_pred(
1563+
write_set->linked_is_fresh, ptr),
1564+
"__CPROVER_pointer_in_range_dfcc does not conflict with other predicate");
1565+
__CPROVER_contracts_record_ptr_in_ptr_pred(write_set->linked_is_fresh, ptr);
14871566
return 1;
14881567
}
14891568
else /* write_set->assert_requires_ctx | write_set->assert_ensures_ctx */
14901569
{
14911570
__CPROVER_size_t offset = __CPROVER_POINTER_OFFSET(*ptr);
1492-
return __CPROVER_same_object(lb, *ptr) && lb_offset <= offset &&
1493-
offset <= ub_offset;
1571+
if(
1572+
__CPROVER_same_object(lb, *ptr) && lb_offset <= offset &&
1573+
offset <= ub_offset)
1574+
{
1575+
__CPROVER_assert(
1576+
__CPROVER_contracts_check_ptr_not_in_ptr_pred(
1577+
write_set->linked_is_fresh, ptr),
1578+
"__CPROVER_pointer_in_range_dfcc does not conflict with other "
1579+
"predicate");
1580+
__CPROVER_contracts_record_ptr_in_ptr_pred(
1581+
write_set->linked_is_fresh, ptr);
1582+
return 1;
1583+
}
1584+
else
1585+
{
1586+
return 0;
1587+
}
14941588
}
14951589
}
14961590

src/goto-instrument/contracts/dynamic-frames/dfcc_is_cprover_symbol.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ init_function_symbols(std::unordered_set<irep_idt> &function_symbols)
5252
function_symbols.insert(CPROVER_PREFIX
5353
"contracts_obj_set_create_indexed_by_object_id");
5454
function_symbols.insert(CPROVER_PREFIX "contracts_obj_set_release");
55+
function_symbols.insert(CPROVER_PREFIX "contracts_obj_set_reset_ptr_pred");
5556
function_symbols.insert(CPROVER_PREFIX "contracts_obj_set_remove");
5657
function_symbols.insert(CPROVER_PREFIX "contracts_pointer_equals");
5758
function_symbols.insert(CPROVER_PREFIX "contracts_pointer_in_range_dfcc");

src/goto-instrument/contracts/dynamic-frames/dfcc_library.cpp

+13
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ const std::map<dfcc_funt, irep_idt> create_dfcc_fun_to_name()
7171
{dfcc_funt::OBJ_SET_CREATE_APPEND,
7272
CONTRACTS_PREFIX "obj_set_create_append"},
7373
{dfcc_funt::OBJ_SET_RELEASE, CONTRACTS_PREFIX "obj_set_release"},
74+
{dfcc_funt::OBJ_SET_RESET_PTR_PRED,
75+
CONTRACTS_PREFIX "obj_set_reset_ptr_pred"},
7476
{dfcc_funt::OBJ_SET_ADD, CONTRACTS_PREFIX "obj_set_add"},
7577
{dfcc_funt::OBJ_SET_APPEND, CONTRACTS_PREFIX "obj_set_append"},
7678
{dfcc_funt::OBJ_SET_REMOVE, CONTRACTS_PREFIX "obj_set_remove"},
@@ -882,3 +884,14 @@ const code_function_callt dfcc_libraryt::obj_set_release_call(
882884
call.add_source_location() = source_location;
883885
return call;
884886
}
887+
888+
const code_function_callt dfcc_libraryt::obj_set_reset_ptr_pred_call(
889+
const exprt &obj_set_ptr,
890+
const source_locationt &source_location)
891+
{
892+
code_function_callt call(
893+
dfcc_fun_symbol[dfcc_funt::OBJ_SET_RESET_PTR_PRED].symbol_expr(),
894+
{obj_set_ptr});
895+
call.add_source_location() = source_location;
896+
return call;
897+
}

src/goto-instrument/contracts/dynamic-frames/dfcc_library.h

+8
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ enum class dfcc_funt
6262
OBJ_SET_CREATE_APPEND,
6363
/// \see __CPROVER_contracts_obj_set_release
6464
OBJ_SET_RELEASE,
65+
/// \see __CPROVER_contracts_obj_set_reset_ptr_pred
66+
OBJ_SET_RESET_PTR_PRED,
6567
/// \see __CPROVER_contracts_obj_set_add
6668
OBJ_SET_ADD,
6769
/// \see __CPROVER_contracts_obj_set_append
@@ -451,6 +453,12 @@ class dfcc_libraryt
451453
const code_function_callt obj_set_release_call(
452454
const exprt &obj_set_ptr,
453455
const source_locationt &source_location);
456+
457+
/// \brief Builds call to
458+
/// \ref __CPROVER_contracts_obj_set_reset_ptr_pred
459+
const code_function_callt obj_set_reset_ptr_pred_call(
460+
const exprt &obj_set_ptr,
461+
const source_locationt &source_location);
454462
};
455463

456464
#endif

src/goto-instrument/contracts/dynamic-frames/dfcc_wrapper_program.cpp

+5
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,11 @@ void dfcc_wrapper_programt::encode_ensures_clauses()
631631
addr_of_ensures_write_set, addr_of_contract_write_set, wrapper_sl),
632632
wrapper_sl));
633633

634+
// reset the tracker of target pointers for pointer predicates
635+
link_deallocated_contract.add(goto_programt::make_function_call(
636+
library.obj_set_reset_ptr_pred_call(addr_of_is_fresh_set, wrapper_sl),
637+
wrapper_sl));
638+
634639
// fix calls to user-defined user-defined memory predicates
635640
memory_predicates.fix_calls(ensures_program);
636641

0 commit comments

Comments
 (0)