Skip to content

Commit 0cdc654

Browse files
committed
Use remove-function-pointers code for restricted function pointers
We had two instances of code generating an if-else sequence of function calls for function pointers. The code used with --restrict-function-pointer, however, was not able to cope with type mismatches. Make the code from remove_function_pointers re-usable in both contexts, enabling support for additional type casts. Fixes: #6368
1 parent 18760ca commit 0cdc654

File tree

8 files changed

+178
-133
lines changed

8 files changed

+178
-133
lines changed

regression/cbmc/Function_Pointer_Init_No_Candidate/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ CORE
22
main.c
33
--function foo --pointer-check
44
^\[foo.assertion.\d+\] line \d+ assertion other_function\(4\) > 5: SUCCESS$
5-
^\[foo.pointer_dereference.\d+\] line \d+ invalid function pointer: FAILURE$
5+
^\[foo.pointer_dereference.\d+\] line \d+ no candidates for dereferenced function pointer: FAILURE$
66
^EXIT=10$
77
^SIGNAL=0$
88
^VERIFICATION FAILED
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#include <assert.h>
2+
3+
typedef int (*fptr_t)(long long);
4+
5+
void use_f(fptr_t fptr)
6+
{
7+
assert(fptr(10) == 11);
8+
}
9+
10+
int f(int x)
11+
{
12+
return x + 1;
13+
}
14+
15+
int g(int x)
16+
{
17+
return x;
18+
}
19+
20+
int main(void)
21+
{
22+
int one = 1;
23+
24+
// We take the address of f and g. In this case remove_function_pointers()
25+
// would create a case distinction involving both f and g in the function
26+
// use_f() above.
27+
use_f(one ? f : g);
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
test.c
3+
--restrict-function-pointer use_f.function_pointer_call.1/f
4+
\[use_f\.assertion\.2\] line \d+ assertion fptr\(10\) == 11: SUCCESS
5+
^EXIT=0$
6+
^SIGNAL=0$
7+
--
8+
--
9+
This test checks that the function f is permitted for the first function pointer
10+
call in function use_f, despite parameters having different integer types.

src/goto-instrument/goto_instrument_parse_options.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -1034,7 +1034,8 @@ void goto_instrument_parse_optionst::instrument_goto_program()
10341034
function_pointer_restrictionst::from_options(
10351035
options, goto_model, log.get_message_handler());
10361036

1037-
restrict_function_pointers(goto_model, function_pointer_restrictions);
1037+
restrict_function_pointers(
1038+
ui_message_handler, goto_model, function_pointer_restrictions);
10381039
}
10391040
}
10401041

src/goto-programs/remove_function_pointers.cpp

+69-50
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Author: Daniel Kroening, [email protected]
1919
#include <util/pointer_offset_size.h>
2020
#include <util/source_location.h>
2121
#include <util/std_expr.h>
22+
#include <util/string_utils.h>
2223

2324
#include <analyses/does_remove_const.h>
2425

@@ -43,24 +44,8 @@ class remove_function_pointerst
4344
goto_programt &goto_program,
4445
const irep_idt &function_id);
4546

46-
// a set of function symbols
47-
using functionst = remove_const_function_pointerst::functionst;
48-
49-
/// Replace a call to a dynamic function at location
50-
/// target in the given goto-program by a case-split
51-
/// over a given set of functions
52-
/// \param goto_program: The goto program that contains target
53-
/// \param function_id: Name of function containing the target
54-
/// \param target: location with function call with function pointer
55-
/// \param functions: The set of functions to consider
56-
void remove_function_pointer(
57-
goto_programt &goto_program,
58-
const irep_idt &function_id,
59-
goto_programt::targett target,
60-
const functionst &functions);
61-
6247
protected:
63-
messaget log;
48+
message_handlert &message_handler;
6449
const namespacet ns;
6550
symbol_tablet &symbol_table;
6651
bool add_safety_assertion;
@@ -88,21 +73,6 @@ class remove_function_pointerst
8873

8974
typedef std::map<irep_idt, code_typet> type_mapt;
9075
type_mapt type_map;
91-
92-
bool is_type_compatible(
93-
bool return_value_used,
94-
const code_typet &call_type,
95-
const code_typet &function_type);
96-
97-
bool arg_is_type_compatible(
98-
const typet &call_type,
99-
const typet &function_type);
100-
101-
void fix_argument_types(code_function_callt &function_call);
102-
void fix_return_type(
103-
const irep_idt &in_function_id,
104-
code_function_callt &function_call,
105-
goto_programt &dest);
10676
};
10777

