|
| 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