Skip to content

Commit dbac963

Browse files
Merge pull request #5183 from xbauch/feature/selective-struct-havoc
Goto-harness support to selectively havoc struct members
2 parents 0002950 + 65bfa60 commit dbac963

File tree

7 files changed

+200
-1
lines changed

7 files changed

+200
-1
lines changed
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
typedef struct st1
5+
{
6+
int data11;
7+
struct innert1
8+
{
9+
int inner_data11;
10+
int inner_data12;
11+
} inner1;
12+
int data12;
13+
} st1_t;
14+
15+
typedef struct st2
16+
{
17+
int data21;
18+
struct innert2
19+
{
20+
int inner_data21;
21+
int inner_data22;
22+
} inner2;
23+
int data22;
24+
} st2_t;
25+
26+
st1_t dummy1 = {.data11 = 0,
27+
.inner1.inner_data11 = 0,
28+
.inner1.inner_data12 = 0,
29+
.data12 = 0};
30+
31+
st2_t dummy2 = {.data21 = 0,
32+
.inner2.inner_data21 = 0,
33+
.inner2.inner_data22 = 0,
34+
.data22 = 0};
35+
36+
void func(st1_t *p)
37+
{
38+
assert(dummy1.data11 == 0); //should succeed
39+
assert(dummy1.inner1.inner_data11 == 0);
40+
assert(dummy1.inner1.inner_data12 == 0); //should fail
41+
assert(dummy1.data12 == 0);
42+
assert(dummy2.data21 == 0); //should succeed
43+
assert(dummy2.inner2.inner_data21 == 0); //should fail
44+
assert(dummy2.inner2.inner_data22 == 0);
45+
assert(dummy2.data22 == 0);
46+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
main.c
3+
--function func --min-null-tree-depth 10 --max-nondet-tree-depth 3 --harness-type call-function --havoc-member 'dummy1.inner1.inner_data12,dummy2.inner2.inner_data21' --nondet-globals
4+
^\[func.assertion.\d+\] line \d+ assertion dummy1.data11 == 0: SUCCESS$
5+
^\[func.assertion.\d+\] line \d+ assertion dummy1.inner1.inner_data11 == 0: SUCCESS$
6+
^\[func.assertion.\d+\] line \d+ assertion dummy1.inner1.inner_data12 == 0: FAILURE$
7+
^\[func.assertion.\d+\] line \d+ assertion dummy1.data12 == 0: SUCCESS$
8+
^\[func.assertion.\d+\] line \d+ assertion dummy2.inner2.inner_data21 == 0: FAILURE$
9+
^EXIT=10$
10+
^SIGNAL=0$
11+
VERIFICATION FAILED
12+
--
13+
^warning: ignoring
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include <assert.h>
2+
#include <stdlib.h>
3+
4+
typedef struct st
5+
{
6+
int data1;
7+
struct innert
8+
{
9+
int inner_data1;
10+
int inner_data2;
11+
} inner;
12+
int data2;
13+
} st_t;
14+
15+
st_t dummy = {.data1 = 0,
16+
.inner.inner_data1 = 0,
17+
.inner.inner_data2 = 0,
18+
.data2 = 0};
19+
20+
void func(st_t *p)
21+
{
22+
assert(dummy.data1 == 0); //should succeed
23+
assert(dummy.inner.inner_data1 == 0);
24+
assert(dummy.inner.inner_data2 == 0); //should fail
25+
assert(dummy.data2 == 0);
26+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
main.c
3+
--function func --min-null-tree-depth 10 --max-nondet-tree-depth 3 --harness-type call-function --havoc-member 'dummy.inner.inner_data2' --nondet-globals
4+
^\[func.assertion.\d+\] line \d+ assertion dummy.data1 == 0: SUCCESS$
5+
^\[func.assertion.\d+\] line \d+ assertion dummy.inner.inner_data1 == 0: SUCCESS$
6+
^\[func.assertion.\d+\] line \d+ assertion dummy.inner.inner_data2 == 0: FAILURE$
7+
^\[func.assertion.\d+\] line \d+ assertion dummy.data2 == 0: SUCCESS$
8+
^EXIT=10$
9+
^SIGNAL=0$
10+
VERIFICATION FAILED
11+
--
12+
^warning: ignoring

src/goto-harness/common_harness_generator_options.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ Author: Diffblue Ltd.
1616
#define COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT "max-array-size"
1717
#define COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT \
1818
"function-pointer-can-be-null"
19+
#define COMMON_HARNESS_GENERATOR_HAVOC_MEMBER_OPT "havoc-member"
1920

2021
// clang-format off
2122
#define COMMON_HARNESS_GENERATOR_OPTIONS \
@@ -24,6 +25,7 @@ Author: Diffblue Ltd.
2425
"(" COMMON_HARNESS_GENERATOR_MIN_ARRAY_SIZE_OPT "):" \
2526
"(" COMMON_HARNESS_GENERATOR_MAX_ARRAY_SIZE_OPT "):" \
2627
"(" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT "):" \
28+
"(" COMMON_HARNESS_GENERATOR_HAVOC_MEMBER_OPT "):" \
2729
// COMMON_HARNESS_GENERATOR_OPTIONS
2830

2931
// clang-format on
@@ -44,7 +46,9 @@ Author: Diffblue Ltd.
4446
" (default: 2)\n" \
4547
"--" COMMON_HARNESS_GENERATOR_FUNCTION_POINTER_CAN_BE_NULL_OPT \
4648
" <function-name>, name of the function(s) pointer parameters\n" \
47-
" that can be NULL pointing."
49+
" that can be NULL pointing." \
50+
"--" COMMON_HARNESS_GENERATOR_HAVOC_MEMBER_OPT \
51+
" path to the member to be havoced\n" \
4852
// COMMON_HARNESS_GENERATOR_HELP
4953

5054
// clang-format on

src/goto-harness/recursive_initialization.cpp

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Author: Diffblue Ltd.
1919
#include <util/std_code.h>
2020
#include <util/std_expr.h>
2121
#include <util/string2int.h>
22+
#include <util/string_utils.h>
2223

2324
#include <functional>
2425
#include <iterator>
@@ -86,6 +87,28 @@ bool recursive_initialization_configt::handle_option(
8687
[](const std::string &opt) -> irep_idt { return irep_idt{opt}; });
8788
return true;
8889
}
90+
else if(option == COMMON_HARNESS_GENERATOR_HAVOC_MEMBER_OPT)
91+
{
92+
const auto list_of_members_string =
93+
harness_options_parser::require_exactly_one_value(
94+
COMMON_HARNESS_GENERATOR_HAVOC_MEMBER_OPT, values);
95+
const auto list_of_members = split_string(list_of_members_string, ',');
96+
for(const auto &member : list_of_members)
97+
{
98+
const auto selection_spec_strings = split_string(member, '.');
99+
100+
selection_specs.push_back({});
101+
auto &selection_spec = selection_specs.back();
102+
std::transform(
103+
selection_spec_strings.begin(),
104+
selection_spec_strings.end(),
105+
std::back_inserter(selection_spec),
106+
[](const std::string &member_name_string) {
107+
return irep_idt{member_name_string};
108+
});
109+
}
110+
return true;
111+
}
89112
return false;
90113
}
91114

