Skip to content

Commit 555fcbe

Browse files
Add support for function pointers to goto-harness
Also add support for having multiple constructors for the same type with different behaviours and different signatures. We are doing this because we need some types to be sometimes nullable and sometimes not, and for example, for arrays we sometimes need to pass a size parameter and sometimes not.
1 parent 23d948c commit 555fcbe

File tree

8 files changed

+185
-8
lines changed

8 files changed

+185
-8
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
#include <assert.h>
2+
3+
typedef int (*fptr)(int a, int b);
4+
5+
int max_normal(int first, int second)
6+
{
7+
return first >= second ? first : second;
8+
}
9+
10+
int max_crazy(int first, int second)
11+
{
12+
return first >= second ? second : first;
13+
}
14+
15+
void use_fptr(fptr max, int first, int second)
16+
{
17+
int m = max(first, second);
18+
assert(m == first || m == second);
19+
assert(m >= first && m >= second);
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
test.c
3+
--harness-type call-function --harness-function-name harness --function use_fptr
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
\[use_fptr.assertion.1\] line \d+ assertion m == first \|\| m == second: SUCCESS
7+
\[use_fptr.assertion.2\] line \d+ assertion m >= first && m >= second: FAILURE
8+
--
9+
^warning: ignoring
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
typedef int (*fptr_t)(void);
5+
6+
void entry_point(fptr_t f, fptr_t f_but_may_be_null)
7+
{
8+
assert(f != (void *)0); // expected to succeed
9+
assert(f == (void *)0); // expected to fail
10+
assert(f_but_may_be_null != (void *)0); // expected to fail
11+
assert(f_but_may_be_null == (void *)0); // expected to fail
12+
assert(f_but_may_be_null == (void *)0 || f() == f_but_may_be_null());
13+
}
14+
15+
int f(void)
16+
{
17+
return 42;
18+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
test.c
3+
--harness-type call-function --function entry_point --function-pointer-can-be-null __goto_harness::f_but_may_be_null
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
\[entry_point.assertion.1\] line \d+ assertion f != \(void \*\)0: SUCCESS
7+
\[entry_point.assertion.2\] line \d+ assertion f == \(void \*\)0: FAILURE
8+
\[entry_point.assertion.3\] line \d+ assertion f_but_may_be_null != \(void \*\)0: FAILURE
9+
\[entry_point.assertion.4\] line \d+ assertion f_but_may_be_null == \(void \*\)0: FAILURE
10+
\[entry_point.assertion.5\] line \d+ assertion f_but_may_be_null == \(void \*\)0 || f\(\) == f_but_may_be_null\(\): SUCCESS
11+
--
12+
^warning: ignoring

src/goto-harness/common_harness_generator_options.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,16 @@ Author: Diffblue Ltd.
1414
"max-nondet-tree-depth"
1515
#define COMMON_HARNESS_GENERATOR_MIN_ARRAY_SIZE_OPT "min-array-size"
1616
#define COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT "max-array-size"
17+
#define COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT \
18+
"function-pointer-can-be-null"
1719

1820
// clang-format off
1921
#define COMMON_HARNESS_GENERATOR_OPTIONS \
2022
"(" COMMON_HARNESS_GENERATOR_MIN_NULL_TREE_DEPTH_OPT "):" \
2123
"(" COMMON_HARNESS_GENERATOR_MAX_NONDET_TREE_DEPTH_OPT "):" \
2224
"(" COMMON_HARNESS_GENERATOR_MIN_ARRAY_SIZE_OPT "):" \
2325
"(" COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT "):" \
26+
"(" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT "):" \
2427
// COMMON_HARNESS_GENERATOR_OPTIONS
2528

2629
// clang-format on
@@ -39,6 +42,9 @@ Author: Diffblue Ltd.
3942
"--" COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT \
4043
" N maximum size of dynamically created arrays\n" \
4144
" (default: 2)\n" \
45+
"--" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT \
46+
" <function-name>, name of the function(s) pointer parameters\n" \
47+
" that can be NULL pointing."
4248
// COMMON_HARNESS_GENERATOR_HELP
4349

4450
// clang-format on

src/goto-harness/function_call_harness_generator.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,18 @@ void function_call_harness_generatort::handle_option(
173173
{
174174
p_impl->recursive_initialization_config.arguments_may_be_equal = true;
175175
}
176+
else if(option == COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT)
177+
{
178+
std::transform(
179+
values.begin(),
180+
values.end(),
181+
std::inserter(
182+
p_impl->recursive_initialization_config
183+
.potential_null_function_pointers,
184+
p_impl->recursive_initialization_config.potential_null_function_pointers
185+
.end()),
186+
[](const std::string &opt) -> irep_idt { return irep_idt{opt}; });
187+
}
176188
else
177189
{
178190
throw invalid_command_line_argument_exceptiont{

src/goto-harness/recursive_initialization.cpp

Lines changed: 73 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,8 @@ code_blockt recursive_initializationt::build_constructor_body(
187187
const exprt &depth_symbol,
188188
const symbol_exprt &result_symbol,
189189
const optionalt<exprt> &size_symbol,
190-
const optionalt<irep_idt> &lhs_name)
190+
const optionalt<irep_idt> &lhs_name,
191+
const bool is_nullable)
191192
{
192193
PRECONDITION(result_symbol.type().id() == ID_pointer);
193194
const typet &type = result_symbol.type().subtype();
@@ -197,6 +198,10 @@ code_blockt recursive_initializationt::build_constructor_body(
197198
}
198199
else if(type.id() == ID_pointer)
199200
{
201+
if(type.subtype().id() == ID_code)
202+
{
203+
return build_function_pointer_constructor(result_symbol, is_nullable);
204+
}
200205
if(lhs_name.has_value())
201206
{
202207
if(should_be_treated_as_cstring(*lhs_name) && type == char_type())
@@ -231,17 +236,23 @@ irep_idt recursive_initializationt::build_constructor(const exprt &expr)
231236
// void type_constructor_T(int depth_T, T *result_T, int *size);
232237
optionalt<irep_idt> size_var;
233238
optionalt<irep_idt> expr_name;
239+
bool is_nullable = false;
240+
bool has_size_param = false;
234241
if(expr.id() == ID_symbol)
235242
{
236243
expr_name = to_symbol_expr(expr).get_identifier();
244+
is_nullable = initialization_config.potential_null_function_pointers.count(
245+
expr_name.value());
237246
if(should_be_treated_as_array(*expr_name))
238247
{
239248
size_var = get_associated_size_variable(*expr_name);
249+
has_size_param = true;
240250
}
241251
}
242252
const typet &type = expr.type();
243-
if(type_constructor_names.find(type) != type_constructor_names.end())
244-
return type_constructor_names[type];
253+
const constructor_keyt key{type, is_nullable, has_size_param};
254+
if(type_constructor_names.find(key) != type_constructor_names.end())
255+
return type_constructor_names[key];
245256

246257
const std::string &pretty_type = type2id(type);
247258
symbolt &depth_symbol =
@@ -282,7 +293,7 @@ irep_idt recursive_initializationt::build_constructor(const exprt &expr)
282293
}
283294
const symbolt &function_symbol = get_fresh_fun_symbol(
284295
"type_constructor_" + pretty_type, code_typet{fun_params, empty_typet{}});
285-
type_constructor_names[type] = function_symbol.name;
296+
type_constructor_names[key] = function_symbol.name;
286297
symbolt *mutable_symbol = symbol_table.get_writeable(function_symbol.name);
287298

288299
// the body is specific for each type of expression
@@ -292,11 +303,12 @@ irep_idt recursive_initializationt::build_constructor(const exprt &expr)
292303
size_symbol_expr,
293304
// the expression name may be needed to decide if expr should be treated as
294305
// a string
295-
expr_name);
306+
expr_name,
307+
is_nullable);
296308

297309
goto_model.goto_functions.function_map[function_symbol.name].type =
298310
to_code_type(function_symbol.type);
299-
return type_constructor_names[type];
311+
return type_constructor_names.at(key);
300312
}
301313

302314
symbol_exprt recursive_initializationt::get_malloc_function()
@@ -877,3 +889,58 @@ void recursive_initializationt::free_cluster_origins(code_blockt &body)
877889
body.add(code_function_callt{get_free_function(), {*origin}});
878890
}
879891
}
892+
893+
code_blockt recursive_initializationt::build_function_pointer_constructor(
894+
const symbol_exprt &result,
895+
bool is_nullable)
896+
{
897+
PRECONDITION(can_cast_type<pointer_typet>(result.type()));
898+
const auto &result_type = to_pointer_type(result.type());
899+
PRECONDITION(can_cast_type<pointer_typet>(result_type.subtype()));
900+
const auto &function_pointer_type = to_pointer_type(result_type.subtype());
901+
PRECONDITION(can_cast_type<code_typet>(function_pointer_type.subtype()));
902+
const auto &function_type = to_code_type(function_pointer_type.subtype());
903+
904+
std::vector<exprt> targets;
905+
906+
for(const auto &sym : goto_model.get_symbol_table())
907+
{
908+
if(sym.second.type == function_type)
909+
{
910+
targets.push_back(address_of_exprt{sym.second.symbol_expr()});
911+
}
912+
}
913+
914+
if(is_nullable)
915+
targets.push_back(null_pointer_exprt{function_pointer_type});
916+
917+
code_blockt body{};
918+
919+
const auto function_pointer_selector =
920+
get_fresh_local_symexpr("function_pointer_selector");
921+
body.add(
922+
code_assignt{function_pointer_selector,
923+
side_effect_expr_nondett{function_pointer_selector.type(),
924+
source_locationt{}}});
925+
auto function_pointer_index = std::size_t{0};
926+
927+
for(const auto &target : targets)
928+
{
929+
auto const assign = code_assignt{dereference_exprt{result}, target};
930+
if(function_pointer_index != targets.size() - 1)
931+
{
932+
auto const condition = equal_exprt{
933+
function_pointer_selector,
934+
from_integer(function_pointer_index, function_pointer_selector.type())};
935+
auto const then = code_blockt{{assign, code_returnt{}}};
936+
body.add(code_ifthenelset{condition, then});
937+
}
938+
else
939+
{
940+
body.add(assign);
941+
}
942+
++function_pointer_index;
943+
}
944+
945+
return body;
946+
}

src/goto-harness/recursive_initialization.h

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ struct recursive_initialization_configt
2727
std::size_t min_null_tree_depth = 1;
2828
std::size_t max_nondet_tree_depth = 2;
2929
irep_idt mode;
30+
std::unordered_set<irep_idt> potential_null_function_pointers;
3031

3132
// array stuff
3233
std::size_t max_dynamic_array_size = 2;
@@ -59,8 +60,30 @@ class recursive_initializationt
5960
{
6061
public:
6162
using recursion_sett = std::set<irep_idt>;
62-
using type_constructor_namest = std::map<typet, irep_idt>;
6363
using equal_cluster_idt = std::size_t;
64+
struct constructor_keyt
65+
{
66+
typet constructor_type;
67+
bool is_nullable;
68+
bool has_size_parameter;
69+
bool operator<(const constructor_keyt &other) const
70+
{
71+
return std::tie(constructor_type, is_nullable, has_size_parameter) <
72+
std::tie(
73+
other.constructor_type,
74+
other.is_nullable,
75+
other.has_size_parameter);
76+
};
77+
bool operator==(const constructor_keyt &other) const
78+
{
79+
return std::tie(constructor_type, is_nullable, has_size_parameter) ==
80+
std::tie(
81+
other.constructor_type,
82+
other.is_nullable,
83+
other.has_size_parameter);
84+
};
85+
};
86+
using type_constructor_namest = std::map<constructor_keyt, irep_idt>;
6487

6588
recursive_initializationt(
6689
recursive_initialization_configt initialization_config,
@@ -170,19 +193,29 @@ class recursive_initializationt
170193
/// \param result_symbol: the symbol for `result` parameter
171194
/// \param size_symbol: maybe the symbol for `size` parameter
172195
/// \param lhs_name: the name of the original symbol
196+
/// \param is_nullable: flag for setting a function pointer nullable
173197
/// \return the body of the constructor
174198
code_blockt build_constructor_body(
175199
const exprt &depth_symbol,
176200
const symbol_exprt &result_symbol,
177201
const optionalt<exprt> &size_symbol,
178-
const optionalt<irep_idt> &lhs_name);
202+
const optionalt<irep_idt> &lhs_name,
203+
const bool is_nullable);
179204

180205
/// Check if a constructor for the type of \p expr already exists and create
181206
/// it if not.
182207
/// \param expr: the expression to be constructed
183208
/// \return name of the constructor function
184209
irep_idt build_constructor(const exprt &expr);
185210

211+
/// Constructor for function pointers.
212+
/// \param result: symbol of the result parameter
213+
/// \param is_nullable: if the function pointer can be null
214+
/// \return the body of the constructor
215+
code_blockt build_function_pointer_constructor(
216+
const symbol_exprt &result,
217+
bool is_nullable);
218+
186219
/// Generic constructor for all pointers: only builds one pointee (not an
187220
/// array) but may recourse in case the pointee contains more pointers, e.g.
188221
/// a struct.

0 commit comments

Comments
 (0)