Skip to content

Commit e8eec2c

Browse files
committed
[path explore 5/7] Support path-based exploratio2
This commit introduces the --paths flag to CBMC, which makes CBMC model-check each individual path of the program rather than merging paths and model-checking the entire program. The overall strategy involves allowing the "state" of symbolic execution---i.e., a pair of goto_symex_statet and symex_target_equationt ---to be saved during an execution, and for an execution to be resumed from a saved pair. By saving the state at every branch point and disabling path merging, symbolic execution only runs along one path but populates a worklist of saved states that should be executed later. At the top level, CBMC or JBMC loops while the worklist of saved states is non-empty, creating a new bmct object to resume executing each saved path. This commit includes the following supporting changes: - goto_symex_statet now owns a symbol table, separate from the one supporting the goto_program, which is used for dynamically created objects during symbolic execution. goto_symex was previously using a symbol table that was passed to it by reference for this purpose, but that symbol table is needed when resuming symbolic execution from a saved point and so ought properly to be part of the symbolic execution state. - goto_symex_statet now has a pointer to a symex_target_equationt, which can be updated with an equation from a previously-saved symbolic execution run. While equations are also conceptually part of the state of symbolic execution, they are heavily used after symbolic execution has completed (and the symex state has been deallocated) and so the equation is not owned by the state. An explicit copy constructor has been added to goto_symex_statet that initializes the equation member, so that symbolic execution can proceed either with an empty equation or with an equation that was earlier saved. - goto_symex_statet no longer has a pointer to a dirtyt, as this was hindering it from being copied.
1 parent c88a3ab commit e8eec2c

24 files changed

+615
-226
lines changed

src/analyses/dirty.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ void dirtyt::find_dirty_address_of(const exprt &expr)
6969

7070
void dirtyt::output(std::ostream &out) const
7171
{
72+
die_if_uninitialized();
7273
for(const auto &d : dirty)
7374
out << d << '\n';
7475
}

src/analyses/dirty.h

+36-5
Original file line numberDiff line numberDiff line change
@@ -17,49 +17,80 @@ Date: March 2013
1717
#include <unordered_set>
1818

1919
#include <util/std_expr.h>
20+
#include <util/invariant.h>
2021
#include <goto-programs/goto_functions.h>
2122