@@ -114,6 +137,18 @@ void recursive_initializationt::initialize(
114137
const exprt &depth,
115138
code_blockt &body)
116139
{
140+
if(lhs.id() == ID_symbol && !initialization_config.selection_specs.empty())
141+
{
142+
auto lhs_id = to_symbol_expr(lhs).get_identifier();
143+
for(const auto &selection_spec : initialization_config.selection_specs)
144+
{
145+
if(selection_spec.front() == lhs_id)
146+
{
147+
initialize_selected_member(lhs, depth, body, selection_spec);
148+
return;
149+
}
150+
}
151+
}
117152
// special handling for the case that pointer arguments should be treated
118153
// equal: if the equality is enforced (rather than the pointers may be equal),
119154
// then we don't even build the constructor functions
@@ -956,3 +991,51 @@ code_blockt recursive_initializationt::build_function_pointer_constructor(
956991

957992
return body;
958993
}
994+
995+
void recursive_initializationt::initialize_selected_member(
996+
const exprt &lhs,
997+
const exprt &depth,
998+
code_blockt &body,
999+
const std::vector<irep_idt> &selection_spec)
1000+
{
1001+
PRECONDITION(lhs.id() == ID_symbol);
1002+
PRECONDITION(lhs.type().id() == ID_struct_tag);
1003+
PRECONDITION(!selection_spec.empty());
1004+
1005+
auto component_member = lhs;
1006+
const namespacet ns{goto_model.symbol_table};
1007+
1008+
for(auto it = selection_spec.begin() + 1; it != selection_spec.end(); it++)
1009+
{
1010+
if(component_member.type().id() != ID_struct_tag)
1011+
{
1012+
throw invalid_command_line_argument_exceptiont{
1013+
"'" + id2string(*it) + "' is not a component name",
1014+
"--" COMMON_HARNESS_GENERATOR_HAVOC_MEMBER_OPT};
1015+
}
1016+
const auto &struct_tag_type = to_struct_tag_type(component_member.type());
1017+
const auto &struct_type = to_struct_type(ns.follow_tag(struct_tag_type));
1018+
1019+
bool found = false;
1020+
for(auto const &component : struct_type.components())
1021+
{
1022+
const auto &component_type = component.type();
1023+
const auto component_name = component.get_name();
1024+
1025+
if(*it == component_name)
1026+
{
1027+
component_member =
1028+
member_exprt{component_member, component_name, component_type};
1029+
found = true;
1030+
break;
1031+
}
1032+
}
1033+
if(!found)
1034+
{
1035+
throw invalid_command_line_argument_exceptiont{
1036+
"'" + id2string(*it) + "' is not a component name",
1037+
"--" COMMON_HARNESS_GENERATOR_HAVOC_MEMBER_OPT};
1038+
}
1039+
}
1040+
initialize(component_member, depth, body);
1041+
}

src/goto-harness/recursive_initialization.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ struct recursive_initialization_configt
4242

4343
bool arguments_may_be_equal = false;
4444

45+
std::vector<std::vector<irep_idt>> selection_specs;
46+
4547
std::string to_string() const; // for debugging purposes
4648

4749
/// Parse the options specific for recursive initialisation
@@ -263,6 +265,19 @@ class recursive_initializationt
263265
/// \param result: symbol of the result parameter
264266
/// \return the body of the constructor
265267
code_blockt build_array_string_constructor(const symbol_exprt &result) const;
268+
269+
/// Select the specified struct-member to be non-deterministically
270+
/// initialized.
271+
/// \param lhs: symbol expression of the top structure
272+
/// \param depth: only to be passed
273+
/// \param body: code block for the initializing assignment
274+
/// \param selection_spec: the user specification of the particular member to
275+
/// havoc
276+
void initialize_selected_member(
277+
const exprt &lhs,
278+
const exprt &depth,
279+
code_blockt &body,
280+
const std::vector<irep_idt> &selection_spec);
266281
};
267282

268283
#endif // CPROVER_GOTO_HARNESS_RECURSIVE_INITIALIZATION_H

0 commit comments

Comments
 (0)