Skip to content

Commit 0002950

Browse files
authored
Merge pull request #5176 from NlightNFotis/feature/function-pointer-nondet-harness
Function pointer non-det initialisation in goto-harness [depends-on: #5171]
2 parents 2400d10 + 6b684f7 commit 0002950

File tree

12 files changed

+370
-31
lines changed

12 files changed

+370
-31
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 entry_point::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
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
void entry_point(int *array_with_size, int size, int *array_without_size)
2+
{
3+
assert(array_with_size != (void *)0);
4+
assert(array_with_size != (void *)0);
5+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
test.c
3+
--function entry_point --harness-type call-function --associated-array-size array_with_size:size
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
--
7+
^warning: ignoring
8+
call to '\w+': not enough arguments, inserting non-deterministic value
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#include <assert.h>
2+
3+
typedef int (*fptr_t)(void);
4+
5+
fptr_t f;
6+
7+
int call_f()
8+
{
9+
assert(f != ((void *)0));
10+
return f();
11+
}
12+
13+
void function(fptr_t f)
14+
{
15+
if(f != ((void *)0))
16+
assert(f() == call_f());
17+
assert(f != ((void *)0));
18+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
test.c
3+
--harness-type call-function --function function --function-pointer-can-be-null function::f
4+
^EXIT=10$
5+
^SIGNAL=0$
6+
\[function.assertion.1\] line \d+ assertion f\(\) == call_f\(\): SUCCESS
7+
\[function.assertion.2\] line \d+ assertion f != \(\(void \*\)0\): FAILURE
8+
\[call_f.assertion.1\] line \d+ assertion f != \(\(void \*\)0\): SUCCESS
9+
--
10+
^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: 110 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Author: Diffblue Ltd.
1212
#include <util/arith_tools.h>
1313
#include <util/c_types.h>
1414
#include <util/exception_utils.h>
15+
#include <util/prefix.h>
1516
#include <util/std_code.h>
1617
#include <util/std_expr.h>
1718
#include <util/string2int.h>
@@ -72,14 +73,24 @@ struct function_call_harness_generatort::implt
7273
void ensure_harness_does_not_already_exist();
7374
/// Update the goto-model with the new harness function.
7475
void add_harness_function_to_goto_model(code_blockt function_body);
75-
/// declare local variables for each of the parameters of the entry function
76+
/// Declare local variables for each of the parameters of the entry function
7677
/// and return them
7778
code_function_callt::argumentst declare_arguments(code_blockt &function_body);
78-
/// write initialisation code for each of the arguments into function_body,
79+
/// Write initialisation code for each of the arguments into function_body,
7980
/// then insert a call to the entry function with the arguments
8081
void call_function(
8182
const code_function_callt::argumentst &arguments,
8283
code_blockt &function_body);
84+
/// For function parameters that are pointers to functions we want to
85+
/// be able to specify whether or not they can be NULL. To disambiguate
86+
/// this specification from that for a global variable of the same name,
87+
/// we prepend the name of the function to the parameter name. However,
88+
/// what is actually being initialised in the implementation is not the
89+
/// parameter itself, but a corresponding function argument (local variable
90+
/// of the harness function). We need a mapping from function parameter
91+
/// name to function argument names, and this is what this function does.
92+
std::unordered_set<irep_idt>
93+
map_function_parameters_to_function_argument_names();
8394
};
8495

8596
function_call_harness_generatort::function_call_harness_generatort(
@@ -173,6 +184,30 @@ void function_call_harness_generatort::handle_option(
173184
{
174185
p_impl->recursive_initialization_config.arguments_may_be_equal = true;
175186
}
187+
else if(option == COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT)
188+
{
189+
std::transform(
190+
values.begin(),
191+
values.end(),
192+
std::inserter(
193+
p_impl->recursive_initialization_config
194+
.potential_null_function_pointers,
195+
p_impl->recursive_initialization_config.potential_null_function_pointers
196+
.end()),
197+
[](const std::string &opt) -> irep_idt { return irep_idt{opt}; });
198+
}
199+
else if(option == COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT)
200+
{
201+
std::transform(
202+
values.begin(),
203+
values.end(),
204+
std::inserter(
205+
p_impl->recursive_initialization_config
206+
.potential_null_function_pointers,
207+
p_impl->recursive_initialization_config.potential_null_function_pointers
208+
.end()),
209+
[](const std::string &opt) -> irep_idt { return irep_idt{opt}; });
210+
}
176211
else
177212
{
178213
throw invalid_command_line_argument_exceptiont{
@@ -214,6 +249,8 @@ void function_call_harness_generatort::implt::generate(
214249
recursive_initialization_config.variables_that_hold_array_sizes.insert(
215250
pair.second);
216251
}
252+
recursive_initialization_config.potential_null_function_pointers =
253+
map_function_parameters_to_function_argument_names();
217254
recursive_initialization_config.pointers_to_treat_as_cstrings =
218255
function_arguments_to_treat_as_cstrings;
219256
recursive_initialization = util_make_unique<recursive_initializationt>(
@@ -269,7 +306,7 @@ void function_call_harness_generatort::implt::generate_initialisation_code_for(
269306
void function_call_harness_generatort::validate_options(
270307
const goto_modelt &goto_model)
271308
{
272-
if(p_impl->function == ID_empty)
309+
if(p_impl->function == ID_empty_string)
273310
throw invalid_command_line_argument_exceptiont{
274311
"required parameter entry function not set",
275312
"--" FUNCTION_HARNESS_GENERATOR_FUNCTION_OPT};
@@ -283,8 +320,17 @@ void function_call_harness_generatort::validate_options(
283320
" --" COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT};
284321
}
285322

286-
auto function_to_call = goto_model.symbol_table.lookup_ref(p_impl->function);
287-
auto ftype = to_code_type(function_to_call.type);
323+
const auto function_to_call_pointer =
324+
goto_model.symbol_table.lookup(p_impl->function);
325+
if(function_to_call_pointer == nullptr)
326+
{
327+
throw invalid_command_line_argument_exceptiont{
328+
"entry function `" + id2string(p_impl->function) +
329+
"' does not exist in the symbol table",
330+
"--" FUNCTION_HARNESS_GENERATOR_FUNCTION_OPT};
331+
}
332+
const auto &function_to_call = *function_to_call_pointer;
333+
const auto &ftype = to_code_type(function_to_call.type);
288334
for(auto const &equal_cluster : p_impl->function_parameters_to_treat_equal)
289335
{
290336
for(auto const &pointer_id : equal_cluster)
@@ -325,6 +371,43 @@ void function_call_harness_generatort::validate_options(
325371
}
326372
}
327373
}
374+
375+
const namespacet ns{goto_model.symbol_table};
376+
377+
// Make sure all function pointers that the user asks are nullable are
378+
// present in the symbol table.
379+
for(const auto &nullable :
380+
p_impl->recursive_initialization_config.potential_null_function_pointers)
381+
{
382+
const auto &function_pointer_symbol_pointer =
383+
goto_model.symbol_table.lookup(nullable);
384+
385+
if(function_pointer_symbol_pointer == nullptr)
386+
{
387+
throw invalid_command_line_argument_exceptiont{
388+
"nullable function pointer `" + id2string(nullable) +
389+
"' not found in symbol table",
390+
"--" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT};
391+
}
392+
393+
const auto &function_pointer_type =
394+
ns.follow(function_pointer_symbol_pointer->type);
395+
396+
if(!can_cast_type<pointer_typet>(function_pointer_type))
397+
{
398+
throw invalid_command_line_argument_exceptiont{
399+
"`" + id2string(nullable) + "' is not a pointer",
400+
"--" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT};
401+
}
402+
403+
if(!can_cast_type<code_typet>(
404+
to_pointer_type(function_pointer_type).subtype()))
405+
{
406+
throw invalid_command_line_argument_exceptiont{
407+
"`" + id2string(nullable) + "' is not pointing to a function",
408+
"--" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT};
409+
}
410+
}
328411
}
329412

330413
const symbolt &
@@ -469,3 +552,25 @@ void function_call_harness_generatort::implt::call_function(
469552

470553
function_body.add(std::move(function_call));
471554
}
555+
556+
std::unordered_set<irep_idt> function_call_harness_generatort::implt::
557+
map_function_parameters_to_function_argument_names()
558+
{
559+
std::unordered_set<irep_idt> nullables;
560+
for(const auto &nullable :
561+
recursive_initialization_config.potential_null_function_pointers)
562+
{
563+
const auto &nullable_name = id2string(nullable);
564+
const auto &function_prefix = id2string(function) + "::";
565+
if(has_prefix(nullable_name, function_prefix))
566+
{
567+
nullables.insert(
568+
"__goto_harness::" + nullable_name.substr(function_prefix.size()));
569+
}
570+
else
571+
{
572+
nullables.insert(nullable_name);
573+
}
574+
}
575+
return nullables;
576+
}

0 commit comments

Comments
 (0)