Skip to content

Commit ad9e93f

Browse files
author
Remi Delmas
committed
CONTRACTS: new context tracking flags in write set
Enables checking for the presence of dynamic allocations or deallocations in loop bodies and transitively in the functions called from loop bodies. - Adds `allow_allocate` and `allow_deallocate` flags in the write set struct - Adds two functions to set the flags. - Modify `add_allocated` to fail an assertion when called on a write set with `allow_allocate` set to false. - Modify `check_deallocate` to return false when called on a write set with `allow_deallocate` set to false.
1 parent bbf256f commit ad9e93f

File tree

5 files changed

+67
-15
lines changed

5 files changed

+67
-15
lines changed

src/ansi-c/library/cprover_contracts.c

Lines changed: 49 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,10 @@ typedef struct
9797
__CPROVER_bool assume_ensures_ctx;
9898
/// \brief True iff this write set checks ensures clauses in an assertion ctx
9999
__CPROVER_bool assert_ensures_ctx;
100+
/// \brief True iff dynamic allocation is allowed (default: true)
101+
__CPROVER_bool allow_allocate;
102+
/// \brief True iff dynamic deallocation is allowed (default: true)
103+
__CPROVER_bool allow_deallocate;
100104
} __CPROVER_contracts_write_set_t;
101105

102106
/// \brief Type of pointers to \ref __CPROVER_contracts_write_set_t.
@@ -436,6 +440,8 @@ __CPROVER_HIDE:;
436440
set->assert_requires_ctx = assert_requires_ctx;
437441
set->assume_ensures_ctx = assume_ensures_ctx;
438442
set->assert_ensures_ctx = assert_ensures_ctx;
443+
set->allow_allocate = 1;
444+
set->allow_deallocate = 1;
439445
}
440446

441447
/// \brief Releases resources used by \p set.
@@ -474,6 +480,20 @@ __CPROVER_HIDE:;
474480
// since they are owned by someone else.
475481
}
476482

483+
/// \brief Forbids dynamic allocation in functions that receive that write set.
484+
void __CPROVER_contracts_write_set_forbid_allocate(
485+
__CPROVER_contracts_write_set_ptr_t set)
486+
{
487+
set->allow_allocate = 0;
488+
}
489+
490+
/// \brief Forbids deallocation in functions that receive that write set.
491+
void __CPROVER_contracts_write_set_forbid_deallocate(
492+
__CPROVER_contracts_write_set_ptr_t set)
493+
{
494+
set->allow_deallocate = 0;
495+
}
496+
477497
/// \brief Inserts a snapshot of the range starting at \p ptr of size \p size
478498
/// at index \p idx in \p set->contract_assigns.
479499
/// \param[inout] set The set to update
@@ -600,14 +620,35 @@ __CPROVER_HIDE:;
600620
#endif
601621
}
602622

