1/*******************************************************************\
3Module: Dynamic frame condition checking for function and loop contracts.
5Author: Qinheping Hu, qinhh@amazon.com
6Author: Remi Delmas delmasrd@amazon.com
10\*******************************************************************/
36 const bool check_side_effect)
45 out <<
"dfcc_loop_id: " <<
loop_id <<
"\n";
68 out <<
format(expr) <<
", ";
74 out <<
"decreases: {";
77 out <<
format(expr) <<
", ";
81 out <<
"inner loops: {"
89 out <<
"outer loops: {"
98std::optional<goto_programt::targett>
113std::optional<goto_programt::targett>
116 std::optional<goto_programt::targett> result = std::nullopt;
121 // go until the end because we want to find the very last occurrence
134 const bool check_side_effect)
147 if(result.has_value())
158 const bool check_side_effect)
166 if(result.has_value())
183 auto &head = node.head;
184 auto &latch = node.latch;
192 // Skip instructions that are already tagged and belong to inner loops.
199 if(t != head && t != latch)
211 // tag remaining instructions as top level
216 // Skip instructions that are already tagged (belong to loops).
230 // For each assigns clause target
231 for(
const auto &expr : assigns)
242 // If `root_object` is not a symbol, then it contains a combination of
243 // address-of and dereference operators that cannot be statically
244 // resolved to a symbol.
245 // Since we know `ident` is not dirty, we know that dereference
246 // operations cannot land on that `ident`. So the root_object cannot
247 // describe a memory location within the object backing that ident.
248 // We conclude that ident is not assigned by this target and move on to
270 const std::unordered_set<irep_idt> &locals,
272 const std::map<std::size_t, dfcc_loop_infot> &loop_info_map)
274 std::unordered_set<irep_idt> tracked;
275 for(
const auto &
ident : locals)
279 tracked.insert(
ident);
283 // Check if this ident is touched by one of the inner loops
287 tracked.insert(
ident);
309 const std::size_t loop_id,
312 const bool check_side_effect,
319 // Process loop contract clauses
324 // Initialise defaults
328 // Generate defaults for all clauses if at least one type of clause is defined
335 // use a default invariant if none given.
337 // assigns clause is missing; we will try to automatic inference
338 log.warning() <<
"No invariant provided for loop " << function_id <<
"."
339 << loop.latch->loop_number <<
" at "
340 << loop.head->source_location()
341 <<
". Using 'true' as a sound default invariant. Please "
342 "provide an invariant the default is too weak."
347 // conjoin all invariant clauses
351 // unpack assigns clause targets
361 // infer assigns clause targets if none given
362 log.debug() <<
"No assigns clause provided for loop " << function_id
363 <<
"." << loop.latch->loop_number <<
" at "
364 << loop.head->source_location() <<
". The inferred set {";
375 log.debug() <<
"} might be incomplete or imprecise, please provide an "
376 "assigns clause if the analysis fails."
383 log.debug() <<
"No decrease clause provided for loop " << function_id
384 <<
"." << loop.latch->loop_number <<
" at "
385 << loop.head->source_location()
394 const std::size_t loop_id,
397 const std::map<std::size_t, dfcc_loop_infot> &loop_info_map,
400 const bool check_side_effect,
413 // Exclude locals of inner nested loops.
417 loop_info_map.find(
inner_loop) != loop_info_map.end(),
418 "DFCC should gen_dfcc_loop_info for inner loops first.");
440 std::set<std::size_t> inner_loops;
446 std::set<std::size_t> outer_loops;
455 // Generate "write set" variable
461 loop.head->source_location());
463 // Generate "address of write set" variable
469 loop.head->source_location());
482 addr_of_write_set_var};
489 const exprt &top_level_write_set,
494 : function_id(function_id),
495 goto_function(goto_function),
496 top_level_write_set(top_level_write_set),
502 // Clean up possible fake loops that are due to do { ... } while(0);
505 // From loop number to the inferred loop assigns.
519 "Found loop without contract nested in a loop with a "
521 "provide a contract or unwind this loop before applying loop "
523 head.value()->source_location());
536 // We infer loop assigns for all loops in the function.
548 // At this point, either loop contracts were activated and the loop nesting
549 // graph describes the loop structure of the function,
550 // or loop contracts were not activated and the loop nesting graph is empty
551 // (i.e. there might be some loops in the function but we won't consider them
552 // for the instrumentation).
553 // In both cases, we tag program instructions and generate the dfcc_cfg_infot
554 // instance from that graph's contents. The tags will decide against which
555 // write set the instructions are going to be instrumented (either the
556 // function's write set, or the write set of a loop), and each dfcc_loop_infot
557 // contained in the loop_info_map describes a loop to be abstracted by a
562 // generate dfcc_cfg_loop_info for loops and add to loop_info_map
588 // generate set of top level of locals
607 out <<
"// dfcc_cfg_infot for: " <<
function_id <<
"\n";
608 out <<
"// top_level_local: {";
615 out <<
"// top_level_tracked: {";
625 out <<
"// dfcc-loop_id:" << loop.first <<
"\n";
629 head.value()->
output(out);
630 out <<
"// latch:\n";
631 latch.value()->
output(out);
634 out <<
"// program:\n";
638 out <<
" cbmc-loop-number:" << target->loop_number;
650 const std::size_t loop_id)
const
678const std::unordered_set<irep_idt> &
696const std::unordered_set<irep_idt> &
729// find the identifier or the immediately enclosing loop in topological order
730const std::optional<std::size_t>
736 // find the first loop in the topological order that is connected
741 std::find(outer_loops.begin(), outer_loops.end(), idx) !=
747 // return nullopt for loops that are not nested in other loops
775 auto &
ident = target->is_decl() ? target->decl_symbol().identifier()
776 : target->dead_symbol().identifier();
778 return tracked.find(
ident) != tracked.end();
787 const std::unordered_set<irep_idt> &local,
788 const std::unordered_set<irep_idt> &tracked)
792 // Check wether all root_objects can be resolved to actual identifiers.
798 // This means that lhs contains either an address-of operation or a
799 // dereference operation, and we cannot really know statically which
800 // object it refers to without using the may_alias analysis.
801 // Since the may_alias analysis is also used to infer targets, for
802 // soundness reasons we cannot also use it to skip checks, so we check
803 // the assignment. If happens to assign to a mix of tracked and
804 // non-tracked identifiers the check will fail but this is sound anyway.
810 // Skip the check if we have a single cprover symbol as root object
811 // cprover symbols are used for generic checks instrumentation and are
812 // de-facto ghost code. We implicitly allow assignments to these symbols.
813 // To make this really sound we should use a white list of known
814 // CPROVER symbols, because right now simply naming a symbol with the
815 // CPROVER prefix bypasses the checks.
822 // error out if we have a cprover symbol and something else in the set
825 "` in assignment refers to a cprover symbol and something else.");
831 // The root idents set is Non-empty.
832 // true iff root_idents contains non-local idents
834 // true iff root_idents contains some local that is not tracked
836 // true iff root_idents contains only local that are not tracked
838 // true iff root_idents contains only local that are tracked
842 bool loc = local.find(
root_ident) != local.end();
852 // some root identifier is not local, the lhs must be checked
855 // if we also have a local that is not tracked, we know the check will
856 // fail with the current algorithm, error out.
861 "` in assignment mentions both explicitly and implicitly tracked "
862 "memory locations. DFCC does not yet handle that case, please "
863 "reformulate the assignment into separate assignments to either "
864 "memory locations.");
870 // all root identifiers are local
871 // if they are all not tracked, we *have* to skip the check
872 // (and it is sound to do so, because we know that the identifiers that
873 // are not tracked explicitly are not dirty and not assigned to outside of
875 // if they are all tracked, we *can* skip the check, because they are all
876 // local to that scope anyway and implicitly allowed.
883 // we have a combination of tracked and not-tracked locals, we know
884 // the check will fail with the current algorithm, error out.
887 "` in assignment mentions both explicitly and implicitly tracked "
888 "memory locations. DFCC does not yet handle that case, please "
889 "reformulate the assignment into separate assignments to either "
890 "memory locations.");
897 PRECONDITION(target->is_assign() || target->is_function_call());
899 target->is_assign() ? target->assign_lhs() : target->call_lhs();
virtual void output(const namespacet &ns, const irep_idt &function_id, const goto_programt &goto_program, std::ostream &out) const
Output the abstract states for a single function.
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Thrown when an unexpected error occurs during the analysis (e.g., when the SAT solver returns an erro...
const std::optional< std::size_t > get_outer_loop_identifier(const std::size_t loop_id) const
Finds the DFCC id of the loop that contains the given loop, returns nullopt when the loop has no oute...
std::unordered_set< irep_idt > top_level_local
Set of identifiers DECL at top level.
const exprt & top_level_write_set
std::size_t get_first_id_not_skipped_or_top_level_id(const std::size_t loop_id) const
Returns the id of the first outer loop (including this one) that is not skipped, or the top level id.
std::vector< std::size_t > topsorted_loops
Loop identifiers sorted from most deeply nested to less deeply nested.
std::unordered_set< irep_idt > top_level_tracked
Set of identifiers DECL at top level.
bool is_top_level_id(const std::size_t id) const
True iff id is in the valid range for a loop id for this function.
const dfcc_loop_infot & get_loop_info(const std::size_t loop_id) const
Returns the loop info for that loop_id.
void output(std::ostream &out) const
const std::vector< std::size_t > & get_loops_toposorted() const
goto_functiont & goto_function
std::vector< std::size_t > top_level_loops
Loop identifiers for top level loops (ie for loops that are not nested in in another loop).
const std::unordered_set< irep_idt > & get_tracked_set(goto_programt::const_targett target) const
Returns the subset of local variable that are explicitly tracked in the write set for the scope where...
dfcc_cfg_infot(goto_modelt &goto_model, const irep_idt &function_id, goto_functiont &goto_function, const exprt &top_level_write_set, const loop_contract_configt &loop_contract_config, symbol_table_baset &symbol_table, message_handlert &message_handler, dfcc_libraryt &library)
bool must_check_lhs(goto_programt::const_targett target) const
True iff the lhs of an assignment must be checked against the ambient write set.
const std::unordered_set< irep_idt > & get_local_set(goto_programt::const_targett target) const
Returns the set of local variable for the scope where that target instruction is found.
const exprt & get_outer_write_set(std::size_t loop_id) const
Returns the write set of the outer loop of that loop or the top level write set if that loop has no o...
const exprt & get_write_set(goto_programt::const_targett target) const
Returns the write set variable to use for the given instruction Returns the write set for the loop,...
bool must_track_decl_or_dead(goto_programt::const_targett target) const
True iff a DECL ident must be tracked in the write set of the loop that contains the DECL.
size_t top_level_id() const
Returns the top level ID.
bool is_valid_loop_id(const std::size_t id) const
True iff id is in the valid range for a loop id for this function.
const irep_idt & function_id
std::map< std::size_t, dfcc_loop_infot > loop_info_map
Map from loop identifier to loop info struct.
bool is_valid_loop_or_top_level_id(const std::size_t id) const
True iff id is in the valid range for a loop id or is equal to the top level id for this function.
Class interface to library types and functions defined in cprover_contracts.c.
std::map< dfcc_typet, typet > dfcc_type
Maps enum values to the actual types (dynamically loaded)
Describes a single loop for the purpose of DFCC loop contract instrumentation.
const symbol_exprt addr_of_write_set_var
Symbol representing pointer to the stack allocated write set object for this loop.
void output(std::ostream &out) const
Prints a textual representation of the struct to out.
const exprt::operandst decreases
Decreases clause expression.
const std::unordered_set< irep_idt > local
Set of local identifiers locally DECL in loop instructions, excluding identifiers declared in nested ...
const std::set< exprt > assigns
Set of targets assigned by the loop, either user-provided or inferred.
const std::size_t cbmc_loop_id
Loop identifier assigned to this loop by traditional CBMC loop numbering.
std::optional< goto_programt::targett > find_latch(goto_programt &goto_program) const
const std::unordered_set< irep_idt > tracked
Subset of locals that must be tracked in the loop's write set.
const std::size_t loop_id
Loop identifier assigned by DFCC to this loop.
const exprt invariant
Loop invariant expression.
const std::set< std::size_t > outer_loops
Integer identifier of the outer loop(s) if they exists.
const std::set< std::size_t > inner_loops
Integer identifiers of inner loops of that loop.
std::optional< goto_programt::targett > find_head(goto_programt &goto_program) const
Finds the first instruction tagged as loop head and having the same loop identifier as this struct in...
const symbol_exprt write_set_var
Symbol representing the stack-allocated write set object for this loop.
Dirty variables are ones which have their address taken so we can't reliably work out where they may ...
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Base class for all expressions.
std::vector< exprt > operandst
A goto function, consisting of function body (see body) and parameter identifiers (see parameter_iden...
parameter_identifierst parameter_identifiers
The identifiers of the parameters of this function.
goto_functionst goto_functions
GOTO functions.
A generic container class for the GOTO intermediate representation of one function.
instructionst instructions
The list of instructions in the goto program.
instructionst::iterator targett
instructionst::const_iterator const_targett
Thrown when we can't handle something in an input source file.
Class that provides messages with a built-in verbosity 'level'.
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
The symbol table base class interface.
The Boolean constant true.
static bool is_assigned(dirtyt &dirty, const irep_idt &ident, assignst assigns)
static bool has_contract(const goto_programt::const_targett &latch_target, const bool check_side_effect)
Returns true iff some contract clause expression is attached to the latch condition of this loop.
static void tag_loop_instructions(goto_programt &goto_program, dfcc_loop_nesting_grapht &loop_nesting_graph)
Tags instructions of loops found in loop_nesting_graph with the loop identifier of the innermost loop...
static std::optional< goto_programt::targett > check_inner_loops_have_contracts(const dfcc_loop_nesting_grapht &loop_nesting_graph, const bool check_side_effect)
Traverses the loop nesting graph from top level loops and checks if all loops nested in loops that ha...
static bool must_check_lhs_from_local_and_tracked(const exprt &lhs, const std::unordered_set< irep_idt > &local, const std::unordered_set< irep_idt > &tracked)
Returns true if the lhs to an assignment must be checked against its write set.
static struct contract_clausest default_loop_contract_clauses(const dfcc_loop_nesting_grapht &loop_nesting_graph, const std::size_t loop_id, const irep_idt &function_id, const assignst &inferred_assigns, const bool check_side_effect, message_handlert &message_handler, const namespacet &ns)
Generate defaults for all contract clauses of the loop with ID loop_id if at least one type of clause...
static dfcc_loop_infot gen_dfcc_loop_info(const dfcc_loop_nesting_grapht &loop_nesting_graph, const std::size_t loop_id, const irep_idt &function_id, goto_functiont &goto_function, const std::map< std::size_t, dfcc_loop_infot > &loop_info_map, dirtyt &dirty, const assignst &inferred_assigns, const bool check_side_effect, message_handlert &message_handler, dfcc_libraryt &library, symbol_table_baset &symbol_table)
static std::optional< goto_programt::targett > check_has_contract_rec(const dfcc_loop_nesting_grapht &loop_nesting_graph, const std::size_t loop_idx, const bool must_have_contract, const bool check_side_effect)
static std::unordered_set< irep_idt > gen_tracked_set(const std::vector< std::size_t > &inner_loops_ids, const std::unordered_set< irep_idt > &locals, dirtyt &dirty, const std::map< std::size_t, dfcc_loop_infot > &loop_info_map)
Compute subset of locals that must be tracked in the loop's write set.
Class that computes CFG information about the loop structure of a GOTO function for the purpose of dy...
std::unordered_set< irep_idt > gen_loop_locals_set(const irep_idt &function_id, goto_functiont &goto_function, const dfcc_loop_nesting_graph_nodet &loop_node, message_handlert &message_handler, const namespacet &ns)
Collect identifiers that are local to this loop.
void dfcc_infer_loop_assigns_for_function(std::map< std::size_t, assignst > &inferred_loop_assigns_map, goto_functionst &goto_functions, const goto_functiont &goto_function, message_handlert &message_handler, const namespacet &ns)
Infer assigns clause targets for loops in goto_function from their instructions and an alias analysis...
Infer a set of assigns clause targets for a natural loop.
bool dfcc_is_cprover_static_symbol(const irep_idt &id)
Returns true iff the symbol is one of the known CPROVER static instrumentation variables or ends with...
Dynamic frame condition checking library loading.
@ WRITE_SET
type of descriptors of assignable/freeable sets of locations
@ WRITE_SET_PTR
type of pointers to descriptors of assignable/freeable sets of locations
dfcc_loop_nesting_grapht build_loop_nesting_graph(goto_programt &goto_program)
Builds a graph instance describing the nesting structure of natural loops in the given goto_program.
Builds a graph describing how loops are nested in a GOTO program.
std::unordered_set< exprt, irep_hash > dfcc_root_objects(const exprt &expr)
Computes a set of root object expressions from an lvalue or assigns clause target expression.
Utility functions that compute root object expressions for assigns clause targets and LHS expressions...
Dynamic frame condition checking utility functions.
Fresh auxiliary symbol creation.
#define Forall_goto_program_instructions(it, program)
std::set< exprt > assignst
bool has_contract(const irep_idt &function_identifier, const namespacet &ns)
Field-insensitive, location-sensitive may-alias analysis.
Compute natural loops in a goto_function.
API to expression classes for Pointers.
#define PRECONDITION(CONDITION)
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
exprt conjunction(exprt a, exprt b)
Conjunction of two expressions.
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
exprt::operandst decreases_clauses
contract_clausest(const exprt::operandst &decreases)
static symbol_exprt create_symbol(symbol_table_baset &, const typet &type, const irep_idt &function_id, const std::string &base_name, const source_locationt &source_location)
Adds a new symbol named function_id::base_name of type type with given attributes in the symbol table...
Loop contract configurations.
bool apply_loop_contracts
exprt get_loop_assigns(const goto_programt::const_targett &loop_end)
Extract loop assigns from annotated loop end.
void simplify_gotos(goto_programt &goto_program, const namespacet &ns)
Turns goto instructions IF cond GOTO label where the condition statically simplifies to false into SK...
exprt get_loop_invariants(const goto_programt::const_targett &loop_end, const bool check_side_effect)
Extract loop invariants from annotated loop end.
exprt get_loop_decreases(const goto_programt::const_targett &loop_end, const bool check_side_effect)
Extract loop decreases from annotated loop end.