Skip to content

Commit f553b83

Browse files
committed
Add implementation of symex_set_field.
1 parent 74f6669 commit f553b83

File tree

3 files changed

+376
-2
lines changed

3 files changed

+376
-2
lines changed

src/goto-symex/shadow_memory.cpp

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,91 @@ void shadow_memoryt::symex_set_field(
9090
goto_symex_statet &state,
9191
const exprt::operandst &arguments)
9292
{
93-
// To be implemented
93+
// parse set_field call
94+
INVARIANT(
95+
arguments.size() == 3, CPROVER_PREFIX "set_field requires 3 arguments");
96+
irep_idt field_name = extract_field_name(arguments[1]);
97+
98+
exprt expr = arguments[0];
99+
typet expr_type = expr.type();
100+
DATA_INVARIANT_WITH_DIAGNOSTICS(
101+
expr_type.id() == ID_pointer,
102+
"shadow memory requires a pointer expression",
103+
irep_pretty_diagnosticst{expr_type});
104+
105+
exprt value = arguments[2];
106+
log_set_field(ns, log, field_name, expr, value);
107+
INVARIANT(
108+
state.shadow_memory.address_fields.count(field_name) == 1,
109+
id2string(field_name) + " should exist");
110+
const auto &addresses = state.shadow_memory.address_fields.at(field_name);
111+
112+
// get value set
113+
replace_invalid_object_by_null(expr);
114+
#ifdef DEBUG_SM
115+
log_set_field(ns, log, field_name, expr, value);
116+
#endif
117+
std::vector<exprt> value_set = state.value_set.get_value_set(expr, ns);
118+
log_value_set(ns, log, value_set);
119+
if(set_field_check_null(ns, log, value_set, expr))
120+
{
121+
log.warning() << "Shadow memory: cannot set shadow memory of NULL"
122+
<< messaget::eom;
123+
return;
124+
}
125+
126+
// build lhs
127+
const exprt &rhs = value;
128+
size_t mux_size = 0;
129+
optionalt<exprt> maybe_lhs =
130+
get_shadow_memory(expr, value_set, addresses, ns, log, mux_size);
131+
132+
// add to equation
133+
if(maybe_lhs.has_value())
134+
{
135+
if(mux_size >= 10)
136+
{
137+
log.warning() << "Shadow memory: mux size set_field: " << mux_size
138+
<< messaget::eom;
139+
}
140+
else
141+
{
142+
log.debug() << "Shadow memory: mux size set_field: " << mux_size
143+
<< messaget::eom;
144+
}
145+
const exprt lhs = deref_expr(*maybe_lhs);
146+
#ifdef DEBUG_SM
147+
log.debug() << "Shadow memory: LHS: " << format(lhs) << messaget::eom;
148+
#endif
149+
if(lhs.type().id() == ID_empty)
150+
{
151+
std::stringstream s;
152+
s << "Shadow memory: cannot set shadow memory via type void* for "
153+
<< format(expr)
154+
<< ". Insert a cast to the type that you want to access.";
155+
throw invalid_input_exceptiont(s.str());
156+
}
157+
// We replicate the rhs value on each byte of the value that we set.
158+
// This allows the get_field or/max semantics to operate correctly
159+
// on unions.
160+
const auto per_byte_rhs =
161+
expr_initializer(lhs.type(), expr.source_location(), ns, rhs);
162+
CHECK_RETURN(per_byte_rhs.has_value());
163+
164+
#ifdef DEBUG_SM
165+
log.debug() << "Shadow memory: RHS: " << format(per_byte_rhs.value())
166+
<< messaget::eom;
167+
#endif
168+
symex_assign(
169+
state,
170+
lhs,
171+
typecast_exprt::conditional_cast(per_byte_rhs.value(), lhs.type()));
172+
}
173+
else
174+
{
175+
log.warning() << "Shadow memory: cannot set_field for " << format(expr)
176+
<< messaget::eom;
177+
}
94178
}
95179

96180
// Function synopsis

src/goto-symex/shadow_memory_util.cpp

Lines changed: 262 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Author: Peter Schrammel
1818
#include <util/format_expr.h>
1919
#include <util/invariant.h>
2020
#include <util/namespace.h>
21-
#include <util/pointer_expr.h>
21+
#include <util/pointer_offset_size.h>
2222
#include <util/ssa_expr.h>
2323
#include <util/std_expr.h>
2424

@@ -54,6 +54,16 @@ static void remove_array_type_l2(typet &type)
5454
size.remove_level_2();
5555
}
5656

