Skip to content

Commit eb0b86b

Browse files
Petr BauchPetr Bauch
Petr Bauch
authored and
Petr Bauch
committed
Well-formedness checking for SSA
Follows the diffblue#3123 skeleton with additional checks in SSA_stept. Adds a condition such that renaming checks are only run with the validate option.
1 parent 7429ac8 commit eb0b86b

14 files changed

+203
-36
lines changed

src/cbmc/bmc.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -639,6 +639,12 @@ void bmct::perform_symbolic_execution(
639639
goto_symext::get_goto_functiont get_goto_function)
640640
{
641641
symex.symex_from_entry_point_of(get_goto_function, symex_symbol_table);
642+
643+
if(options.get_bool_option("validate-ssa-equation"))
644+
{
645+
symex.validate(ns, validation_modet::INVARIANT);
646+
}
647+
642648
INVARIANT(
643649
options.get_bool_option("paths") || path_storage.empty(),
644650
"Branch points were saved even though we should have been "

src/cbmc/cbmc_parse_options.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,11 @@ void cbmc_parse_optionst::get_command_line_options(optionst &options)
398398
"symex-coverage-report",
399399
cmdline.get_value("symex-coverage-report"));
400400

401+
if(cmdline.isset("validate-ssa-equation"))
402+
{
403+
options.set_option("validate-ssa-equation", true);
404+
}
405+
401406
PARSE_OPTIONS_GOTO_TRACE(cmdline, options);
402407
}
403408

src/goto-symex/goto_symex.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ struct symex_configt final
6161
bool partial_loops;
6262
mp_integer debug_level;
6363

64+
/// \brief Should the additional validation checks be run?
65+
///
66+
/// If this flag is set the checks for renaming (both level1 and level2) are
67+
/// executed in the goto_symex_statet (in the assignment method).
68+
bool run_validation_checks;
69+
6470
explicit symex_configt(const optionst &options);
6571
};
6672

@@ -455,6 +461,11 @@ class goto_symext
455461
"attempting to read remaining_vccs");
456462
return _remaining_vccs;
457463
}
464+
465+
void validate(const namespacet &ns, const validation_modet vm) const
466+
{
467+
target.validate(ns, vm);
468+
}
458469
};
459470

460471
void symex_transition(goto_symext::statet &state);

src/goto-symex/goto_symex_state.cpp

Lines changed: 16 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -121,32 +121,6 @@ static bool check_renaming(const exprt &expr)
121121
return false;
122122
}
123123

