Skip to content

Commit 6bc7648

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

14 files changed

+211
-38
lines changed

src/cbmc/bmc.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,12 @@ void bmct::perform_symbolic_execution(
638638
goto_symext::get_goto_functiont get_goto_function)
639639
{
640640
symex.symex_from_entry_point_of(get_goto_function, symex_symbol_table);
641+
642+
if(options.get_bool_option("validate-ssa-equation"))
643+
{
644+
symex.validate(ns, validation_modet::INVARIANT);
645+
}
646+
641647
INVARIANT(
642648
options.get_bool_option("paths") || path_storage.empty(),
643649
"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
@@ -395,6 +395,11 @@ void cbmc_parse_optionst::get_command_line_options(optionst &options)
395395
"symex-coverage-report",
396396
cmdline.get_value("symex-coverage-report"));
397397

398+
if(cmdline.isset("validate-ssa-equation"))
399+
{
400+
options.set_option("validate-ssa-equation", true);
401+
}
402+
398403
PARSE_OPTIONS_GOTO_TRACE(cmdline, options);
399404
}
400405

src/goto-symex/goto_symex.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,8 @@ class goto_symext
7676
path_storage(path_storage),
7777
path_segment_vccs(0),
7878
_total_vccs(std::numeric_limits<unsigned>::max()),
79-
_remaining_vccs(std::numeric_limits<unsigned>::max())
79+
_remaining_vccs(std::numeric_limits<unsigned>::max()),
80+
run_validation_checks(options.get_bool_option("validate-ssa-equation"))
8081
{
8182
}
8283

@@ -183,6 +184,12 @@ class goto_symext
183184
/// successor state to continue executing, and resume symex from that state.
184185
bool should_pause_symex;
185186

187+
/// \brief Should the additional validation checks be run
188+
///
189+
/// If this flag is set the checks for renaming (both level1 and level2) are
190+
/// executed in the goto_symex_statet (in the assignment method).
191+
bool run_validation_checks;
192+
186193
protected:
187194
/// Initialise the symbolic execution and the given state with <code>pc</code>
188195
/// as entry point.
@@ -518,6 +525,11 @@ class goto_symext
518525
"attempting to read remaining_vccs");
519526
return _remaining_vccs;
520527
}
528+
529+
void validate(const namespacet &ns, const validation_modet vm) const
530+
{
531+
target.validate(ns, vm);
532+
}
521533
};
522534

523535
#endif // CPROVER_GOTO_SYMEX_GOTO_SYMEX_H

src/goto-symex/goto_symex_state.cpp

Lines changed: 20 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -165,32 +165,6 @@ static bool check_renaming(const exprt &expr)
165165
return false;
166166
}
167167