57+
exprt deref_expr(const exprt &expr)
58+
{
59+
if(expr.id() == ID_address_of)
60+
{
61+
return to_address_of_expr(expr).object();
62+
}
63+
64+
return dereference_exprt(expr);
65+
}
66+
5767
void clean_pointer_expr(exprt &expr, const typet &type)
5868
{
5969
if(
@@ -81,6 +91,21 @@ void clean_pointer_expr(exprt &expr, const typet &type)
8191
POSTCONDITION(expr.type().id() == ID_pointer);
8292
}
8393

94+
void log_set_field(
95+
const namespacet &ns,
96+
const messaget &log,
97+
const irep_idt &field_name,
98+
const exprt &expr,
99+
const exprt &value)
100+
{
101+
log.conditional_output(
102+
log.debug(), [field_name, ns, expr, value](messaget::mstreamt &mstream) {
103+
mstream << "Shadow memory: set_field: " << id2string(field_name)
104+
<< " for " << format(expr) << " to " << format(value)
105+
<< messaget::eom;
106+
});
107+
}
108+
84109
void log_get_field(
85110
const namespacet &ns,
86111
const messaget &log,
@@ -828,3 +853,239 @@ std::vector<std::pair<exprt, exprt>> get_shadow_dereference_candidates(
828853
}
829854
return result;
830855
}
856+
857+
// TODO: doxygen?
858+
// Unfortunately.
859+
static object_descriptor_exprt
860+
normalize(const object_descriptor_exprt &expr, const namespacet &ns)
861+
{
862+
if(expr.object().id() == ID_symbol)
863+
{
864+
return expr;
865+
}
866+
if(expr.offset().id() == ID_unknown)
867+
{
868+
return expr;
869+
}
870+
871+
object_descriptor_exprt result = expr;
872+
mp_integer offset =
873+
numeric_cast_v<mp_integer>(to_constant_expr(expr.offset()));
874+
exprt object = expr.object();
875+
876+
while(true)
877+
{
878+
if(object.id() == ID_index)
879+
{
880+
const index_exprt &index_expr = to_index_expr(object);
881+
mp_integer index =
882+
numeric_cast_v<mp_integer>(to_constant_expr(index_expr.index()));
883+
884+
offset += *pointer_offset_size(index_expr.type(), ns) * index;
885+
object = index_expr.array();
886+
}
887+
else if(object.id() == ID_member)
888+
{
889+
const member_exprt &member_expr = to_member_expr(object);
890+
const struct_typet &struct_type =
891+
to_struct_type(ns.follow(member_expr.struct_op().type()));
892+
offset +=
893+
*member_offset(struct_type, member_expr.get_component_name(), ns);
894+
object = member_expr.struct_op();
895+
}
896+
else
897+
{
898+
break;
899+
}
900+
}
901+
result.object() = object;
902+
result.offset() = from_integer(offset, expr.offset().type());
903+
return result;
904+
}
905+
906+
bool set_field_check_null(
907+
const namespacet &ns,
908+
const messaget &log,
909+
const std::vector<exprt> &value_set,
910+
const exprt &expr)
911+
{
912+
const null_pointer_exprt null_pointer(to_pointer_type(expr.type()));
913+
if(value_set.size() == 1 && contains_null_or_invalid(value_set, null_pointer))
914+
{
915+
// TODO: duplicated in log_value_set_match
916+
#ifdef DEBUG_SM
917+
log.conditional_output(
918+
log.debug(), [ns, null_pointer, expr](messaget::mstreamt &mstream) {
919+
mstream << "Shadow memory: value set match: " << format(null_pointer)
920+
<< " <-- " << format(expr) << messaget::eom;
921+
mstream << "Shadow memory: ignoring set field on NULL object"
922+
<< messaget::eom;
923+
});
924+
#endif
925+
return true;
926+
}
927+
return false;
928+
}
929+
930+
/// Used for set_field and shadow memory copy
931+
static std::vector<std::pair<exprt, exprt>>
932+
get_shadow_memory_for_matched_object(
933+
const exprt &expr,
934+
const exprt &matched_object,
935+
const std::vector<shadow_memory_statet::shadowed_addresst> &addresses,
936+
const namespacet &ns,
937+
const messaget &log,
938+
bool &exact_match)
939+
{
940+
std::vector<std::pair<exprt, exprt>> result;
941+
for(const auto &shadowed_address : addresses)
942+
{
943+
log_try_shadow_address(ns, log, shadowed_address);
944+
945+
if(!are_types_compatible(expr.type(), shadowed_address.address.type()))
946+
{
947+
#ifdef DEBUG_SM
948+
log.debug() << "Shadow memory: incompatible types "
949+
<< from_type(ns, "", expr.type()) << ", "
950+
<< from_type(ns, "", shadowed_address.address.type())
951+
<< messaget::eom;
952+
#endif
953+
continue;
954+
}
955+
956+
object_descriptor_exprt matched_base_descriptor =
957+
normalize(to_object_descriptor_expr(matched_object), ns);
958+
959+
value_set_dereferencet::valuet dereference =
960+
value_set_dereferencet::build_reference_to(
961+
matched_base_descriptor, expr, ns);
962+
963+
exprt matched_base_address =
964+
address_of_exprt(matched_base_descriptor.object());
965+
clean_string_constant(to_address_of_expr(matched_base_address).op());
966+
967+
// NULL has already been handled in the caller; skip.
968+
if(matched_base_descriptor.object().id() == ID_null_object)
969+
{
970+
continue;
971+
}
972+
const value_set_dereferencet::valuet shadow_dereference =
973+
get_shadow_dereference(
974+
shadowed_address.shadow, matched_base_descriptor, expr, ns, log);
975+
log_value_set_match(
976+
ns,
977+
log,
978+
shadowed_address,
979+
matched_base_address,
980+
dereference,
981+
expr,
982+
shadow_dereference);
983+
984+
const exprt base_cond = get_matched_base_cond(
985+
shadowed_address.address, matched_base_address, ns, log);
986+
log_cond(ns, log, "base_cond", base_cond);
987+
if(base_cond.is_false())
988+
{
989+
continue;
990+
}
991+
992+
const exprt expr_cond =
993+
get_matched_expr_cond(dereference.pointer, expr, ns, log);
994+
if(expr_cond.is_false())
995+
{
996+
continue;
997+
}
998+
999+
if(base_cond.is_true() && expr_cond.is_true())
1000+
{
1001+
#ifdef DEBUG_SM
1002+
log.debug() << "exact match" << messaget::eom;
1003+
#endif
1004+
exact_match = true;
1005+
result.clear();
1006+
result.push_back({base_cond, shadow_dereference.pointer});
1007+
break;
1008+
}
1009+
1010+
if(base_cond.is_true())
1011+
{
1012+
// No point looking at further shadow addresses
1013+
// as only one of them can match.
1014+
#ifdef DEBUG_SM
1015+
log.debug() << "base match" << messaget::eom;
1016+
#endif
1017+
result.clear();
1018+
result.emplace_back(expr_cond, shadow_dereference.pointer);
1019+
break;
1020+
}
1021+
else
1022+
{
1023+
#ifdef DEBUG_SM
1024+
log.debug() << "conditional match" << messaget::eom;
1025+
#endif
1026+
result.emplace_back(
1027+
and_exprt(base_cond, expr_cond), shadow_dereference.pointer);
1028+
}
1029+
}
1030+
return result;
1031+
}
1032+
1033+
optionalt<exprt> get_shadow_memory(
1034+
const exprt &expr,
1035+
const std::vector<exprt> &value_set,
1036+
const std::vector<shadow_memory_statet::shadowed_addresst> &addresses,
1037+
const namespacet &ns,
1038+
const messaget &log,
1039+
size_t &mux_size)
1040+
{
1041+
std::vector<std::pair<exprt, exprt>> conds_values;
1042+
for(const auto &matched_object : value_set)
1043+
{
1044+
if(matched_object.id() != ID_object_descriptor)
1045+
{
1046+
log.warning() << "Shadow memory: value set contains unknown for "
1047+
<< format(expr) << messaget::eom;
1048+
continue;
1049+
}
1050+
if(
1051+
to_object_descriptor_expr(matched_object).root_object().id() ==
1052+
ID_integer_address)
1053+
{
1054+
log.warning() << "Shadow memory: value set contains integer_address for "
1055+
<< format(expr) << messaget::eom;
1056+
continue;
1057+
}
1058+
if(matched_object.type().get_bool(ID_C_is_failed_symbol))
1059+
{
1060+
log.warning() << "Shadow memory: value set contains failed symbol for "
1061+
<< format(expr) << messaget::eom;
1062+
continue;
1063+
}
1064+
1065+
bool exact_match = false;
1066+
auto per_value_set_conds_values = get_shadow_memory_for_matched_object(
1067+
expr, matched_object, addresses, ns, log, exact_match);
1068+
1069+
if(!per_value_set_conds_values.empty())
1070+
{
1071+
if(exact_match)
1072+
{
1073+
conds_values.clear();
1074+
}
1075+
conds_values.insert(
1076+
conds_values.begin(),
1077+
per_value_set_conds_values.begin(),
1078+
per_value_set_conds_values.end());
1079+
mux_size += conds_values.size() - 1;
1080+
if(exact_match)
1081+
{
1082+
break;
1083+
}
1084+
}
1085+
}
1086+
if(!conds_values.empty())
1087+
{
1088+
return build_if_else_expr(conds_values);
1089+
}
1090+
return {};
1091+
}

0 commit comments

Comments
 (0)