2223
class dirtyt
2324
{
25+
private:
26+
void die_if_uninitialized() const
27+
{
28+
INVARIANT(
29+
initialized,
30+
"Uninitialized dirtyt. This dirtyt was constructed using the default "
31+
"constructor and not subsequently initialized using "
32+
"dirtyt::build().");
33+
}
34+
2435
public:
36+
bool initialized;
2537
typedef std::unordered_set<irep_idt, irep_id_hash> id_sett;
2638
typedef goto_functionst::goto_functiont goto_functiont;
2739

28-
dirtyt()
40+
/// \post dirtyt objects that are created through this constructor are not
41+
/// safe to use. If you copied a dirtyt (for example, by adding an object
42+
/// that owns a dirtyt to a container and then copying it back out), you will
43+
/// need to re-initialize the dirtyt by calling dirtyt::build().
44+
dirtyt() : initialized(false)
2945
{
3046
}
3147

32-
explicit dirtyt(const goto_functiont &goto_function)
48+
explicit dirtyt(const goto_functiont &goto_function) : initialized(false)
3349
{
3450
build(goto_function);
51+
initialized = true;
3552
}
3653

37-
explicit dirtyt(const goto_functionst &goto_functions)
54+
explicit dirtyt(const goto_functionst &goto_functions) : initialized(false)
3855
{
39-
forall_goto_functions(it, goto_functions)
40-
build(it->second);
56+
build(goto_functions);
57+
// build(g_funs) responsible for setting initialized to true, since
58+
// it is public and can be called independently
4159
}
4260

4361
void output(std::ostream &out) const;
4462

4563
bool operator()(const irep_idt &id) const
4664
{
65+
die_if_uninitialized();
4766
return dirty.find(id)!=dirty.end();
4867
}
4968

5069
bool operator()(const symbol_exprt &expr) const
5170
{
71+
die_if_uninitialized();
5272
return operator()(expr.get_identifier());
5373
}
5474

5575
const id_sett &get_dirty_ids() const
5676
{
77+
die_if_uninitialized();
5778
return dirty;
5879
}
5980

6081
void add_function(const goto_functiont &goto_function)
6182
{
6283
build(goto_function);
84+
initialized = true;
85+
}
86+
87+
void build(const goto_functionst &goto_functions)
88+
{
89+
// dirtyts should not be initialized twice
90+
PRECONDITION(!initialized);
91+
forall_goto_functions(it, goto_functions)
92+
build(it->second);
93+
initialized = true;
6394
}
6495

6596
protected:

src/cbmc/bmc.cpp

+82-31
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Author: Daniel Kroening, [email protected]
2929
#include <langapi/mode.h>
3030
#include <langapi/language_util.h>
3131

32+
#include <goto-programs/goto_model.h>
3233
#include <goto-programs/xml_goto_trace.h>
3334
#include <goto-programs/json_goto_trace.h>
3435
#include <goto-programs/graphml_witness.h>
@@ -363,8 +364,7 @@ safety_checkert::resultt bmct::execute(const goto_functionst &goto_functions)
363364
{
364365
try
365366
{
366-
// perform symbolic execution
367-
symex.symex_from_entry_point_of(goto_functions);
367+
perform_symbolic_execution(goto_functions);
368368

369369
// add a partial ordering, if required
370370
if(equation.has_threads())
@@ -607,13 +607,69 @@ int bmct::do_language_agnostic_bmc(
607607
messaget &message,
608608
std::function<void(bmct &, const goto_modelt &)> frontend_configure_bmc)
609609
{
610-
cbmc_solverst solvers(
611-
opts, goto_model.symbol_table, message.get_message_handler());
612-
solvers.set_ui(ui);
613-
std::unique_ptr<cbmc_solverst::solvert> solver;
610+
message_handlert &mh = message.get_message_handler();
611+
safety_checkert::resultt result;
612+
goto_symext::branch_worklistt worklist;
614613
try
615614
{
616-
solver = solvers.get_solver();
615+
{
616+
cbmc_solverst solvers(
617+
opts, goto_model.symbol_table, message.get_message_handler());
618+
solvers.set_ui(ui);
619+
std::unique_ptr<cbmc_solverst::solvert> cbmc_solver;
620+
cbmc_solver = solvers.get_solver();
621+
prop_convt &pc = cbmc_solver->prop_conv();
622+
bmct bmc(opts, goto_model.symbol_table, mh, pc, worklist);
623+
bmc.set_ui(ui);
624+
frontend_configure_bmc(bmc, goto_model);
625+
result = bmc.run(goto_model.goto_functions);
626+
}
627+
INVARIANT(
628+
opts.get_bool_option("paths") || worklist.empty(),
629+
"the worklist should be empty after doing full-program "
630+
"model checking, but the worklist contains " +
631+
std::to_string(worklist.size()) + " unexplored branches.");
632+
633+
// When model checking, the bmc.run() above will already have explored
634+
// the entire program, and result contains the verification result. The
635+
// worklist (containing paths that have not yet been explored) is thus
636+
// empty, and we don't enter this loop.
637+
//
638+
// When doing path exploration, there will be some saved paths left to
639+
// explore in the worklist. We thus need to run the above code again,
640+
// once for each saved path in the worklist, to continue symbolically
641+
// execute the program. Note that the code in the loop is similar to
642+
// the code above except that we construct a path_explorert rather than
643+
// a bmct, which allows us to execute from a saved state rather than
644+
// from the entry point. See the path_explorert documentation, and the
645+
// difference between the implementations of perform_symbolic_exection()
646+
// in bmct and path_explorert, for more information.
647+
648+
while(!worklist.empty())
649+
{
650+
message.status() << "___________________________\n"
651+
<< "Starting new path (" << worklist.size()
652+
<< " to go)\n"
653+
<< message.eom;
654+
cbmc_solverst solvers(
655+
opts, goto_model.symbol_table, message.get_message_handler());
656+
solvers.set_ui(ui);
657+
std::unique_ptr<cbmc_solverst::solvert> cbmc_solver;
658+
cbmc_solver = solvers.get_solver();
659+
prop_convt &pc = cbmc_solver->prop_conv();
660+
goto_symext::branch_pointt &resume = worklist.front();
661+
path_explorert pe(
662+
opts,
663+
goto_model.symbol_table,
664+
mh,
665+
pc,
666+
resume.equation,
667+
resume.state,
668+
worklist);
669+
frontend_configure_bmc(pe, goto_model);
670+
result &= pe.run(goto_model.goto_functions);
671+
worklist.pop_front();
672+
}
617673
}
618674
catch(const char *error_msg)
619675
{
@@ -631,35 +687,30 @@ int bmct::do_language_agnostic_bmc(
631687
throw std::current_exception();
632688
}
633689

634-
bmct bmc(
635-
opts,
636-
goto_model.symbol_table,
637-
message.get_message_handler(),
638-
solver->prop_conv());
639-
640-
frontend_configure_bmc(bmc, goto_model);
641-
bmc.set_ui(ui);
642-
643-
int result = CPROVER_EXIT_INTERNAL_ERROR;
644-
645-
// do actual BMC
646-
switch(bmc.run(goto_model.goto_functions))
690+
switch(result)
647691
{
648692
case safety_checkert::resultt::SAFE:
649-
result = CPROVER_EXIT_VERIFICATION_SAFE;
650-
break;
693+
return CPROVER_EXIT_VERIFICATION_SAFE;
651694
case safety_checkert::resultt::UNSAFE:
652-
result = CPROVER_EXIT_VERIFICATION_UNSAFE;
653-
break;
695+
return CPROVER_EXIT_VERIFICATION_UNSAFE;
654696
case safety_checkert::resultt::ERROR:
655-
result = CPROVER_EXIT_INTERNAL_ERROR;
656-
break;
697+
return CPROVER_EXIT_INTERNAL_ERROR;
657698
}
699+
UNREACHABLE;
700+
}
658701

659-
// let's log some more statistics
660-
message.debug() << "Memory consumption:" << messaget::endl;
661-
memory_info(message.debug());
662-
message.debug() << eom;
702+
void bmct::perform_symbolic_execution(const goto_functionst &goto_functions)
703+
{
704+
symex.symex_from_entry_point_of(goto_functions, symex_symbol_table);
705+
INVARIANT(
706+
options.get_bool_option("paths") || branch_worklist.empty(),
707+
"Branch points were saved even though we should have been "
708+
"executing the entire program and merging paths");
709+
}
663710

664-
return result;
711+
void path_explorert::perform_symbolic_execution(
712+
const goto_functionst &goto_functions)
713+
{
714+
symex.resume_symex_from_saved_state(
715+
goto_functions, saved_state, &equation, symex_symbol_table);
665716
}

0 commit comments

Comments
 (0)