124-
static void assert_l1_renaming(const exprt &expr)
125-
{
126-
#if 1
127-
if(check_renaming_l1(expr))
128-
{
129-
std::cerr << expr.pretty() << '\n';
130-
UNREACHABLE;
131-
}
132-
#else
133-
(void)expr;
134-
#endif
135-
}
136-
137-
static void assert_l2_renaming(const exprt &expr)
138-
{
139-
#if 1
140-
if(check_renaming(expr))
141-
{
142-
std::cerr << expr.pretty() << '\n';
143-
UNREACHABLE;
144-
}
145-
#else
146-
(void)expr;
147-
#endif
148-
}
149-
150124
class goto_symex_is_constantt : public is_constantt
151125
{
152126
protected:
@@ -197,15 +171,18 @@ void goto_symex_statet::assignment(
197171
// the type might need renaming
198172
rename(lhs.type(), l1_identifier, ns);
199173
lhs.update_type();
200-
assert_l1_renaming(lhs);
174+
if(run_validation_checks)
175+
{
176+
DATA_INVARIANT(!check_renaming_l1(lhs), "lhs renaming failed on l1");
177+
}
201178

202-
#if 0
179+
#if 0
203180
PRECONDITION(l1_identifier != get_original_name(l1_identifier)
204181
|| l1_identifier=="goto_symex::\\guard"
205182
|| ns.lookup(l1_identifier).is_shared()
206183
|| has_prefix(id2string(l1_identifier), "symex::invalid_object")
207184
|| has_prefix(id2string(l1_identifier), "symex_dynamic::dynamic_object"));
208-
#endif
185+
#endif
209186

210187
// do the l2 renaming
211188
if(level2.current_names.find(l1_identifier)==level2.current_names.end())
@@ -216,8 +193,11 @@ void goto_symex_statet::assignment(
216193
// in case we happen to be multi-threaded, record the memory access
217194
bool is_shared=l2_thread_write_encoding(lhs, ns);
218195

219-
assert_l2_renaming(lhs);
220-
assert_l2_renaming(rhs);
196+
if(run_validation_checks)
197+
{
198+
DATA_INVARIANT(!check_renaming(lhs), "lhs renaming failed on l2");
199+
DATA_INVARIANT(!check_renaming(rhs), "rhs renaming failed on l2");
200+
}
221201

222202
// see #305 on GitHub for a simple example and possible discussion
223203
if(is_shared && lhs.type().id() == ID_pointer && !allow_pointer_unsoundness)
@@ -240,8 +220,11 @@ void goto_symex_statet::assignment(
240220
ssa_exprt l1_lhs(lhs);
241221
get_l1_name(l1_lhs);
242222

243-
assert_l1_renaming(l1_lhs);
244-
assert_l1_renaming(l1_rhs);
223+
if(run_validation_checks)
224+
{
225+
DATA_INVARIANT(!check_renaming_l1(l1_lhs), "lhs renaming failed on l1");
226+
DATA_INVARIANT(!check_renaming_l1(l1_rhs), "rhs renaming failed on l1");
227+
}
245228

246229
value_set.assign(l1_lhs, l1_rhs, ns, rhs_is_simplified, is_shared);
247230
}

src/goto-symex/goto_symex_state.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ class goto_symex_statet final
268268
/// of a GOTO
269269
bool has_saved_next_instruction;
270270

271+
/// \brief Should the additional validation checks be run?
272+
bool run_validation_checks;
273+
271274
private:
272275
/// \brief Dangerous, do not use
273276
///

src/goto-symex/symex_main.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ symex_configt::symex_configt(const optionst &options)
3434
simplify_opt(options.get_bool_option("simplify")),
3535
unwinding_assertions(options.get_bool_option("unwinding-assertions")),
3636
partial_loops(options.get_bool_option("partial-loops")),
37-
debug_level(unsafe_string2int(options.get_option("debug-level")))
37+
debug_level(unsafe_string2int(options.get_option("debug-level"))),
38+
run_validation_checks(options.get_bool_option("validate-ssa-equation"))
3839
{
3940
}
4041

@@ -307,6 +308,8 @@ void goto_symext::symex_from_entry_point_of(
307308

308309
statet state;
309310

311+
state.run_validation_checks = symex_config.run_validation_checks;
312+
310313
initialize_entry_point(
311314
state,
312315
get_goto_function,

src/goto-symex/symex_target_equation.cpp

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -978,3 +978,63 @@ void symex_target_equationt::SSA_stept::output(std::ostream &out) const
978978

979979
out << "Guard: " << format(guard) << '\n';
980980
}
981+
982+
/// Check that the SSA step is well-formed
983+
/// \param ns namespace to lookup identifiers
984+
/// \param vm validation mode to be used for reporting failures
985+
void symex_target_equationt::SSA_stept::validate(
986+
const namespacet &ns,
987+
const validation_modet vm) const
988+
{
989+
validate_full_expr(guard, ns, vm);
990+
991+
switch(type)
992+
{
993+
case goto_trace_stept::typet::ASSERT:
994+
case goto_trace_stept::typet::ASSUME:
995+
case goto_trace_stept::typet::GOTO:
996+
case goto_trace_stept::typet::CONSTRAINT:
997+
validate_full_expr(cond_expr, ns, vm);
998+
break;
999+
case goto_trace_stept::typet::ASSIGNMENT:
1000+
case goto_trace_stept::typet::DECL:
1001+
validate_full_expr(ssa_lhs, ns, vm);
1002+
validate_full_expr(ssa_full_lhs, ns, vm);
1003+
validate_full_expr(original_full_lhs, ns, vm);
1004+
validate_full_expr(ssa_rhs, ns, vm);
1005+
DATA_CHECK(
1006+
vm,
1007+
base_type_eq(ssa_lhs.get_original_expr(), ssa_rhs, ns),
1008+
"Type inequality in SSA assignment\nlhs-type: " +
1009+
ssa_lhs.get_original_expr().type().id_string() +
1010+
"\nrhs-type: " + ssa_rhs.type().id_string());
1011+
break;
1012+
case goto_trace_stept::typet::INPUT:
1013+
case goto_trace_stept::typet::OUTPUT:
1014+
for(const auto &expr : io_args)
1015+
validate_full_expr(expr, ns, vm);
1016+
break;
1017+
case goto_trace_stept::typet::FUNCTION_CALL:
1018+
for(const auto &expr : ssa_function_arguments)
1019+
validate_full_expr(expr, ns, vm);
1020+
case goto_trace_stept::typet::FUNCTION_RETURN:
1021+
{
1022+
const symbolt *symbol;
1023+
DATA_CHECK(
1024+
vm,
1025+
called_function.empty() || !ns.lookup(called_function, symbol),
1026+
"unknown function identifier " + id2string(called_function));
1027+
}
1028+
break;
1029+
case goto_trace_stept::typet::NONE:
1030+
case goto_trace_stept::typet::LOCATION:
1031+
case goto_trace_stept::typet::DEAD:
1032+
case goto_trace_stept::typet::SHARED_READ:
1033+
case goto_trace_stept::typet::SHARED_WRITE:
1034+
case goto_trace_stept::typet::SPAWN:
1035+
case goto_trace_stept::typet::MEMORY_BARRIER:
1036+
case goto_trace_stept::typet::ATOMIC_BEGIN:
1037+
case goto_trace_stept::typet::ATOMIC_END:
1038+
break;
1039+
}
1040+
}

src/goto-symex/symex_target_equation.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,8 @@ class symex_target_equationt:public symex_targett
267267
std::ostream &out) const;
268268

269269
void output(std::ostream &out) const;
270+
271+
void validate(const namespacet &ns, const validation_modet vm) const;
270272
};
271273

272274
std::size_t count_assertions() const
@@ -323,6 +325,12 @@ class symex_target_equationt:public symex_targett
323325
return false;
324326
}
325327

328+
void validate(const namespacet &ns, const validation_modet vm) const
329+
{
330+
for(const SSA_stept &step : SSA_steps)
331+
step.validate(ns, vm);
332+
}
333+
326334
protected:
327335
// for enforcing sharing in the expressions stored
328336
merge_irept merge_irep;

src/util/ssa_expr.h

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,27 @@ class ssa_exprt:public symbol_exprt
139139
/* Used to determine whether or not an identifier can be built
140140
* before trying and getting an exception */
141141
static bool can_build_identifier(const exprt &src);
142+
143+
static void check(
144+
const exprt &expr,
145+
const validation_modet vm = validation_modet::INVARIANT)
146+
{
147+
DATA_CHECK(
148+
vm, !expr.has_operands(), "SSA expression should not have operands");
149+
DATA_CHECK(
150+
vm, expr.id() == ID_symbol, "SSA expression symbols are symbols");
151+
DATA_CHECK(vm, expr.get_bool(ID_C_SSA_symbol), "wrong SSA expression ID");
152+
}
153+
154+
static void validate(
155+
const exprt &expr,
156+
const namespacet &ns,
157+
const validation_modet vm = validation_modet::INVARIANT)
158+
{
159+
check(expr, vm);
160+
validate_full_expr(
161+
static_cast<const exprt &>(expr.find(ID_expression)), ns, vm);
162+
}
142163
};
143164

