Skip to content

Commit 8416fac

Browse files
author
Remi Delmas
committed
CONTRACTS: Add dfcc_swap_and_wrapt class
Given a `function_id`, a `contract_id`, a contract `mode`, and a `dfcc_contract_handlert` for the contract, this class swaps the body of the function to a function with a mangled name and instruments the mangled function for dynamic frame condition checking using `dfcc_instrument`. The body of the original function is then replaced with instructions generated by the contract handler for the required contract mode (CHECK or REPLACE). To handle recursive functions in contract checking mode, the body of the wrapper function is generated in such a way the first invocation results in checking the contract against the mangled function and recursive invocations of the wrapper behave like the contract abstraction (induction hypothesis for the recursive call).
1 parent 0255aa2 commit 8416fac

File tree

3 files changed

+434
-0
lines changed

3 files changed

+434
-0
lines changed

src/goto-instrument/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ SRC = accelerate/accelerate.cpp \
2626
contracts/dynamic-frames/dfcc_dsl_contract_functions.cpp \
2727
contracts/dynamic-frames/dfcc_dsl_wrapper_program.cpp \
2828
contracts/dynamic-frames/dfcc_dsl_contract_handler.cpp \
29+
contracts/dynamic-frames/dfcc_swap_and_wrap.cpp \
2930
contracts/havoc_assigns_clause_targets.cpp \
3031
contracts/instrument_spec_assigns.cpp \
3132
contracts/memory_predicates.cpp \
Lines changed: 313 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,313 @@
1+
/*******************************************************************\
2+
3+
Module: Dynamic frame condition checking
4+
5+
Author: Remi Delmas, [email protected]
6+
7+
\*******************************************************************/
8+
9+
#include "dfcc_swap_and_wrap.h"
10+
11+
#include <util/config.h>
12+
#include <util/expr_util.h>
13+
#include <util/format_expr.h>
14+
#include <util/format_type.h>
15+
#include <util/fresh_symbol.h>
16+
#include <util/mathematical_expr.h>
17+
#include <util/mathematical_types.h>
18+
#include <util/namespace.h>
19+
#include <util/optional.h>
20+
#include <util/pointer_expr.h>
21+
#include <util/pointer_offset_size.h>
22+
#include <util/pointer_predicates.h>
23+
#include <util/std_expr.h>
24+
25+
#include <goto-programs/goto_functions.h>
26+
#include <goto-programs/goto_inline.h>
27+
#include <goto-programs/goto_model.h>
28+
#include <goto-programs/instrument_preconditions.h>
29+
#include <goto-programs/link_to_library.h>
30+
#include <goto-programs/remove_skip.h>
31+
32+
#include <ansi-c/c_expr.h>
33+
#include <ansi-c/cprover_library.h>
34+
#include <goto-instrument/contracts/cfg_info.h>
35+
#include <goto-instrument/contracts/instrument_spec_assigns.h>
36+
#include <goto-instrument/contracts/utils.h>
37+
#include <linking/static_lifetime_init.h>
38+
39+
dfcc_swap_and_wrapt::dfcc_swap_and_wrapt(
40+
goto_modelt &goto_model,
41+
messaget &log,
42+
dfcc_utilst &utils,
43+
dfcc_libraryt &library,
44+
dfcc_instrumentt &instrument,
45+
dfcc_spec_functionst &spec_functions,
46+
dfcc_contract_handlert &contract_handler)
47+
: goto_model(goto_model),
48+
log(log),
49+
message_handler(log.get_message_handler()),
50+
utils(utils),
51+
library(library),
52+
instrument(instrument),
53+
spec_functions(spec_functions),
54+
contract_handler(contract_handler),
55+
ns(goto_model.symbol_table)
56+
{
57+
}
58+
59+
// static map
60+
std::map<irep_idt, std::pair<irep_idt, dfcc_contract_modet>>
61+
dfcc_swap_and_wrapt::cache;
62+
63+
void dfcc_swap_and_wrapt::swap_and_wrap(
64+
const dfcc_contract_modet contract_mode,
65+
const irep_idt &function_id,
66+
const irep_idt &contract_id,
67+
std::set<irep_idt> &function_pointer_contracts,
68+
bool allow_recursive_calls)
69+
{
70+
log.debug() << "->swap_and_wrap(" << function_id << "," << contract_id << ")"
71+
<< messaget::eom;
72+
auto found = cache.find(function_id);
73+
if(found != cache.end())
74+
{
75+
auto &pair = found->second;
76+
77+
// same swap already performed
78+
if(pair.first == contract_id && pair.second == contract_mode)
79+
return;
80+
81+
// already swapped with something else, abort
82+
log.error() << " multiple attempts to swap and wrap function '"
83+
<< function_id << "':" << messaget::eom;
84+
auto mode1 =
85+
(pair.second == dfcc_contract_modet::REPLACE) ? "REPLACE" : "CHECK";
86+
log.error() << "with '" << pair.first << "' in mode " << mode1
87+
<< messaget::eom;
88+
auto mode2 =
89+
(contract_mode == dfcc_contract_modet::REPLACE) ? "REPLACE" : "CHECK)";
90+
log.error() << "with '" << contract_id << "' in mode " << mode2
91+
<< messaget::eom;
92+
throw 0;
93+
}
94+
95+
cache.insert({function_id, {contract_id, contract_mode}});
96+
97+
switch(contract_mode)
98+
{
99+
case dfcc_contract_modet::CHECK:
100+
{
101+
check_contract(
102+
function_id,
103+
contract_id,
104+
function_pointer_contracts,
105+
allow_recursive_calls);
106+
break;
107+
}
108+
case dfcc_contract_modet::REPLACE:
109+
{
110+
replace_with_contract(function_id, contract_id, function_pointer_contracts);
111+
break;
112+
}
113+
}
114+
log.debug() << "<-swap_and_wrap(" << function_id << "," << contract_id << ")"
115+
<< messaget::eom;
116+
}
117+
118+
void dfcc_swap_and_wrapt::get_swapped_functions(std::set<irep_idt> &dest) const
119+
{
120+
for(const auto &it : dfcc_swap_and_wrapt::cache)
121+
{
122+
dest.insert(it.first);
123+
}
124+
}
125+
126+
/// \details Generates globals statics:
127+
/// ```c
128+
/// static bool __contract_check_in_progress = false;
129+
/// static bool __contract_checked_once = false;
130+
/// ```
131+
///
132+
/// Adds the following instructions in the wrapper function body:
133+
/// ```c
134+
/// IF __contract_check_in_progress GOTO replace;
135+
/// ASSERT !__contract_checked_once "only a single top-level called allowed";
136+
/// __contract_check_in_progress = true;
137+
/// <contract_handler.add_contract_checking_instructions(...)>;
138+
/// __contract_checked_once = true;
139+
/// __contract_check_in_progress = false;
140+
/// GOTO end;
141+
/// replace:
142+
/// // if allow_recursive_calls
143+
/// <contract_handler.add_contract_replacement_instructions(...)>;
144+
/// // if !allow_recursive_calls
145+
/// ASSERT false, "no recursive calls";
146+
/// ASSUME false;
147+
/// end:
148+
/// END_FUNCTION;
149+
/// ```
150+
void dfcc_swap_and_wrapt::check_contract(
151+
const irep_idt &function_id,
152+
const irep_idt &contract_id,
153+
std::set<irep_idt> &function_pointer_contracts,
154+
bool allow_recursive_calls)
155+
{
156+
log.debug() << "->check_contract(" << function_id << "," << contract_id << ")"
157+
<< messaget::eom;
158+
159+
// all code generation needs to run on functions with unmodified signatures
160+
const irep_idt &wrapper_id = function_id;
161+
const irep_idt wrapped_id =
162+
id2string(wrapper_id) + "_wrapped_for_contract_checking";
163+
utils.wrap_function(wrapper_id, wrapped_id);
164+
165+
// wrapper body
166+
goto_programt body;
167+
168+
const auto &wrapper_symbol = utils.get_function_symbol(wrapper_id);
169+
170+
auto check_started = utils
171+
.create_static_symbol(
172+
bool_typet(),
173+
id2string(function_id),
174+
"__contract_check_in_progress",
175+
wrapper_symbol.location,
176+
wrapper_symbol.mode,
177+
wrapper_symbol.module,
178+
false_exprt())
179+
.symbol_expr();
180+
181+
auto check_completed = utils
182+
.create_static_symbol(
183+
bool_typet(),
184+
id2string(function_id),
185+
"__contract_checked_once",
186+
wrapper_symbol.location,
187+
wrapper_symbol.mode,
188+
wrapper_symbol.module,
189+
false_exprt())
190+
.symbol_expr();
191+
192+
auto check_started_goto =
193+
body.add(goto_programt::make_incomplete_goto(check_started));
194+
195+
// At most a single top level call to the checked function in any execution
196+
197+
// Recursive calls within a contract check correspond to
198+
// `check_started && !check_completed` and are allowed.
199+
200+
// Any other call occuring with `check_completed` true is forbidden.
201+
source_locationt sl;
202+
sl.set_comment(
203+
"Only a single top-level call to function " + id2string(function_id) +
204+
" when checking contract " + id2string(contract_id));
205+
body.add(goto_programt::make_assertion(not_exprt(check_completed), sl));
206+
body.add(goto_programt::make_assignment(check_started, true_exprt()));
207+
208+
const auto write_set_symbol = utils.create_new_parameter_symbol(
209+
function_id,
210+
"__write_set_to_check",
211+
library.dfcc_type[dfcc_typet::CAR_SET_PTR]);
212+
213+
contract_handler.add_contract_instructions(
214+
dfcc_contract_modet::CHECK,
215+
wrapper_id,
216+
wrapped_id,
217+
contract_id,
218+
write_set_symbol,
219+
body,
220+
function_pointer_contracts);
221+
222+
body.add(goto_programt::make_assignment(check_completed, true_exprt()));
223+
body.add(goto_programt::make_assignment(check_started, false_exprt()));
224+
225+
// unconditionally Jump to the end after the check
226+
auto goto_end_function = body.add(goto_programt::make_incomplete_goto());
227+
228+
// Jump to the replacement section if check already in progress
229+
auto contract_replacement_label = body.add(goto_programt::make_skip());
230+
check_started_goto->complete_goto(contract_replacement_label);
231+
232+
if(allow_recursive_calls)
233+
{
234+
contract_handler.add_contract_instructions(
235+
dfcc_contract_modet::REPLACE,
236+
wrapper_id,
237+
wrapped_id,
238+
contract_id,
239+
write_set_symbol,
240+
body,
241+
function_pointer_contracts);
242+
}
243+
else
244+
{
245+
source_locationt sl;
246+
sl.set_comment(
247+
"No recursive call to function " + id2string(function_id) +
248+
" when checking contract " + id2string(contract_id));
249+
body.add(goto_programt::make_assertion(false_exprt(), sl));
250+
body.add(goto_programt::make_assumption(false_exprt(), sl));
251+
}
252+
253+
auto end_function_label = body.add(goto_programt::make_end_function());
254+
goto_end_function->complete_goto(end_function_label);
255+
256+
// write the body to the GOTO function
257+
goto_model.goto_functions.function_map.at(function_id).body.swap(body);
258+
259+
// extend the signature of the wrapper function with the write set parameter
260+
utils.add_parameter(write_set_symbol, function_id);
261+
262+
utils.set_hide(wrapper_id, true);
263+
264+
// instrument the wrapped function
265+
instrument.instrument_wrapped_function(wrapped_id, wrapper_id);
266+
267+
goto_model.goto_functions.update();
268+
269+
log.debug() << "<-check_contract(" << function_id << "," << contract_id << ")"
270+
<< messaget::eom;
271+
}
272+
273+
void dfcc_swap_and_wrapt::replace_with_contract(
274+
const irep_idt &function_id,
275+
const irep_idt &contract_id,
276+
std::set<irep_idt> &function_pointer_contracts)
277+
{
278+
log.debug() << "->replace_with_contract(" << function_id << "," << contract_id
279+
<< ")" << messaget::eom;
280+
281+
const irep_idt &wrapper_id = function_id;
282+
const irep_idt wrapped_id =
283+
id2string(function_id) + "_wrapped_for_replacement_with_contract";
284+
utils.wrap_function(function_id, wrapped_id);
285+
286+
const auto write_set_symbol = utils.create_new_parameter_symbol(
287+
function_id,
288+
"__write_set_to_check",
289+
library.dfcc_type[dfcc_typet::CAR_SET_PTR]);
290+
291+
goto_programt body;
292+
293+
contract_handler.add_contract_instructions(
294+
dfcc_contract_modet::REPLACE,
295+
wrapper_id,
296+
wrapped_id,
297+
contract_id,
298+
write_set_symbol,
299+
body,
300+
function_pointer_contracts);
301+
302+
body.add(goto_programt::make_end_function());
303+
304+
utils.set_hide(wrapper_id, true);
305+
306+
// write the body to the GOTO function
307+
goto_model.goto_functions.function_map.at(function_id).body.swap(body);
308+
309+
// extend the signature with the new write set parameter
310+
utils.add_parameter(write_set_symbol, function_id);
311+
log.debug() << "<-replace_with_contract(" << function_id << "," << contract_id
312+
<< ")" << messaget::eom;
313+
}

0 commit comments

Comments
 (0)