168-
static void assert_l1_renaming(const exprt &expr)
169-
{
170-
#if 1
171-
if(check_renaming_l1(expr))
172-
{
173-
std::cerr << expr.pretty() << '\n';
174-
UNREACHABLE;
175-
}
176-
#else
177-
(void)expr;
178-
#endif
179-
}
180-
181-
static void assert_l2_renaming(const exprt &expr)
182-
{
183-
#if 1
184-
if(check_renaming(expr))
185-
{
186-
std::cerr << expr.pretty() << '\n';
187-
UNREACHABLE;
188-
}
189-
#else
190-
(void)expr;
191-
#endif
192-
}
193-
194168
class goto_symex_is_constantt : public is_constantt
195169
{
196170
protected:
@@ -241,15 +215,19 @@ void goto_symex_statet::assignment(
241215
// the type might need renaming
242216
rename(lhs.type(), l1_identifier, ns);
243217
lhs.update_type();
244-
assert_l1_renaming(lhs);
245-
246-
#if 0
218+
const validation_modet local_validation_mode = validation_modet::INVARIANT;
219+
if(run_validation_checks)
220+
{
221+
const validation_modet vm = local_validation_mode;
222+
DATA_CHECK(!check_renaming_l1(lhs), "lhs renaming failed on l1");
223+
}
224+
#if 0
247225
PRECONDITION(l1_identifier != get_original_name(l1_identifier)
248226
|| l1_identifier=="goto_symex::\\guard"
249227
|| ns.lookup(l1_identifier).is_shared()
250228
|| has_prefix(id2string(l1_identifier), "symex::invalid_object")
251229
|| has_prefix(id2string(l1_identifier), "symex_dynamic::dynamic_object"));
252-
#endif
230+
#endif
253231

254232
// do the l2 renaming
255233
if(level2.current_names.find(l1_identifier)==level2.current_names.end())
@@ -260,9 +238,12 @@ void goto_symex_statet::assignment(
260238
// in case we happen to be multi-threaded, record the memory access
261239
bool is_shared=l2_thread_write_encoding(lhs, ns);
262240

263-
assert_l2_renaming(lhs);
264-
assert_l2_renaming(rhs);
265-
241+
if(run_validation_checks)
242+
{
243+
const validation_modet vm = local_validation_mode;
244+
DATA_CHECK(!check_renaming(lhs), "lhs renaming failed on l2");
245+
DATA_CHECK(!check_renaming(rhs), "rhs renaming failed on l2");
246+
}
266247
// see #305 on GitHub for a simple example and possible discussion
267248
if(is_shared && lhs.type().id() == ID_pointer && !allow_pointer_unsoundness)
268249
throw unsupported_operation_exceptiont(
@@ -284,8 +265,12 @@ void goto_symex_statet::assignment(
284265
ssa_exprt l1_lhs(lhs);
285266
get_l1_name(l1_lhs);
286267

287-
assert_l1_renaming(l1_lhs);
288-
assert_l1_renaming(l1_rhs);
268+
if(run_validation_checks)
269+
{
270+
const validation_modet vm = local_validation_mode;
271+
DATA_CHECK(!check_renaming_l1(l1_lhs), "lhs renaming failed on l1");
272+
DATA_CHECK(!check_renaming_l1(l1_rhs), "rhs renaming failed on l1");
273+
}
289274

290275
value_set.assign(l1_lhs, l1_rhs, ns, rhs_is_simplified, is_shared);
291276
}

src/goto-symex/goto_symex_state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ class goto_symex_statet final
390390
/// of a GOTO
391391
bool has_saved_next_instruction;
392392
bool saved_target_is_backwards;
393+
bool run_validation_checks;
393394

394395
private:
395396
/// \brief Dangerous, do not use

src/goto-symex/symex_main.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,8 @@ void goto_symext::symex_from_entry_point_of(
301301

302302
statet state;
303303

304+
state.run_validation_checks = run_validation_checks;
305+
304306
initialize_entry_point(
305307
state,
306308
get_goto_function,

src/goto-symex/symex_target_equation.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -974,3 +974,61 @@ void symex_target_equationt::SSA_stept::output(std::ostream &out) const
974974

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

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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,25 @@ 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(!expr.has_operands(), "SSA expression should not have operands");
148+
DATA_CHECK(expr.id() == ID_symbol, "SSA expression symbols are symbols");
149+
DATA_CHECK(expr.get_bool(ID_C_SSA_symbol), "wrong SSA expression ID");
150+
}
151+
152+
static void validate(
153+
const exprt &expr,
154+
const namespacet &ns,
155+
const validation_modet vm = validation_modet::INVARIANT)
156+
{
157+
check(expr, vm);
158+
validate_full_expr(
159+
static_cast<const exprt &>(expr.find(ID_expression)), ns, vm);
160+
}
142161
};
143162

144163
/*! \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: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
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+
equation.validate(ns, validation_modet::INVARIANT);
40+
REQUIRE(true);
41+
}
42+
}
43+
44+
WHEN("Called function is not in symbol table")
45+
{
46+
namespacet ns(symbol_table);
47+
48+
THEN("The consistency check fails")
49+
{
50+
bool caught = false;
51+
try
52+
{
53+
equation.validate(ns, validation_modet::EXCEPTION);
54+
}
55+
catch(incorrect_goto_program_exceptiont &e)
56+
{
57+
caught = true;
58+
}
59+
REQUIRE(caught);
60+
}
61+
}
62+
}
63+
}

0 commit comments

Comments
 (0)