144165
/*! \brief Cast a generic exprt to an \ref ssa_exprt

src/util/validate_expressions.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Author: Daniel Poetzl
1515

1616
#include "expr.h"
1717
#include "namespace.h"
18+
#include "ssa_expr.h"
1819
#include "std_expr.h"
1920
#include "validate.h"
2021

@@ -32,6 +33,10 @@ void call_on_expr(const exprt &expr, Args &&... args)
3233
{
3334
CALL_ON_EXPR(plus_exprt);
3435
}
36+
else if(expr.get_bool(ID_C_SSA_symbol))
37+
{
38+
CALL_ON_EXPR(ssa_exprt);
39+
}
3540
else
3641
{
3742
#ifdef REPORT_UNIMPLEMENTED_EXPRESSION_CHECKS

src/util/validation_interface.h

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,16 @@ Author: Daniel Poetzl
99
#ifndef CPROVER_UTIL_VALIDATION_INTERFACE_H
1010
#define CPROVER_UTIL_VALIDATION_INTERFACE_H
1111

12-
#define OPT_VALIDATE "(validate-goto-model)"
12+
#define OPT_VALIDATE \
13+
"(validate-goto-model)" \
14+
"(validate-ssa-equation)"
1315

1416
#define HELP_VALIDATE \
1517
" --validate-goto-model enable additional well-formedness checks on " \
1618
"the\n" \
17-
" goto program\n"
19+
" goto program\n" \
20+
" --validate-ssa-equation enable additional well-formedness checks on " \
21+
"the\n" \
22+
" SSA representation\n"
1823

1924
#endif /* CPROVER_UTIL_VALIDATION_INTERFACE_H */

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ SRC += analyses/ai/ai.cpp \
1616
analyses/does_remove_const/is_type_at_least_as_const_as.cpp \
1717
compound_block_locations.cpp \
1818
goto-programs/goto_trace_output.cpp \
19+
goto-symex/ssa_equation.cpp \
1920
interpreter/interpreter.cpp \
2021
json/json_parser.cpp \
2122
path_strategies.cpp \
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
goto-symex
2+
testing-utils
3+
util

