Skip to content

Commit 5ff1a04

Browse files
author
Remi Delmas
committed
CONTRACTS: make __CPROVER_is_fresh really call-local.
The previous implementation of the is_fresh predicates used a global static map, which caused different occurences of a call at same call site to interfere. We now use a locally allocated memory map which solves the problem.
1 parent 0c58a89 commit 5ff1a04

File tree

5 files changed

+217
-58
lines changed

5 files changed

+217
-58
lines changed
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include <stdbool.h>
2+
#include <stdlib.h>
3+
4+
char nondet_char();
5+
6+
char *foo(char *a, char *b, size_t s)
7+
// clang-format off
8+
__CPROVER_requires(s > 0)
9+
__CPROVER_requires(__CPROVER_is_fresh(a, s))
10+
__CPROVER_requires(__CPROVER_is_fresh(b, s))
11+
__CPROVER_assigns(a[0])
12+
__CPROVER_ensures(__CPROVER_is_fresh(__CPROVER_return_value, s))
13+
// clang-format on
14+
{
15+
a[0] = nondet_char();
16+
return malloc(s);
17+
}
18+
19+
char *bar(char *a, char *b, size_t s)
20+
{
21+
return foo(a, b, s);
22+
}
23+
24+
int main()
25+
{
26+
size_t s;
27+
__CPROVER_assume(0 < s && s < __CPROVER_max_malloc_size);
28+
char *a = malloc(s);
29+
char *b = malloc(s);
30+
31+
char *c = bar(a, b, s);
32+
__CPROVER_assert(__CPROVER_rw_ok(c, s), "c is rw_ok");
33+
__CPROVER_assert(c != a, "c and a are distinct");
34+
__CPROVER_assert(c != b, "c and b are distinct");
35+
36+
char *d = bar(a, b, s);
37+
__CPROVER_assert(__CPROVER_rw_ok(d, s), "d is rw_ok");
38+
__CPROVER_assert(d != a, "d and a are distinct");
39+
__CPROVER_assert(d != b, "d and b are distinct");
40+
__CPROVER_assert(d != c, "d and c distinct");
41+
42+
return 0;
43+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
CORE
2+
main.c
3+
--replace-call-with-contract foo
4+
^\[foo.assigns.\d+\].*Check that a\[.*0\] is valid: SUCCESS$
5+
^\[main.assertion.\d+\].*c is rw_ok: SUCCESS$
6+
^\[main.assertion.\d+\].*c and a are distinct: SUCCESS$
7+
^\[main.assertion.\d+\].*c and b are distinct: SUCCESS$
8+
^\[main.assertion.\d+\].*d is rw_ok: SUCCESS$
9+
^\[main.assertion.\d+\].*d and a are distinct: SUCCESS$
10+
^\[main.assertion.\d+\].*d and b are distinct: SUCCESS$
11+
^\[main.assertion.\d+\].*d and c distinct: SUCCESS$
12+
^EXIT=0$
13+
^SIGNAL=0$
14+
^VERIFICATION SUCCESSFUL$
15+
--
16+
--
17+
This test checks that the interpretation of is_fresh predicates is
18+
local to a call. `bar` is called twice with the same arguments.
19+
`bar` calls `foo` so `foo` also gets called twice with the same arguments.
20+
The is_fresh preconditions of `foo` are checked and satisfied independently
21+
for each call.
22+
This shows that the memory_map which keeps track of objects seen by the
23+
is_fresh predicates and the input and output of a function call is local to
24+
that function call.

src/goto-instrument/contracts/contracts.cpp

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -820,8 +820,12 @@ void code_contractst::apply_function_contract(
820820

821821
const auto &mode = function_symbol.mode;
822822

823+
// new program where all contract-derived instructions are added
824+
goto_programt new_program;
825+
823826
is_fresh_replacet is_fresh(*this, log, target_function);
824827
is_fresh.create_declarations();
828+
is_fresh.add_memory_map_decl(new_program);
825829

826830
// Insert assertion of the precondition immediately before the call site.
827831
if(!requires.is_true())
@@ -839,7 +843,7 @@ void code_contractst::apply_function_contract(
839843
assertion.instructions.back().source_location_nonconst().set_property_class(
840844
ID_precondition);
841845
is_fresh.update_requires(assertion);
842-
insert_before_swap_and_advance(function_body, target, assertion);
846+
new_program.destructive_append(assertion);
843847
}
844848

845849
// Gather all the instructions required to handle history variables
@@ -856,8 +860,7 @@ void code_contractst::apply_function_contract(
856860
create_ensures_instruction(assumption, ensures.source_location(), mode);
857861

858862
// add all the history variable initialization instructions
859-
// to the goto program
860-
insert_before_swap_and_advance(function_body, target, ensures_pair.second);
863+
new_program.destructive_append(ensures_pair.second);
861864
}
862865

863866
// ASSIGNS clause should not refer to any quantified variables,
@@ -901,14 +904,14 @@ void code_contractst::apply_function_contract(
901904
}
902905

903906
// Insert havoc instructions immediately before the call site.
904-
insert_before_swap_and_advance(function_body, target, havoc_instructions);
907+
new_program.destructive_append(havoc_instructions);
905908

906909
// To remove the function call, insert statements related to the assumption.
907910
// Then, replace the function call with a SKIP statement.
908911
if(!ensures.is_false())
909912
{
910913
is_fresh.update_ensures(ensures_pair.first);
911-
insert_before_swap_and_advance(function_body, target, ensures_pair.first);
914+
new_program.destructive_append(ensures_pair.first);
912915
}
913916

914917
// Kill return value variable if fresh
@@ -921,11 +924,19 @@ void code_contractst::apply_function_contract(
921924
++target;
922925
}
923926

927+
is_fresh.add_memory_map_dead(new_program);
928+
924929
// Erase original function call
925930
*target = goto_programt::make_skip();
926931

932+
// insert contract replacement instructions
933+
insert_before_swap_and_advance(function_body, target, new_program);
934+
927935
// Add this function to the set of replaced functions.
928936
summarized.insert(target_function);
937+
938+
// restore global goto_program consistency
939+
goto_functions.update();
929940
}
930941

931942
void code_contractst::apply_loop_contract(
@@ -1450,7 +1461,7 @@ void code_contractst::add_contract_check(
14501461

14511462
is_fresh_enforcet visitor(*this, log, wrapper_function);
14521463
visitor.create_declarations();
1453-
1464+
visitor.add_memory_map_decl(check);
14541465
// Generate: assume(requires)
14551466
if(!requires.is_false())
14561467
{
@@ -1513,9 +1524,17 @@ void code_contractst::add_contract_check(
15131524
return_stmt.value().return_value(), skip->source_location()));
15141525
}
15151526

1516-
// prepend the new code to dest
1527+
// kill the is_fresh memory map
1528+
visitor.add_memory_map_dead(check);
1529+
1530+
// add final instruction
15171531
check.destructive_append(tmp_skip);
1532+
1533+
// prepend the new code to dest
15181534
dest.destructive_insert(dest.instructions.begin(), check);
1535+
1536+
// restore global goto_program consistency
1537+
goto_functions.update();
15191538
}
15201539

15211540
void code_contractst::check_all_functions_found(

0 commit comments

Comments
 (0)