Skip to content

Commit 97c41eb

Browse files
committed
goto-symex: optionally support updates across member boundaries
If using the new option --symex-no-member-bounds, goto-symex will rewrite all indexed writes that constant propagation does not show to be within bounds to writes to the member object.
1 parent f78b8f8 commit 97c41eb

File tree

6 files changed

+103
-10
lines changed

6 files changed

+103
-10
lines changed
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
KNOWNBUG
1+
CORE
22
main.c
3-
--no-simplify
3+
--no-simplify --symex-no-member-bounds
44
^VERIFICATION SUCCESSFUL$
55
^EXIT=0$
66
^SIGNAL=0$
@@ -9,3 +9,5 @@ main.c
99
--
1010
This test passes when using the simplifier, but results in "WITH" expressions
1111
that update elements outside their bounds without the simplifier.
12+
--symex-no-member-bounds ensures that such expressions are rewritten by
13+
goto-symex.
Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
KNOWNBUG
1+
CORE
22
main.c
3-
3+
--symex-no-member-bounds
44
^VERIFICATION SUCCESSFUL$
55
^EXIT=0$
66
^SIGNAL=0$
@@ -9,3 +9,5 @@ main.c
99
--
1010
Field sensitivity makes it (currently) impossible to update bytes outside the
1111
particular field.
12+
--symex-no-member-bounds ensures that such expressions are rewritten by
13+
goto-symex.