unit/goto-symex/ssa_equation.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests for symex_target_equation::validate
4+
5+
Author: Diffblue Ltd.
6+
7+
\*******************************************************************/
8+
9+
#include <goto-symex/symex_target_equation.h>
10+
#include <testing-utils/catch.hpp>
11+
#include <util/arith_tools.h>
12+
13+
SCENARIO("Validation of well-formed SSA steps", "[core][goto-symex][validate]")
14+
{
15+
GIVEN("A program with one function return")
16+
{
17+
symbol_tablet symbol_table;
18+
const typet type1 = signedbv_typet(32);
19+
const code_typet code_type({}, type1);
20+
21+
symbolt fun_symbol;
22+
irep_idt fun_name = "foo";
23+
fun_symbol.name = fun_name;
24+
symbol_exprt fun_foo(fun_name, code_type);
25+
26+
symex_target_equationt equation;
27+
equation.SSA_steps.push_back(symex_target_equationt::SSA_stept());
28+
auto &step = equation.SSA_steps.back();
29+
step.type = goto_trace_stept::typet::FUNCTION_RETURN;
30+
step.called_function = fun_name;
31+
32+
WHEN("Called function is in symbol table")
33+
{
34+
symbol_table.insert(fun_symbol);
35+
namespacet ns(symbol_table);
36+
37+
THEN("The consistency check succeeds")
38+
{
39+
REQUIRE_NOTHROW(equation.validate(ns, validation_modet::INVARIANT));
40+
}
41+
}
42+
43+
WHEN("Called function is not in symbol table")
44+
{
45+
namespacet ns(symbol_table);
46+
47+
THEN("The consistency check fails")
48+
{
49+
REQUIRE_THROWS(equation.validate(ns, validation_modet::EXCEPTION));
50+
}
51+
}
52+
}
53+
}

0 commit comments

Comments
 (0)