Skip to content

Commit 831fa1a

Browse files
committed
Prerequisite PR diffblue#5176 -- do not review
1 parent cb20917 commit 831fa1a

File tree

15 files changed

+457
-30
lines changed

15 files changed

+457
-30
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
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
typedef struct st
5+
{
6+
struct st *next;
7+
int data;
8+
} st_t;
9+
10+
void func(st_t *p, st_t *q)
11+
{
12+
assert(p != NULL);
13+
assert(p->next != NULL);
14+
15+
assert(p == q);
16+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
CORE
2+
main.c
3+
--function func --min-null-tree-depth 10 --max-nondet-tree-depth 3 --harness-type call-function --treat-pointers-equal p,q
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
VERIFICATION SUCCESSFUL
7+
--
8+
^warning: ignoring

src/goto-harness/common_harness_generator_options.h

Lines changed: 3 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

src/goto-harness/function_call_harness_generator.cpp

Lines changed: 146 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>
@@ -48,6 +49,9 @@ struct function_call_harness_generatort::implt
4849
std::set<irep_idt> function_parameters_to_treat_as_arrays;
4950
std::set<irep_idt> function_arguments_to_treat_as_arrays;
5051

52+
std::set<irep_idt> function_parameters_to_treat_equal;
53+
std::set<irep_idt> function_arguments_to_treat_equal;
54+
5155
std::set<irep_idt> function_parameters_to_treat_as_cstrings;
5256
std::set<irep_idt> function_arguments_to_treat_as_cstrings;
5357

@@ -69,14 +73,24 @@ struct function_call_harness_generatort::implt
6973
void ensure_harness_does_not_already_exist();
7074
/// Update the goto-model with the new harness function.
7175
void add_harness_function_to_goto_model(code_blockt function_body);
72-
/// 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
7377
/// and return them
7478
code_function_callt::argumentst declare_arguments(code_blockt &function_body);
75-
/// write initialisation code for each of the arguments into function_body,
79+
/// Write initialisation code for each of the arguments into function_body,
7680
/// then insert a call to the entry function with the arguments
7781
void call_function(
7882
const code_function_callt::argumentst &arguments,
7983
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 correspond 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();
8094
};
8195

8296
function_call_harness_generatort::function_call_harness_generatort(
@@ -112,6 +126,16 @@ void function_call_harness_generatort::handle_option(
112126
p_impl->function_parameters_to_treat_as_arrays.insert(
113127
values.begin(), values.end());
114128
}
129+
else if(option == FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT)
130+
{
131+
for(auto const &value : values)
132+
{
133+
for(auto const &param_id : split_string(value, ','))
134+
{
135+
p_impl->function_parameters_to_treat_equal.insert(param_id);
136+
}
137+
}
138+
}
115139
else if(option == FUNCTION_HARNESS_GENERATOR_ASSOCIATED_ARRAY_SIZE_OPT)
116140
{
117141
for(auto const &array_size_pair : values)
@@ -183,13 +207,17 @@ void function_call_harness_generatort::implt::generate(
183207
recursive_initialization_config.mode = function_to_call.mode;
184208
recursive_initialization_config.pointers_to_treat_as_arrays =
185209
function_arguments_to_treat_as_arrays;
210+
recursive_initialization_config.pointers_to_treat_equal =
211+
function_arguments_to_treat_equal;
186212
recursive_initialization_config.array_name_to_associated_array_size_variable =
187213
function_argument_to_associated_array_size;
188214
for(const auto &pair : function_argument_to_associated_array_size)
189215
{
190216
recursive_initialization_config.variables_that_hold_array_sizes.insert(
191217
pair.second);
192218
}
219+
recursive_initialization_config.potential_null_function_pointers =
220+
map_function_parameters_to_function_argument_names();
193221
recursive_initialization_config.pointers_to_treat_as_cstrings =
194222
function_arguments_to_treat_as_cstrings;
195223
recursive_initialization = util_make_unique<recursive_initializationt>(
@@ -238,14 +266,14 @@ void function_call_harness_generatort::implt::generate_initialisation_code_for(
238266
{
239267
recursive_initialization->initialize(
240268
lhs, from_integer(0, signed_int_type()), block);
241-
if(lhs.type().id() == ID_pointer)
269+
if(recursive_initialization->needs_freeing(lhs))
242270
global_pointers.insert(to_symbol_expr(lhs));
243271
}
244272

245273
void function_call_harness_generatort::validate_options(
246274
const goto_modelt &goto_model)
247275
{
248-
if(p_impl->function == ID_empty)
276+
if(p_impl->function == ID_empty_string)
249277
throw invalid_command_line_argument_exceptiont{
250278
"required parameter entry function not set",
251279
"--" FUNCTION_HARNESS_GENERATOR_FUNCTION_OPT};
@@ -258,6 +286,92 @@ void function_call_harness_generatort::validate_options(
258286
"--" COMMON_HARNESS_GENERATOR_MIN_ARRAY_SIZE_OPT
259287
" --" COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT};
260288
}
289+
290+
const auto function_to_call_pointer =
291+
goto_model.symbol_table.lookup(p_impl->function);
292+
if(function_to_call_pointer == nullptr)
293+
{
294+
throw invalid_command_line_argument_exceptiont{
295+
"entry function `" + id2string(p_impl->function) +
296+
"' does not exist in the symbol table",
297+
"--" FUNCTION_HARNESS_GENERATOR_FUNCTION_OPT};
298+
}
299+
const auto &function_to_call = *function_to_call_pointer;
300+
const auto &ftype = to_code_type(function_to_call.type);
301+
for(auto const &pointer_id : p_impl->function_parameters_to_treat_equal)
302+
{
303+
std::string decorated_pointer_id =
304+
id2string(p_impl->function) + "::" + id2string(pointer_id);
305+
bool is_a_param = false;
306+
typet common_type = empty_typet{};
307+
for(auto const &formal_param : ftype.parameters())
308+
{
309+
if(formal_param.get_identifier() == decorated_pointer_id)
310+
{
311+
is_a_param = true;
312+
if(formal_param.type().id() != ID_pointer)
313+
{
314+
throw invalid_command_line_argument_exceptiont{
315+
id2string(pointer_id) + " is not a pointer parameter",
316+
"--" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT};
317+
}
318+
if(common_type.id() != ID_empty)
319+
{
320+
if(common_type != formal_param.type())
321+
{
322+
throw invalid_command_line_argument_exceptiont{
323+
"pointer arguments of conflicting type",
324+
"--" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT};
325+
}
326+
}
327+
else
328+
common_type = formal_param.type();
329+
}
330+
}
331+
if(!is_a_param)
332+
{
333+
throw invalid_command_line_argument_exceptiont{
334+
id2string(pointer_id) + " is not a parameter",
335+
"--" FUNCTION_HARNESS_GENERATOR_TREAT_POINTERS_EQUAL_OPT};
336+
}
337+
}
338+
339+
const namespacet ns{goto_model.symbol_table};
340+
341+
// Make sure all function pointers that the user asks are nullable are
342+
// present in the symbol table.
343+
for(const auto &nullable :
344+
p_impl->recursive_initialization_config.potential_null_function_pointers)
345+
{
346+
const auto &function_pointer_symbol_pointer =
347+
goto_model.symbol_table.lookup(nullable);
348+
349+
if(function_pointer_symbol_pointer == nullptr)
350+
{
351+
throw invalid_command_line_argument_exceptiont{
352+
"nullable function pointer `" + id2string(nullable) +
353+
"' not found in symbol table",
354+
"--" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT};
355+
}
356+
357+
const auto &function_pointer_type =
358+
ns.follow(function_pointer_symbol_pointer->type);
359+
360+
if(!can_cast_type<pointer_typet>(function_pointer_type))
361+
{
362+
throw invalid_command_line_argument_exceptiont{
363+
"`" + id2string(nullable) + "' is not a pointer",
364+
"--" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT};
365+
}
366+
367+
if(!can_cast_type<code_typet>(
368+
to_pointer_type(function_pointer_type).subtype()))
369+
{
370+
throw invalid_command_line_argument_exceptiont{
371+
"`" + id2string(nullable) + "' is not pointing to a function",
372+
"--" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT};
373+
}
374+
}
261375
}
262376

263377
const symbolt &
@@ -348,6 +462,11 @@ function_call_harness_generatort::implt::declare_arguments(
348462
function_arguments_to_treat_as_cstrings.insert(argument_name);
349463
}
350464

465+
if(function_parameters_to_treat_equal.count(parameter_name) != 0)
466+
{
467+
function_arguments_to_treat_equal.insert(argument_name);
468+
}
469+
351470
auto it = function_parameter_to_associated_array_size.find(parameter_name);
352471
if(it != function_parameter_to_associated_array_size.end())
353472
{
@@ -377,7 +496,7 @@ void function_call_harness_generatort::implt::call_function(
377496
for(auto const &argument : arguments)
378497
{
379498
generate_initialisation_code_for(function_body, argument);
380-
if(argument.type().id() == ID_pointer)
499+
if(recursive_initialization->needs_freeing(argument))
381500
global_pointers.insert(to_symbol_expr(argument));
382501
}
383502
code_function_callt function_call{function_to_call.symbol_expr(),
@@ -386,3 +505,25 @@ void function_call_harness_generatort::implt::call_function(
386505

387506
function_body.add(std::move(function_call));
388507
}
508+
509+
std::unordered_set<irep_idt> function_call_harness_generatort::implt::
510+
map_function_parameters_to_function_argument_names()
511+
{
512+
std::unordered_set<irep_idt> nullables;
513+
for(const auto &nullable :
514+
recursive_initialization_config.potential_null_function_pointers)
515+
{
516+
const auto &nullable_name = id2string(nullable);
517+
const auto &function_prefix = id2string(function) + "::";
518+
if(has_prefix(nullable_name, function_prefix))
519+
{
520+
nullables.insert(
521+
"__goto_harness::" + nullable_name.substr(function_prefix.size()));
522+
}
523+
else
524+
{
525+
nullables.insert(nullable_name);
526+
}
527+
}
528+
return nullables;
529+
}

0 commit comments

Comments
 (0)