Skip to content

Commit d005873

Browse files
committed
Add support to selectively havoc struct members
to goto-harness. By means of a new option `member-selection` the user can now specify a struct-within-struct path, e.g. `struct_outer.struct_inner.data` to specify only which member they want to havoc. Includes a test.
1 parent 0002950 commit d005873

File tree

5 files changed

+132
-2
lines changed

5 files changed

+132
-2
lines changed
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 --member-selection '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_MEMBER_SELECTION_OPT "member-selection"
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_MEMBER_SELECTION_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_MEMBER_SELECTION_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: 77 additions & 1 deletion
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,22 @@ 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_MEMBER_SELECTION_OPT)
91+
{
92+
auto selection_spec_strings = split_string(
93+
harness_options_parser::require_exactly_one_value(
94+
COMMON_HARNESS_GENERATOR_MEMBER_SELECTION_OPT, values),
95+
'.');
96+
97+
std::transform(
98+
selection_spec_strings.begin(),
99+
selection_spec_strings.end(),
100+
std::back_inserter(selection_spec),
101+
[](const std::string &member_name_string) {
102+
return irep_idt{member_name_string};
103+
});
104+
return true;
105+
}
89106
return false;
90107
}
91108

@@ -114,10 +131,20 @@ void recursive_initializationt::initialize(
114131
const exprt &depth,
115132
code_blockt &body)
116133
{
134+
if(
135+
lhs.id() == ID_symbol && !initialization_config.selection_spec.empty() &&
136+
initialization_config.selection_spec.front() ==
137+
to_symbol_expr(lhs).get_identifier())
138+
{
139+
initialize_selected_member(lhs, depth, body);
140+
return;
141+
}
117142
// special handling for the case that pointer arguments should be treated
118143
// equal: if the equality is enforced (rather than the pointers may be equal),
119144
// then we don't even build the constructor functions
120-
if(lhs.id() == ID_symbol)
145+
if(
146+
lhs.id() == ID_symbol &&
147+
should_be_treated_equal(to_symbol_expr(lhs).get_identifier()))
121148
{
122149
const auto maybe_cluster_index =
123150
find_equal_cluster(to_symbol_expr(lhs).get_identifier());
@@ -956,3 +983,52 @@ code_blockt recursive_initializationt::build_function_pointer_constructor(
956983

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

src/goto-harness/recursive_initialization.h

Lines changed: 12 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<irep_idt> selection_spec;
46+
4547
std::string to_string() const; // for debugging purposes
4648

4749
/// Parse the options specific for recursive initialisation
@@ -263,6 +265,16 @@ 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+
void initialize_selected_member(
275+
const exprt &lhs,
276+
const exprt &depth,
277+
code_blockt &body);
266278
};
267279

268280
#endif // CPROVER_GOTO_HARNESS_RECURSIVE_INITIALIZATION_H

0 commit comments

Comments
 (0)