diff --git a/.gitignore b/.gitignore index ea8be88725b..9693fb2faef 100644 --- a/.gitignore +++ b/.gitignore @@ -101,6 +101,7 @@ src/goto-cc/goto-cc.exe src/goto-cc/goto-cl.exe src/goto-instrument/goto-instrument src/goto-instrument/goto-instrument.exe +src/jbmc/jbmc src/musketeer/musketeer src/musketeer/musketeer.exe src/symex/symex diff --git a/scripts/cpplint.py b/scripts/cpplint.py index 4b4aac788f8..bb08391f4d9 100755 --- a/scripts/cpplint.py +++ b/scripts/cpplint.py @@ -3625,8 +3625,8 @@ def CheckOperatorSpacing(filename, clean_lines, linenum, error): # 'Remove spaces around %s' % match.group(0)) # check any inherited classes don't have a space between the type and the : - if Search(r'(struct|class)\s[\w_]*\s+:', line): - error(filename, linenum, 'readability/identifier_spacing', 4, 'There shouldn\'t be a space between class identifier and :') + if Search(r'(struct|class)\s[\w_]*:', line): + error(filename, linenum, 'readability/identifier_spacing', 4, 'There should be a space between class identifier and :') #check type definitions end with t # Look for class declarations and check the final character is a t diff --git a/src/analyses/dirty.cpp b/src/analyses/dirty.cpp index b40e057a975..66c40141db8 100644 --- a/src/analyses/dirty.cpp +++ b/src/analyses/dirty.cpp @@ -69,6 +69,7 @@ void dirtyt::find_dirty_address_of(const exprt &expr) void dirtyt::output(std::ostream &out) const { + die_if_uninitialized(); for(const auto &d : dirty) out << d << '\n'; } diff --git a/src/analyses/dirty.h b/src/analyses/dirty.h index 880d01cb3ee..a50b8dbcaad 100644 --- a/src/analyses/dirty.h +++ b/src/analyses/dirty.h @@ -17,49 +17,80 @@ Date: March 2013 #include #include +#include #include class dirtyt { +private: + void die_if_uninitialized() const + { + INVARIANT( + initialized, + "Uninitialized dirtyt. This dirtyt was constructed using the default " + "constructor and not subsequently initialized using " + "dirtyt::build()."); + } + public: + bool initialized; typedef std::unordered_set id_sett; typedef goto_functionst::goto_functiont goto_functiont; - dirtyt() + /// \post dirtyt objects that are created through this constructor are not + /// safe to use. If you copied a dirtyt (for example, by adding an object + /// that owns a dirtyt to a container and then copying it back out), you will + /// need to re-initialize the dirtyt by calling dirtyt::build(). + dirtyt() : initialized(false) { } - explicit dirtyt(const goto_functiont &goto_function) + explicit dirtyt(const goto_functiont &goto_function) : initialized(false) { build(goto_function); + initialized = true; } - explicit dirtyt(const goto_functionst &goto_functions) + explicit dirtyt(const goto_functionst &goto_functions) : initialized(false) { - forall_goto_functions(it, goto_functions) - build(it->second); + build(goto_functions); + // build(g_funs) responsible for setting initialized to true, since + // it is public and can be called independently } void output(std::ostream &out) const; bool operator()(const irep_idt &id) const { + die_if_uninitialized(); return dirty.find(id)!=dirty.end(); } bool operator()(const symbol_exprt &expr) const { + die_if_uninitialized(); return operator()(expr.get_identifier()); } const id_sett &get_dirty_ids() const { + die_if_uninitialized(); return dirty; } void add_function(const goto_functiont &goto_function) { build(goto_function); + initialized = true; + } + + void build(const goto_functionst &goto_functions) + { + // dirtyts should not be initialized twice + PRECONDITION(!initialized); + forall_goto_functions(it, goto_functions) + build(it->second); + initialized = true; } protected: diff --git a/src/cbmc/bmc.cpp b/src/cbmc/bmc.cpp index 9bc86d3eec6..66ff5cb90ce 100644 --- a/src/cbmc/bmc.cpp +++ b/src/cbmc/bmc.cpp @@ -12,13 +12,16 @@ Author: Daniel Kroening, kroening@kroening.com #include "bmc.h" #include +#include #include #include #include +#include #include #include #include +#include #include #include #include @@ -26,6 +29,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include #include #include #include @@ -37,6 +41,7 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include "cbmc_solvers.h" #include "counterexample_beautification.h" #include "fault_localization.h" @@ -359,8 +364,7 @@ safety_checkert::resultt bmct::execute(const goto_functionst &goto_functions) { try { - // perform symbolic execution - symex.symex_from_entry_point_of(goto_functions); + perform_symbolic_execution(goto_functions); // add a partial ordering, if required if(equation.has_threads()) @@ -595,3 +599,118 @@ void bmct::setup_unwind() if(options.get_option("unwind")!="") symex.set_unwind_limit(options.get_unsigned_int_option("unwind")); } + +int bmct::do_language_agnostic_bmc( + const optionst &opts, + const goto_modelt &goto_model, + const ui_message_handlert::uit &ui, + messaget &message, + std::function frontend_configure_bmc) +{ + message_handlert &mh = message.get_message_handler(); + safety_checkert::resultt result; + goto_symext::branch_worklistt worklist; + try + { + { + cbmc_solverst solvers( + opts, goto_model.symbol_table, message.get_message_handler()); + solvers.set_ui(ui); + std::unique_ptr cbmc_solver; + cbmc_solver = solvers.get_solver(); + prop_convt &pc = cbmc_solver->prop_conv(); + bmct bmc(opts, goto_model.symbol_table, mh, pc, worklist); + bmc.set_ui(ui); + frontend_configure_bmc(bmc, goto_model); + result = bmc.run(goto_model.goto_functions); + } + INVARIANT( + opts.get_bool_option("paths") || worklist.empty(), + "the worklist should be empty after doing full-program " + "model checking, but the worklist contains " + + std::to_string(worklist.size()) + " unexplored branches."); + + // When model checking, the bmc.run() above will already have explored + // the entire program, and result contains the verification result. The + // worklist (containing paths that have not yet been explored) is thus + // empty, and we don't enter this loop. + // + // When doing path exploration, there will be some saved paths left to + // explore in the worklist. We thus need to run the above code again, + // once for each saved path in the worklist, to continue symbolically + // execute the program. Note that the code in the loop is similar to + // the code above except that we construct a path_explorert rather than + // a bmct, which allows us to execute from a saved state rather than + // from the entry point. See the path_explorert documentation, and the + // difference between the implementations of perform_symbolic_exection() + // in bmct and path_explorert, for more information. + + while(!worklist.empty()) + { + message.status() << "___________________________\n" + << "Starting new path (" << worklist.size() + << " to go)\n" + << message.eom; + cbmc_solverst solvers( + opts, goto_model.symbol_table, message.get_message_handler()); + solvers.set_ui(ui); + std::unique_ptr cbmc_solver; + cbmc_solver = solvers.get_solver(); + prop_convt &pc = cbmc_solver->prop_conv(); + goto_symext::branch_pointt &resume = worklist.front(); + path_explorert pe( + opts, + goto_model.symbol_table, + mh, + pc, + resume.equation, + resume.state, + worklist); + frontend_configure_bmc(pe, goto_model); + result &= pe.run(goto_model.goto_functions); + worklist.pop_front(); + } + } + catch(const char *error_msg) + { + message.error() << error_msg << message.eom; + return CPROVER_EXIT_EXCEPTION; + } + catch(const std::string &error_msg) + { + message.error() << error_msg << message.eom; + return CPROVER_EXIT_EXCEPTION; + } + catch(...) + { + message.error() << "unable to get solver" << message.eom; + throw std::current_exception(); + } + + switch(result) + { + case safety_checkert::resultt::SAFE: + return CPROVER_EXIT_VERIFICATION_SAFE; + case safety_checkert::resultt::UNSAFE: + return CPROVER_EXIT_VERIFICATION_UNSAFE; + case safety_checkert::resultt::ERROR: + return CPROVER_EXIT_INTERNAL_ERROR; + } + UNREACHABLE; +} + +void bmct::perform_symbolic_execution(const goto_functionst &goto_functions) +{ + symex.symex_from_entry_point_of(goto_functions, symex_symbol_table); + INVARIANT( + options.get_bool_option("paths") || branch_worklist.empty(), + "Branch points were saved even though we should have been " + "executing the entire program and merging paths"); +} + +void path_explorert::perform_symbolic_execution( + const goto_functionst &goto_functions) +{ + symex.resume_symex_from_saved_state( + goto_functions, saved_state, &equation, symex_symbol_table); +} diff --git a/src/cbmc/bmc.h b/src/cbmc/bmc.h index b3f9a7846e1..8357b609aed 100644 --- a/src/cbmc/bmc.h +++ b/src/cbmc/bmc.h @@ -15,9 +15,12 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include #include #include +#include + #include #include #include @@ -28,24 +31,57 @@ Author: Daniel Kroening, kroening@kroening.com #include #include +#include #include #include #include "symex_bmc.h" +class cbmc_solverst; + +/// \brief Bounded model checking or path exploration for goto-programs +/// +/// Higher-level architectural information on symbolic execution is +/// documented in the \ref symex-overview +/// "Symbolic execution module page". class bmct:public safety_checkert { public: + /// \brief Constructor for path exploration with freshly-initialized state + /// + /// This constructor should be used for symbolically executing a program + /// from the entry point with fresh state. There are two main behaviours + /// that can happen when constructing an instance of this class: + /// + /// - If the `--paths` flag in the `options` argument to this + /// constructor is `false` (unset), an instance of this class will + /// symbolically execute the entire program, performing path merging + /// to build a formula corresponding to all executions of the program + /// up to the unwinding limit. In this case, the `branch_worklist` + /// member shall not be touched; this is enforced by the assertion in + /// this class' implementation of bmct::perform_symbolic_execution(). + /// + /// - If the `--paths` flag is `true`, this `bmct` object will explore a + /// single path through the codebase without doing any path merging. + /// If some paths were not taken, the state at those branch points + /// will be appended to `branch_worklist`. After the single path that + /// this `bmct` object executed has been model-checked, you can resume + /// exploring further paths by popping an element from + /// `branch_worklist` and using it to construct a path_explorert + /// object. bmct( const optionst &_options, - const symbol_tablet &_symbol_table, + const symbol_tablet &outer_symbol_table, message_handlert &_message_handler, - prop_convt &_prop_conv) + prop_convt &_prop_conv, + goto_symext::branch_worklistt &_branch_worklist) : safety_checkert(ns, _message_handler), options(_options), - ns(_symbol_table, new_symbol_table), - equation(ns), - symex(_message_handler, ns, new_symbol_table, equation), + outer_symbol_table(outer_symbol_table), + ns(outer_symbol_table, symex_symbol_table), + equation(), + branch_worklist(_branch_worklist), + symex(_message_handler, outer_symbol_table, equation, branch_worklist), prop_conv(_prop_conv), ui(ui_message_handlert::uit::PLAIN) { @@ -82,11 +118,67 @@ class bmct:public safety_checkert symex.add_recursion_unwind_handler(handler); } + /// \brief common BMC code, invoked from language-specific frontends + /// + /// Do bounded model-checking after all language-specific program + /// preprocessing has been completed by language-specific frontends. + /// Language-specific frontends may customize the \ref bmct objects + /// that are used for model-checking by supplying a function object to + /// the `frontend_configure_bmc` parameter of this function; the + /// function object will be called with every \ref bmct object that + /// is constructed by `do_language_agnostic_bmc`. + static int do_language_agnostic_bmc( + const optionst &opts, + const goto_modelt &goto_model, + const ui_message_handlert::uit &ui, + messaget &message, + std::function frontend_configure_bmc = + [](bmct &_bmc, const goto_modelt &) { // NOLINT (*) + // Empty default implementation + }); + protected: + /// \brief Constructor for path exploration from saved state + /// + /// This constructor exists as a delegate for the path_explorert class. + /// It differs from \ref bmct's public constructor in that it actually + /// does something with the branch_worklistt argument, and also takes a + /// symex_target_equationt. See the documentation for path_explorert for + /// details. + bmct( + const optionst &_options, + const symbol_tablet &outer_symbol_table, + message_handlert &_message_handler, + prop_convt &_prop_conv, + symex_target_equationt &_equation, + goto_symext::branch_worklistt &_branch_worklist) + : safety_checkert(ns, _message_handler), + options(_options), + outer_symbol_table(outer_symbol_table), + ns(outer_symbol_table), + equation(_equation), + branch_worklist(_branch_worklist), + symex(_message_handler, outer_symbol_table, equation, branch_worklist), + prop_conv(_prop_conv), + ui(ui_message_handlert::uit::PLAIN) + { + symex.constant_propagation = options.get_bool_option("propagation"); + symex.record_coverage = + !options.get_option("symex-coverage-report").empty(); + INVARIANT( + options.get_bool_option("paths"), + "Should only use saved equation & goto_state constructor " + "when doing path exploration"); + } + const optionst &options; - symbol_tablet new_symbol_table; + /// \brief symbol table for the goto-program that we will execute + const symbol_tablet &outer_symbol_table; + /// \brief symbol table generated during symbolic execution + symbol_tablet symex_symbol_table; namespacet ns; symex_target_equationt equation; + goto_symext::branch_worklistt &branch_worklist; symex_bmct symex; prop_convt &prop_conv; std::unique_ptr memory_model; @@ -144,6 +236,93 @@ class bmct:public safety_checkert template