603-
/// \brief Adds the pointer \p ptr to \p set->allocated.
623+
/// \brief Adds the dynamically allocated pointer \p ptr to \p set->allocated.
604624
/// \param[inout] set The set to update
605-
/// \param[in] ptr Pointer to an object declared using a `DECL x` or
606-
/// `x = __CPROVER_allocate(...)` GOTO instruction.
625+
/// \param[in] ptr Pointer to a dynamic object `x = __CPROVER_allocate(...)`.
607626
void __CPROVER_contracts_write_set_add_allocated(
608627
__CPROVER_contracts_write_set_ptr_t set,
609628
void *ptr)
610629
{
630+
__CPROVER_HIDE:;
631+
__CPROVER_assert(set->allow_allocate, "dynamic allocation is allowed");
632+
#if DFCC_DEBUG
633+
// call inlined below
634+
__CPROVER_contracts_obj_set_add(&(set->allocated), ptr);
635+
#else
636+
__CPROVER_size_t object_id = __CPROVER_POINTER_OBJECT(ptr);
637+
set->allocated.nof_elems = (set->allocated.elems[object_id] != 0)
638+
? set->allocated.nof_elems
639+
: set->allocated.nof_elems + 1;
640+
set->allocated.elems[object_id] = ptr;
641+
set->allocated.is_empty = 0;
642+
#endif
643+
}
644+
645+
/// \brief Adds the pointer \p ptr to \p set->allocated.
646+
/// \param[inout] set The set to update
647+
/// \param[in] ptr Pointer to an object declared using `DECL x`.
648+
void __CPROVER_contracts_write_set_add_decl(
649+
__CPROVER_contracts_write_set_ptr_t set,
650+
void *ptr)
651+
{
611652
__CPROVER_HIDE:;
612653
#if DFCC_DEBUG
613654
// call inlined below
@@ -904,8 +945,8 @@ __CPROVER_HIDE:;
904945
/// \param[in] set Write set to check the deallocation against
905946
/// \param[in] ptr Deallocated pointer to check set to check the deallocation
906947
/// against
907-
/// \return True iff \p ptr is contained in \p set->contract_frees or
908-
/// \p set->allocated.
948+
/// \return True iff deallocation is allowed and \p ptr is contained in
949+
/// \p set->contract_frees or \p set->allocated.
909950
__CPROVER_bool __CPROVER_contracts_write_set_check_deallocate(
910951
__CPROVER_contracts_write_set_ptr_t set,
911952
void *ptr)
@@ -924,8 +965,9 @@ __CPROVER_HIDE:;
924965
set->allocated.indexed_by_object_id,
925966
"set->allocated is indexed by object id");
926967
#endif
927-
return (ptr == 0) | (set->contract_frees.elems[object_id] == ptr) |
928-
(set->allocated.elems[object_id] == ptr);
968+
return (set->allow_deallocate) &
969+
((ptr == 0) | (set->contract_frees.elems[object_id] == ptr) |
970+
(set->allocated.elems[object_id] == ptr));
929971
}
930972