10878
remove_function_pointerst::remove_function_pointerst(
@@ -111,7 +81,7 @@ remove_function_pointerst::remove_function_pointerst(
11181
bool _add_safety_assertion,
11282
bool only_resolve_const_fps,
11383
const goto_functionst &goto_functions)
114-
: log(_message_handler),
84+
: message_handler(_message_handler),
11585
ns(_symbol_table),
11686
symbol_table(_symbol_table),
11787
add_safety_assertion(_add_safety_assertion),
@@ -130,9 +100,10 @@ remove_function_pointerst::remove_function_pointerst(
130100
}
131101
}
132102

133-
bool remove_function_pointerst::arg_is_type_compatible(
103+
static bool arg_is_type_compatible(
134104
const typet &call_type,
135-
const typet &function_type)
105+
const typet &function_type,
106+
const namespacet &ns)
136107
{
137108
if(call_type == function_type)
138109
return true;
@@ -156,10 +127,11 @@ bool remove_function_pointerst::arg_is_type_compatible(
156127
pointer_offset_bits(function_type, ns);
157128
}
158129

159-
bool remove_function_pointerst::is_type_compatible(
130+
bool function_is_type_convertable(
160131
bool return_value_used,
161132
const code_typet &call_type,
162-
const code_typet &function_type)
133+
const code_typet &function_type,
134+
const namespacet &ns)
163135
{
164136
// we are willing to ignore anything that's returned
165137
// if we call with 'void'
@@ -172,8 +144,8 @@ bool remove_function_pointerst::is_type_compatible(
172144
}
173145
else
174146
{
175-
if(!arg_is_type_compatible(call_type.return_type(),
176-
function_type.return_type()))
147+
if(!arg_is_type_compatible(
148+
call_type.return_type(), function_type.return_type(), ns))
177149
return false;
178150
}
179151

@@ -198,16 +170,15 @@ bool remove_function_pointerst::is_type_compatible(
198170
return false;
199171

200172
for(std::size_t i=0; i<call_parameters.size(); i++)
201-
if(!arg_is_type_compatible(call_parameters[i].type(),
202-
function_parameters[i].type()))
173+
if(!arg_is_type_compatible(
174+
call_parameters[i].type(), function_parameters[i].type(), ns))
203175
return false;
204176
}
205177

206178
return true;
207179
}
208180

209-
void remove_function_pointerst::fix_argument_types(
210-
code_function_callt &function_call)
181+
static void fix_argument_types(code_function_callt &function_call)
211182
{
212183
const code_typet &code_type = to_code_type(function_call.function().type());
213184

@@ -230,9 +201,10 @@ void remove_function_pointerst::fix_argument_types(
230201
}
231202
}
232203

233-
void remove_function_pointerst::fix_return_type(
204+
static void fix_return_type(
234205
const irep_idt &in_function_id,
235206
code_function_callt &function_call,
207+
symbol_tablet &symbol_table,
236208
goto_programt &dest)
237209
{
238210
// are we returning anything at all?
@@ -245,6 +217,7 @@ void remove_function_pointerst::fix_return_type(
245217
if(function_call.lhs().type() == code_type.return_type())
246218
return;
247219

220+
const namespacet ns(symbol_table);
248221
const symbolt &function_symbol =
249222
ns.lookup(to_symbol_expr(function_call.function()).get_identifier());
250223

@@ -292,6 +265,7 @@ void remove_function_pointerst::remove_function_pointer(
292265
remove_const_function_pointerst::functionst functions;
293266
does_remove_constt const_removal_check(goto_program, ns);
294267
const auto does_remove_const = const_removal_check();
268+
messaget log{message_handler};
295269
if(does_remove_const.first)
296270
{
297271
log.warning().source_location = does_remove_const.second;
@@ -344,7 +318,8 @@ void remove_function_pointerst::remove_function_pointer(
344318
continue;
345319

346320
// type-compatible?
347-
if(!is_type_compatible(return_value_used, call_type, t.second))
321+
if(!function_is_type_convertable(
322+
return_value_used, call_type, t.second, ns))
348323
continue;
349324

350325
if(t.first=="pthread_mutex_cleanup")
@@ -355,14 +330,56 @@ void remove_function_pointerst::remove_function_pointer(
355330
}
356331
}
357332

358-
remove_function_pointer(goto_program, function_id, target, functions);
333+
::remove_function_pointer(
334+
message_handler,
335+
symbol_table,
336+
goto_program,
337+
function_id,
338+
target,
339+
functions,
340+
add_safety_assertion);
359341
}
360342

361-
void remove_function_pointerst::remove_function_pointer(
343+
static std::string function_pointer_assertion_comment(
344+
const std::unordered_set<symbol_exprt, irep_hash> &candidates)
345+
{
346+
std::stringstream comment;
347+
348+
comment << "dereferenced function pointer at must be ";
349+
350+
if(candidates.size() == 1)
351+
{
352+
comment << candidates.begin()->get_identifier();
353+
}
354+
else if(candidates.empty())
355+
{
356+
comment.str("no candidates for dereferenced function pointer");
357+
}
358+
else
359+
{
360+
comment << "one of [";
361+
362+
join_strings(
363+
comment,
364+
candidates.begin(),
365+
candidates.end(),
366+
", ",
367+
[](const symbol_exprt &s) { return s.get_identifier(); });
368+
369+
comment << ']';
370+
}
371+
372+
return comment.str();
373+
}
374+
375+
void remove_function_pointer(
376+
message_handlert &message_handler,
377+
symbol_tablet &symbol_table,
362378
goto_programt &goto_program,
363379
const irep_idt &function_id,
364380
goto_programt::targett target,
365-
const functionst &functions)
381+
const std::unordered_set<symbol_exprt, irep_hash> &functions,
382+
const bool add_safety_assertion)
366383
{
367384
const exprt &function = target->call_function();
368385
const exprt &pointer = to_dereference_expr(function).pointer();
@@ -387,7 +404,7 @@ void remove_function_pointerst::remove_function_pointer(
387404
fix_argument_types(new_call);
388405

389406
goto_programt tmp;
390-
fix_return_type(function_id, new_call, tmp);
407+
fix_return_type(function_id, new_call, symbol_table, tmp);
391408

392409
auto call = new_code_calls.add(goto_programt::make_function_call(new_call));
393410
new_code_calls.destructive_append(tmp);
@@ -411,7 +428,8 @@ void remove_function_pointerst::remove_function_pointer(
411428
goto_programt::targett t =
412429
new_code_gotos.add(goto_programt::make_assertion(false_exprt()));
413430
t->source_location.set_property_class("pointer dereference");
414-
t->source_location.set_comment("invalid function pointer");
431+
t->source_location.set_comment(
432+
function_pointer_assertion_comment(functions));
415433
}
416434
new_code_gotos.add(goto_programt::make_assumption(false_exprt()));
417435

@@ -449,6 +467,7 @@ void remove_function_pointerst::remove_function_pointer(
449467
target->type=OTHER;
450468

451469
// report statistics
470+
messaget log{message_handler};
452471
log.statistics().source_location = target->source_location;
453472
log.statistics() << "replacing function pointer by " << functions.size()
454473
<< " possible targets" << messaget::eom;

src/goto-programs/remove_function_pointers.h

+31-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@ Date: June 2003
1414
#ifndef CPROVER_GOTO_PROGRAMS_REMOVE_FUNCTION_POINTERS_H
1515
#define CPROVER_GOTO_PROGRAMS_REMOVE_FUNCTION_POINTERS_H
1616

17-
#include <util/irep.h>
17+
#include "goto_program.h"
18+
19+
#include <unordered_set>
1820

1921
class goto_functionst;
20-
class goto_programt;
2122
class goto_modelt;
2223
class message_handlert;
2324
class symbol_tablet;
@@ -46,4 +47,32 @@ bool remove_function_pointers(
4647
bool add_safety_assertion,
4748
bool only_remove_const_fps = false);
4849

50+
/// Replace a call to a dynamic function at location
51+
/// target in the given goto-program by a case-split
52+
/// over a given set of functions
53+
/// \param message_handler: Message handler
54+
/// \param symbol_table: Symbol table
55+
/// \param goto_program: The goto program that contains target
56+
/// \param function_id: Name of function containing the target
57+
/// \param target: location with function call with function pointer
58+
/// \param functions: The set of functions to consider
59+
/// \param add_safety_assertion: Iff true, include an assertion that the
60+
// pointer matches one of the candidate functions
61+
void remove_function_pointer(
62+
message_handlert &message_handler,
63+
symbol_tablet &symbol_table,
64+
goto_programt &goto_program,
65+
const irep_idt &function_id,
66+
goto_programt::targett target,
67+
const std::unordered_set<symbol_exprt, irep_hash> &functions,
68+
const bool add_safety_assertion);
69+
70+
/// Returns true iff \p call_type can be converted to produce a function call of
71+
/// the same type as \p function_type.
72+
bool function_is_type_convertable(
73+
bool return_value_used,
74+
const code_typet &call_type,
75+
const code_typet &function_type,
76+
const namespacet &ns);
77+
4978
#endif // CPROVER_GOTO_PROGRAMS_REMOVE_FUNCTION_POINTERS_H

0 commit comments

Comments
 (0)