Skip to content

Commit 9d1e625

Browse files
authored
Merge pull request #1641 from karkhaz/kk-big-6-6
Add support for path exploration to CBMC/JBMC, in addition to full bounded model checking
2 parents 6d657a0 + d67fa60 commit 9d1e625

29 files changed

+908
-377
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ src/goto-cc/goto-cc.exe
101101
src/goto-cc/goto-cl.exe
102102
src/goto-instrument/goto-instrument
103103
src/goto-instrument/goto-instrument.exe
104+
src/jbmc/jbmc
104105
src/musketeer/musketeer
105106
src/musketeer/musketeer.exe
106107
src/symex/symex

scripts/cpplint.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -3625,8 +3625,8 @@ def CheckOperatorSpacing(filename, clean_lines, linenum, error):
36253625
# 'Remove spaces around %s' % match.group(0))
36263626

36273627
# check any inherited classes don't have a space between the type and the :
3628-
if Search(r'(struct|class)\s[\w_]*\s+:', line):
3629-
error(filename, linenum, 'readability/identifier_spacing', 4, 'There shouldn\'t be a space between class identifier and :')
3628+
if Search(r'(struct|class)\s[\w_]*:', line):
3629+
error(filename, linenum, 'readability/identifier_spacing', 4, 'There should be a space between class identifier and :')
36303630

36313631
#check type definitions end with t
36323632
# Look for class declarations and check the final character is a t

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

+121-2
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,24 @@ Author: Daniel Kroening, [email protected]
1212
#include "bmc.h"
1313

1414
#include <chrono>
15+
#include <exception>
1516
#include <fstream>
1617
#include <iostream>
1718
#include <memory>
1819

20+
#include <util/exit_codes.h>
1921
#include <util/string2int.h>
2022
#include <util/source_location.h>
2123
#include <util/string_utils.h>
24+
#include <util/memory_info.h>
2225
#include <util/message.h>
2326
#include <util/json.h>
2427
#include <util/cprover_prefix.h>
2528

2629
#include <langapi/mode.h>
2730
#include <langapi/language_util.h>
2831

32+
#include <goto-programs/goto_model.h>
2933
#include <goto-programs/xml_goto_trace.h>
3034
#include <goto-programs/json_goto_trace.h>
3135
#include <goto-programs/graphml_witness.h>
@@ -37,6 +41,7 @@ Author: Daniel Kroening, [email protected]
3741
#include <goto-symex/memory_model_tso.h>
3842
#include <goto-symex/memory_model_pso.h>
3943

44+
#include "cbmc_solvers.h"
4045
#include "counterexample_beautification.h"
4146
#include "fault_localization.h"
4247

@@ -359,8 +364,7 @@ safety_checkert::resultt bmct::execute(const goto_functionst &goto_functions)
359364
{
360365
try
361366
{
362-
// perform symbolic execution
363-
symex.symex_from_entry_point_of(goto_functions);
367+
perform_symbolic_execution(goto_functions);
364368

365369
// add a partial ordering, if required
366370
if(equation.has_threads())
@@ -595,3 +599,118 @@ void bmct::setup_unwind()
595599
if(options.get_option("unwind")!="")
596600
symex.set_unwind_limit(options.get_unsigned_int_option("unwind"));
597601
}
602+
603+
int bmct::do_language_agnostic_bmc(
604+
const optionst &opts,
605+
const goto_modelt &goto_model,
606+
const ui_message_handlert::uit &ui,
607+
messaget &message,
608+
std::function<void(bmct &, const goto_modelt &)> frontend_configure_bmc)
609+
{
610+
message_handlert &mh = message.get_message_handler();
611+
safety_checkert::resultt result;
612+
goto_symext::branch_worklistt worklist;
613+
try
614+
{
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+
}
673+
}
674+
catch(const char *error_msg)
675+
{
676+
message.error() << error_msg << message.eom;
677+
return CPROVER_EXIT_EXCEPTION;
678+
}
679+
catch(const std::string &error_msg)
680+
{
681+
message.error() << error_msg << message.eom;
682+
return CPROVER_EXIT_EXCEPTION;
683+
}
684+
catch(...)
685+
{
686+
message.error() << "unable to get solver" << message.eom;
687+
throw std::current_exception();
688+
}
689+
690+
switch(result)
691+
{
692+
case safety_checkert::resultt::SAFE:
693+
return CPROVER_EXIT_VERIFICATION_SAFE;
694+
case safety_checkert::resultt::UNSAFE:
695+
return CPROVER_EXIT_VERIFICATION_UNSAFE;
696+
case safety_checkert::resultt::ERROR:
697+
return CPROVER_EXIT_INTERNAL_ERROR;
698+
}
699+
UNREACHABLE;
700+
}
701+
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+
}
710+
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);
716+
}

0 commit comments

Comments
 (0)