931973
/// \brief Checks the inclusion of the \p candidate->contract_assigns elements

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

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -380,8 +380,7 @@ void dfcc_instrumentt::instrument_function_body(
380380
for(const auto &local_static : local_statics)
381381
{
382382
// automatically add local statics to the write set
383-
insert_add_allocated_call(
384-
function_id, write_set, local_static, begin, body);
383+
insert_add_decl_call(function_id, write_set, local_static, begin, body);
385384

386385
// automatically remove local statics to the write set
387386
insert_record_dead_call(function_id, write_set, local_static, end, body);
@@ -510,7 +509,7 @@ bool dfcc_instrumentt::must_track_decl_or_dead(
510509
return retval;
511510
}
512511

513-
void dfcc_instrumentt::insert_add_allocated_call(
512+
void dfcc_instrumentt::insert_add_decl_call(
514513
const irep_idt &function_id,
515514
const exprt &write_set,
516515
const symbol_exprt &symbol_expr,
@@ -523,7 +522,7 @@ void dfcc_instrumentt::insert_add_allocated_call(
523522

524523
payload.add(goto_programt::make_function_call(
525524
code_function_callt{
526-
library.dfcc_fun_symbol[dfcc_funt::WRITE_SET_ADD_ALLOCATED].symbol_expr(),
525+
library.dfcc_fun_symbol[dfcc_funt::WRITE_SET_ADD_DECL].symbol_expr(),
527526
{write_set, address_of_exprt(symbol_expr)}},
528527
target->source_location()));
529528

@@ -538,7 +537,7 @@ void dfcc_instrumentt::insert_add_allocated_call(
538537
/// ```c
539538
/// DECL x;
540539
/// ----
541-
/// insert_add_allocated_call(...);
540+
/// insert_add_decl_call(...);
542541
/// ```
543542
void dfcc_instrumentt::instrument_decl(
544543
const irep_idt &function_id,
@@ -552,7 +551,7 @@ void dfcc_instrumentt::instrument_decl(
552551

553552
const auto &decl_symbol = target->decl_symbol();
554553
target++;
555-
insert_add_allocated_call(
554+
insert_add_decl_call(
556555
function_id, write_set, decl_symbol, target, goto_program);
557556
target--;
558557
}

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,15 @@ class dfcc_instrumentt
175175
/// forward.
176176
/// ```
177177
/// IF !write_set GOTO skip_target;
178-
/// CALL __CPROVER_contracts_write_set_add_allocated(write_set, &x);
178+
/// CALL __CPROVER_contracts_write_set_add_decl(write_set, &x);
179179
/// skip_target: SKIP;
180180
/// ```
181181
/// \param function_id Name of the function in which the instructions is added
182182
/// \param write_set The write set to the symbol expr to
183183
/// \param symbol_expr The symbol to add to the write set
184184
/// \param target The instruction pointer to insert at
185185
/// \param goto_program the goto_program being instrumented
186-
void insert_add_allocated_call(
186+
void insert_add_decl_call(
187187
const irep_idt &function_id,
188188
const exprt &write_set,
189189
const symbol_exprt &symbol_expr,

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

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ const std::map<dfcc_funt, irep_idt> create_dfcc_fun_to_name()
7979
CONTRACTS_PREFIX "obj_set_contains_exact"},
8080
{dfcc_funt::WRITE_SET_CREATE, CONTRACTS_PREFIX "write_set_create"},
8181
{dfcc_funt::WRITE_SET_RELEASE, CONTRACTS_PREFIX "write_set_release"},
82+
{dfcc_funt::WRITE_SET_FORBID_ALLOCATE,
83+
CONTRACTS_PREFIX "write_set_forbid_allocate"},
84+
{dfcc_funt::WRITE_SET_FORBID_DEALLOCATE,
85+
CONTRACTS_PREFIX "write_set_forbid_deallocate"},
8286
{dfcc_funt::WRITE_SET_INSERT_ASSIGNABLE,
8387
CONTRACTS_PREFIX "write_set_insert_assignable"},
8488
{dfcc_funt::WRITE_SET_INSERT_OBJECT_WHOLE,
@@ -91,6 +95,7 @@ const std::map<dfcc_funt, irep_idt> create_dfcc_fun_to_name()
9195
CONTRACTS_PREFIX "write_set_add_freeable"},
9296
{dfcc_funt::WRITE_SET_ADD_ALLOCATED,
9397
CONTRACTS_PREFIX "write_set_add_allocated"},
98+
{dfcc_funt::WRITE_SET_ADD_DECL, CONTRACTS_PREFIX "write_set_add_decl"},
9499
{dfcc_funt::WRITE_SET_RECORD_DEAD,
95100
CONTRACTS_PREFIX "write_set_record_dead"},
96101
{dfcc_funt::WRITE_SET_RECORD_DEALLOCATED,

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ enum class dfcc_funt
7575
WRITE_SET_CREATE,
7676
/// \see __CPROVER_contracts_write_set_release
7777
WRITE_SET_RELEASE,
78+
/// \see __CPROVER_contracts_write_set_forbid_allocate
79+
WRITE_SET_FORBID_ALLOCATE,
80+
/// \see __CPROVER_contracts_write_set_forbid_deallocate
81+
WRITE_SET_FORBID_DEALLOCATE,
7882
/// \see __CPROVER_contracts_write_set_insert_assignable
7983
WRITE_SET_INSERT_ASSIGNABLE,
8084
/// \see __CPROVER_contracts_write_set_insert_object_whole
@@ -87,6 +91,8 @@ enum class dfcc_funt
8791
WRITE_SET_ADD_FREEABLE,
8892
/// \see __CPROVER_contracts_write_set_add_allocated
8993
WRITE_SET_ADD_ALLOCATED,
94+
/// \see __CPROVER_contracts_write_set_add_decl
95+
WRITE_SET_ADD_DECL,
9096
/// \see __CPROVER_contracts_write_set_record_dead
9197
WRITE_SET_RECORD_DEAD,
9298
/// \see __CPROVER_contracts_write_set_record_deallocated

0 commit comments

Comments
 (0)