src/goto-checker/bmc_util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ void run_property_decider(
197197
"(unwind-max):" \
198198
"(ignore-properties-before-unwind-min)" \
199199
"(symex-cache-dereferences)" \
200+
"(symex-no-member-bounds)" \
200201

201202
#define HELP_BMC \
202203
" --paths [strategy] explore paths one at a time\n" \
@@ -252,6 +253,7 @@ void run_property_decider(
252253
" gets blacklisted\n" \
253254
" --graphml-witness filename write the witness in GraphML format to filename\n" /* NOLINT(*) */ \
254255
" --symex-cache-dereferences enable caching of repeated dereferences" \
256+
" --symex-no-member-bounds support updates beyond bounds of a compound member" /* NOLINT(*) */ \
255257
// clang-format on
256258

257259
#endif // CPROVER_GOTO_CHECKER_BMC_UTIL_H

src/goto-symex/symex_assign.cpp

Lines changed: 86 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,14 @@ Author: Daniel Kroening, [email protected]
1111

1212
#include "symex_assign.h"
1313

14-
#include "expr_skeleton.h"
15-
#include "goto_symex_state.h"
14+
#include <util/arith_tools.h>
1615
#include <util/byte_operators.h>
1716
#include <util/expr_util.h>
17+
#include <util/pointer_offset_size.h>
1818
#include <util/range.h>
1919

20+
#include "expr_skeleton.h"
21+
#include "goto_symex_state.h"
2022
#include "symex_config.h"
2123

2224
// We can either use with_exprt or update_exprt when building expressions that
@@ -253,11 +255,90 @@ void symex_assignt::assign_array(
253255
{
254256
const exprt &lhs_array=lhs.array();
255257
const exprt &lhs_index=lhs.index();
256-
const typet &lhs_index_type = lhs_array.type();
258+
const array_typet &lhs_array_type = to_array_type(lhs_array.type());
259+
260+
const bool may_be_out_of_bounds =
261+
symex_config.updates_across_member_bounds &&
262+
(lhs_index.id() != ID_constant ||
263+
lhs_array_type.size().id() != ID_constant ||
264+
numeric_cast_v<mp_integer>(to_constant_expr(lhs_index)) < 0 ||
265+
numeric_cast_v<mp_integer>(to_constant_expr(lhs_index)) >=
266+
numeric_cast_v<mp_integer>(to_constant_expr(lhs_array_type.size())));
267+
268+
if(
269+
may_be_out_of_bounds &&
270+
(lhs_array.id() == ID_member || lhs_array.id() == ID_index))
271+
{
272+
const auto subtype_bytes_opt = size_of_expr(lhs_array.type().subtype(), ns);
273+
CHECK_RETURN(subtype_bytes_opt.has_value());
257274

258-
PRECONDITION(lhs_index_type.id() == ID_array);
275+
exprt new_offset = mult_exprt{
276+
lhs_index,
277+
typecast_exprt::conditional_cast(*subtype_bytes_opt, lhs_index.type())};
259278

260-
if(use_update)
279+
exprt parent;
280+
if(lhs_array.id() == ID_member)
281+
{
282+
const member_exprt &member = to_member_expr(lhs_array);
283+
const auto member_offset = member_offset_expr(member, ns);
284+
CHECK_RETURN(member_offset.has_value());
285+
286+
parent = member.compound();
287+
new_offset = plus_exprt{
288+
typecast_exprt::conditional_cast(*member_offset, new_offset.type()),
289+
new_offset};
290+
}
291+
else
292+
{
293+
const index_exprt &index = to_index_expr(lhs_array);
294+
295+
const auto element_size_opt =
296+
size_of_expr(to_array_type(index.array().type()).subtype(), ns);
297+
CHECK_RETURN(element_size_opt.has_value());
298+
299+
parent = index.array();
300+
new_offset =
301+
plus_exprt{mult_exprt{typecast_exprt::conditional_cast(
302+
*element_size_opt, new_offset.type()),
303+
typecast_exprt::conditional_cast(
304+
index.index(), new_offset.type())},
305+
new_offset};
306+
}
307+
308+
if(symex_config.simplify_opt)
309+
simplify(new_offset, ns);
310+
311+
const byte_update_exprt new_rhs = make_byte_update(parent, new_offset, rhs);
312+
const expr_skeletont new_skeleton =
313+
full_lhs.compose(expr_skeletont::remove_op0(lhs_array));
314+
assign_rec(parent, new_skeleton, new_rhs, guard);
315+
}
316+
else if(
317+
may_be_out_of_bounds && (lhs_array.id() == ID_byte_extract_big_endian ||
318+
lhs_array.id() == ID_byte_extract_little_endian))
319+
{
320+
const byte_extract_exprt &byte_extract = to_byte_extract_expr(lhs_array);
321+
322+
const auto subtype_bytes_opt = size_of_expr(lhs_array.type().subtype(), ns);
323+
CHECK_RETURN(subtype_bytes_opt.has_value());
324+
325+
exprt new_offset =
326+
plus_exprt{mult_exprt{lhs_index,
327+
typecast_exprt::conditional_cast(
328+
*subtype_bytes_opt, lhs_index.type())},
329+
typecast_exprt::conditional_cast(
330+
byte_extract.offset(), lhs_index.type())};
331+
332+
if(symex_config.simplify_opt)
333+
simplify(new_offset, ns);
334+
335+
byte_extract_exprt new_lhs = byte_extract;
336+
new_lhs.offset() = new_offset;
337+
new_lhs.type() = rhs.type();
338+
339+
assign_rec(new_lhs, full_lhs, rhs, guard);
340+
}
341+
else if(use_update)
261342
{
262343
// turn
263344
// a[i]=e

src/goto-symex/symex_config.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ struct symex_configt final
6363
/// Used in goto_symext::dereference_rec
6464
bool cache_dereferences;
6565

66+
/// Handle assignments beyond the bounds prescribed by a struct or array
67+
/// access path. See tests in regression/cbmc/member_bounds* for examples.
68+
bool updates_across_member_bounds;
69+
6670
/// \brief Construct a symex_configt using options specified in an
6771
/// \ref optionst
6872
explicit symex_configt(const optionst &options);

src/goto-symex/symex_main.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ symex_configt::symex_configt(const optionst &options)
5656
: DEFAULT_MAX_FIELD_SENSITIVITY_ARRAY_SIZE),
5757
complexity_limits_active(
5858
options.get_signed_int_option("symex-complexity-limit") > 0),
59-
cache_dereferences{options.get_bool_option("symex-cache-dereferences")}
59+
cache_dereferences{options.get_bool_option("symex-cache-dereferences")},
60+
updates_across_member_bounds{
61+
options.get_bool_option("symex-no-member-bounds")}
6062
{
6163
}
6264

0 commit comments

Comments
 (0)