Skip to content

Commit c40694a

Browse files
author
Sonny Martin
committed
Validation checks for Goto Programs
There are several checks, implemented with separate methods, and some flagged as optional. NB use of validate_goto_model(*this, vm) in goto_functions.h is to avoid circular inclusion as function_mapt is a nested type and so cannot be forward declared.
1 parent dff1e15 commit c40694a

File tree

5 files changed

+647
-0
lines changed

5 files changed

+647
-0
lines changed

src/cbmc/cbmc_parse_options.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,14 @@ int cbmc_parse_optionst::get_goto_program(
592592
if(cbmc_parse_optionst::process_goto_program(goto_model, options, log))
593593
return CPROVER_EXIT_INTERNAL_ERROR;
594594

595+
#if 1 // TODO remove this call location once appropriate \
596+
// optional flags are set \
597+
// (currently with '--validate-goto-model' fails due to finding \
598+
// fn pointers \
599+
// At this location all checks pass
600+
validate_goto_model(goto_model.goto_functions, validation_modet::INVARIANT);
601+
#endif
602+
595603
// show it?
596604
if(cmdline.isset("show-loops"))
597605
{

src/goto-programs/goto_functions.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Date: June 2003
1616

1717
#include "goto_function.h"
1818

19+
#include "validate_goto_model.h"
1920
#include <util/cprover_prefix.h>
2021

2122
/// A collection of goto functions
@@ -121,6 +122,9 @@ class goto_functionst
121122
/// reported via DATA_INVARIANT violations or exceptions.
122123
void validate(const namespacet &ns, const validation_modet vm) const
123124
{
125+
#if 1
126+
validate_goto_model(*this, vm);
127+
#endif
124128
for(const auto &entry : function_map)
125129
{
126130
const goto_functiont &goto_function = entry.second;
Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
/*******************************************************************\
2+
3+
Module: Validate Goto Programs
4+
5+
Author: Diffblue
6+
7+
Date: Oct 2018
8+
9+
\*******************************************************************/
10+
#include "validate_goto_model.h"
11+
12+
#include <set>
13+
14+
#include <util/invariant.h>
15+
#include <util/namespace.h>
16+
17+
#include "expr_util.h"
18+
#include "goto_model.h"
19+
20+
class validate_goto_modelt
21+
{
22+
public:
23+
using function_mapt = goto_functionst::function_mapt;
24+
25+
validate_goto_modelt(
26+
const goto_functionst &goto_functions,
27+
const validation_modet vm,
28+
bool check_function_id,
29+
bool check_no_fn_ptr_calls,
30+
bool check_no_returns)
31+
: vm{vm}, function_map{goto_functions.function_map}
32+
{
33+
}
34+
35+
void do_goto_program_checks(
36+
bool check_function_id,
37+
bool check_no_fn_ptr_calls,
38+
bool check_no_returns);
39+
40+
/// check the goto_program has an entry point
41+
void entry_point_exists();
42+
43+
/// 'function' field of an instruction should contain the id of
44+
/// the function it is currently in [OPTIONAL]
45+
void instruction_function_id_is_valid();
46+
47+
/// check there are no function pointers [OPTIONAL]
48+
void function_pointer_calls_removed();
49+
50+
/// no returns in each goto_program.
51+
/// NB function pointer calls must have been removed already when removing
52+
/// returns - so this also enables 'function_pointer_calls_removed()'
53+
void check_returns_removed();
54+
55+
/// a) check every function call callee is in the function map
56+
/// b) check every function whose address is taken is in the function map
57+
void check_called_functions();
58+
59+
/// check each function has a body and the last instruction is an 'end
60+
/// function' instruction
61+
void check_last_instruction();
62+
63+
private:
64+
const validation_modet vm;
65+
const function_mapt &function_map;
66+
};
67+
68+
void validate_goto_modelt::do_goto_program_checks(
69+
bool check_function_id,
70+
bool check_no_fn_ptr_calls,
71+
bool check_no_returns)
72+
{
73+
entry_point_exists();
74+
75+
if(check_function_id)
76+
instruction_function_id_is_valid();
77+
78+
if(check_no_fn_ptr_calls || check_no_returns)
79+
function_pointer_calls_removed();
80+
81+
if(check_no_returns)
82+
check_returns_removed();
83+
84+
check_called_functions();
85+
check_last_instruction();
86+
}
87+
88+
void validate_goto_modelt::entry_point_exists()
89+
{
90+
DATA_CHECK(
91+
function_map.find(goto_functionst::entry_point()) != function_map.end(),
92+
"an entry point must exist");
93+
}
94+
95+
void validate_goto_modelt::instruction_function_id_is_valid()
96+
{
97+
for(const auto &fun : function_map)
98+
{
99+
for(auto &instr : fun.second.body.instructions)
100+
DATA_CHECK(
101+
instr.function == fun.first, "instruction id must be parent function");
102+
}
103+
}
104+
105+
void validate_goto_modelt::function_pointer_calls_removed()
106+
{
107+
for(const auto &fun : function_map)
108+
{
109+
for(auto &instr : fun.second.body.instructions)
110+
{
111+
if(instr.is_function_call())
112+
{
113+
const code_function_callt &function_call =
114+
to_code_function_call(instr.code);
115+
DATA_CHECK(
116+
function_call.function().id() == ID_symbol,
117+
"no calls via fn pointer should be present");
118+
}
119+
}
120+
}
121+
}
122+
123+
void validate_goto_modelt::check_returns_removed()
124+
{
125+
for(const auto &fun : function_map)
126+
{
127+
const goto_functiont &goto_function = fun.second;
128+
DATA_CHECK(
129+
goto_function.type.return_type().id() == ID_empty,
130+
"functions must have empty return type");
131+
132+
for(const auto &instr : goto_function.body.instructions)
133+
{
134+
DATA_CHECK(
135+
!instr.is_return(), "no return instructions should be present");
136+
137+
if(instr.is_function_call())
138+
{
139+
const auto &function_call = to_code_function_call(instr.code);
140+
DATA_CHECK(
141+
function_call.lhs().is_nil(), "function call return should be nil");
142+
143+
// consider: if this is redundant?
144+
// I could not trigger this with unit tests without failing
145+
// the first test "goto_function.type.return_type().id() == ID_empty"
146+
// (or a built in invariant if callee was not in symbol table)
147+
const auto &callee = to_code_type(function_call.function().type());
148+
DATA_CHECK(callee.return_type().id() == ID_empty, "");
149+
}
150+
}
151+
}
152+
}
153+
154+
void validate_goto_modelt::check_called_functions()
155+
{
156+
class test_for_function_addresst : public const_expr_visitort
157+
{
158+
private:
159+
const irep_idt match_id{ID_address_of};
160+
161+
public:
162+
std::set<irep_idt> identifiers;
163+
164+
void operator()(const exprt &expr) override
165+
{
166+
if(expr.id() == match_id)
167+
{
168+
const exprt &pointee = to_address_of_expr(expr).object();
169+
if(pointee.id() == ID_symbol)
170+
identifiers.insert(to_symbol_expr(pointee).get_identifier());
171+
}
172+
}
173+
174+
void clear()
175+
{
176+
identifiers.clear();
177+
}
178+
} test_for_function_address;
179+
180+
for(const auto &fun : function_map)
181+
{
182+
for(auto &instr : fun.second.body.instructions)
183+
{
184+
if(instr.is_function_call())
185+
{
186+
const auto &function_call = to_code_function_call(instr.code);
187+
const irep_idt &identifier =
188+
to_symbol_expr(function_call.function()).get_identifier();
189+
190+
DATA_CHECK(function_map.find(identifier) != function_map.end(),
191+
"every function call callee must be in the function map");
192+
}
193+
194+
// to consider: excluding some of the 18 instruction types
195+
// from this brute force check
196+
const exprt &src{instr.code};
197+
src.visit(test_for_function_address);
198+
if(!test_for_function_address.identifiers.empty())
199+
{
200+
for(auto &identifier : test_for_function_address.identifiers)
201+
DATA_CHECK(function_map.find(identifier) != function_map.end(),
202+
"every function whose address is taken must be in the "
203+
"function map");
204+
}
205+
test_for_function_address.clear();
206+
}
207+
}
208+
}
209+
210+
void validate_goto_modelt::check_last_instruction()
211+
{
212+
for(const auto &fun : function_map)
213+
{
214+
DATA_CHECK(
215+
fun.second.body_available(),
216+
"all goto_programt instances should have a body");
217+
DATA_CHECK(
218+
fun.second.body.instructions.back().is_end_function(),
219+
"last instruction should be of end function type");
220+
}
221+
}
222+
223+
void validate_goto_model(
224+
const goto_functionst &goto_functions,
225+
const validation_modet vm,
226+
bool check_function_id,
227+
bool check_no_fn_ptr_calls,
228+
bool check_no_returns)
229+
{
230+
validate_goto_modelt{goto_functions,
231+
vm,
232+
check_function_id,
233+
check_no_fn_ptr_calls,
234+
check_no_returns}
235+
.do_goto_program_checks(
236+
check_function_id, check_no_fn_ptr_calls, check_no_returns);
237+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*******************************************************************\
2+
3+
Module: Validate Goto Programs
4+
5+
Author: Diffblue
6+
7+
Date: Oct 2018
8+
9+
\*******************************************************************/
10+
#ifndef CPROVER_GOTO_PROGRAMS_VALIDATE_GOTO_MODELT_H
11+
#define CPROVER_GOTO_PROGRAMS_VALIDATE_GOTO_MODELT_H
12+
13+
#include "validate.h"
14+
15+
class goto_functionst;
16+
17+
void validate_goto_model(
18+
const goto_functionst &goto_functions,
19+
const validation_modet vm,
20+
bool check_function_id = true,
21+
bool check_no_fn_ptr_calls = true,
22+
bool check_no_returns = true);
23+
24+
#endif //CPROVER_GOTO_PROGRAMS_VALIDATE_GOTO_MODELT_H

0 commit comments

Comments
 (0)