Skip to content

Commit f33b96a

Browse files
authored
Merge pull request #7935 from esteffin/esteffin/sm-aggregate-on-non-byte-bv
Fix shadow memory missing aggregation on non-compound bitvector types
2 parents c435f43 + 3251ce4 commit f33b96a

File tree

10 files changed

+215
-118
lines changed

10 files changed

+215
-118
lines changed
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
__CPROVER_field_decl_local("uninitialized", (char)0);
6+
__CPROVER_field_decl_global("uninitialized", (char)0);
7+
8+
int i;
9+
char *a = &i;
10+
11+
__CPROVER_set_field(a + 1, "uninitialized", 1);
12+
13+
assert(__CPROVER_get_field(a, "uninitialized") == 0);
14+
assert(__CPROVER_get_field(&i, "uninitialized") == 1);
15+
assert(__CPROVER_get_field(a + 1, "uninitialized") == 1);
16+
// Expecting the remaining bytes to be untouched by the setting of the field.
17+
assert(__CPROVER_get_field(a + 2, "uninitialized") == 0);
18+
assert(__CPROVER_get_field(a + 3, "uninitialized") == 0);
19+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#include <assert.h>
2+
3+
int main()
4+
{
5+
__CPROVER_field_decl_local("uninitialized", (_Bool)0);
6+
__CPROVER_field_decl_global("uninitialized", (_Bool)0);
7+
8+
int i;
9+
char *a = &i;
10+
11+
__CPROVER_set_field(a + 1, "uninitialized", 1);
12+
13+
assert(__CPROVER_get_field(a, "uninitialized") == 0);
14+
assert(__CPROVER_get_field(a + 1, "uninitialized") == 1);
15+
assert(__CPROVER_get_field(&i, "uninitialized") == 1);
16+
// Expecting the remaining bytes to be untouched by the setting of the field.
17+
assert(__CPROVER_get_field(a + 2, "uninitialized") == 0);
18+
assert(__CPROVER_get_field(a + 3, "uninitialized") == 0);
19+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
CORE
2+
main.c
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^VERIFICATION SUCCESSFUL$
7+
--
8+
^warning: ignoring
9+
--

regression/cbmc-shadow-memory/union-get-max1/main.c

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,18 @@ union UNIONNAME
1414
char x3[3];
1515
};
1616

17+
// [Shadow] Memory layout (PP is padding)
18+
// u = [ byte1 byte2 byte3 byte4 byte5 byte6 ]
19+
// u.x1 = [ X1 X1 X1 X1 PP PP ]
20+
// u.x2 = [ Y1 PP Y2 Y2 Y3 Y3 ]
21+
// u.x3 = [ X3[0] X3[1] X3[2] PP PP PP ]
22+
1723
int main()
1824
{
1925
__CPROVER_field_decl_local("field2", (__CPROVER_bitvector[6])0);
2026

2127
union UNIONNAME u;
28+
// u = [0x00 0x00 0x00 0x00 0x00 0x00]
2229

2330
assert(__CPROVER_get_field(&u, "field2") == 0);
2431
assert(__CPROVER_get_field(&(u.x1), "field2") == 0);
@@ -32,6 +39,7 @@ int main()
3239
assert(__CPROVER_get_field(&(u.x3[2]), "field2") == 0);
3340

3441
__CPROVER_set_field(&(u.x1), "field2", 1);
42+
// u = [0x02 0x01 0x01 0x01 0x00 0x00]
3543
assert(__CPROVER_get_field(&u, "field2") == 1);
3644
assert(__CPROVER_get_field(&(u.x1), "field2") == 1);
3745
assert(__CPROVER_get_field(&(u.x2), "field2") == 1);
@@ -44,6 +52,7 @@ int main()
4452
assert(__CPROVER_get_field(&(u.x3[2]), "field2") == 1);
4553

4654
__CPROVER_set_field(&(u.x2.y1), "field2", 2);
55+
// u = [0x02 0x01 0x01 0x01 0x00 0x00]
4756
assert(__CPROVER_get_field(&u, "field2") == 2);
4857
assert(__CPROVER_get_field(&(u.x1), "field2") == 2);
4958
assert(__CPROVER_get_field(&(u.x2), "field2") == 2);
@@ -56,8 +65,9 @@ int main()
5665
assert(__CPROVER_get_field(&(u.x3[2]), "field2") == 1);
5766

5867
__CPROVER_set_field(&(u.x2.y2), "field2", 3);
68+
// u = [0x02 0x01 0x03 0x03 0x00 0x00]
5969
assert(__CPROVER_get_field(&u, "field2") == 3);
60-
assert(__CPROVER_get_field(&(u.x1), "field2") == 2);
70+
assert(__CPROVER_get_field(&(u.x1), "field2") == 3);
6171
assert(__CPROVER_get_field(&(u.x2), "field2") == 3);
6272
assert(__CPROVER_get_field(&(u.x2.y1), "field2") == 2);
6373
assert(__CPROVER_get_field(&(u.x2.y2), "field2") == 3);
@@ -68,8 +78,9 @@ int main()
6878
assert(__CPROVER_get_field(&(u.x3[2]), "field2") == 3);
6979

7080
__CPROVER_set_field(&(u.x2.y3), "field2", 4);
81+
// u = [0x02 0x01 0x03 0x03 0x04 0x04]
7182
assert(__CPROVER_get_field(&u, "field2") == 4);
72-
assert(__CPROVER_get_field(&(u.x1), "field2") == 2);
83+
assert(__CPROVER_get_field(&(u.x1), "field2") == 3);
7384
assert(__CPROVER_get_field(&(u.x2), "field2") == 4);
7485
assert(__CPROVER_get_field(&(u.x2.y1), "field2") == 2);
7586
assert(__CPROVER_get_field(&(u.x2.y2), "field2") == 3);
@@ -78,4 +89,21 @@ int main()
7889
assert(__CPROVER_get_field(&(u.x3[0]), "field2") == 2);
7990
assert(__CPROVER_get_field(&(u.x3[1]), "field2") == 1);
8091
assert(__CPROVER_get_field(&(u.x3[2]), "field2") == 3);
92+
93+
__CPROVER_set_field(&(u.x3[1]), "field2", 5);
94+
// u = [0x02 0x05 0x03 0x03 0x04 0x04]
95+
assert(__CPROVER_get_field(&u, "field2") == 5);
96+
assert(__CPROVER_get_field(&(u.x1), "field2") == 5);
97+
assert(__CPROVER_get_field(&(u.x2), "field2") == 5);
98+
assert(__CPROVER_get_field(&(u.x2.y1), "field2") == 2);
99+
assert(__CPROVER_get_field(&(u.x2.y2), "field2") == 3);
100+
assert(__CPROVER_get_field(&(u.x2.y3), "field2") == 4);
101+
// Not allowed: assert(__CPROVER_get_field(u.x3, "field2") == 5);
102+
assert(__CPROVER_get_field(&(u.x3[0]), "field2") == 2);
103+
assert(__CPROVER_get_field(&(u.x3[1]), "field2") == 5);
104+
assert(__CPROVER_get_field(&(u.x3[2]), "field2") == 3);
105+
106+
// Failing assertion added to get trace and to test what the inner
107+
// representation is.
108+
assert(__CPROVER_get_field(&u, "field2") == 42);
81109
}

regression/cbmc-shadow-memory/union-get-max1/test.desc

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
CORE
22
main.c
3-
4-
^EXIT=0$
3+
--trace
4+
^EXIT=10$
55
^SIGNAL=0$
6-
^VERIFICATION SUCCESSFUL$
6+
^VERIFICATION FAILED$
7+
SM__&u!0@1__field2\.x2=\{ \.y1=1, \.\$pad1=1, \.y2=257, \.y3=0 \} \(\{ 00000001, 00000001, 00000001 00000001, 00000000 00000000 \}\)$
8+
SM__&u!0@1__field2\.x2=\{ \.y1=2, \.\$pad1=1, \.y2=257, \.y3=0 \} \(\{ 00000010, 00000001, 00000001 00000001, 00000000 00000000 \}\)$
9+
SM__&u!0@1__field2\.x2=\{ \.y1=2, \.\$pad1=1, \.y2=771, \.y3=0 \} \(\{ 00000010, 00000001, 00000011 00000011, 00000000 00000000 \}\)$
10+
SM__&u!0@1__field2\.x2=\{ \.y1=2, \.\$pad1=1, \.y2=771, \.y3=1028 \} \(\{ 00000010, 00000001, 00000011 00000011, 00000100 00000100 \}\)$
11+
SM__&u!0@1__field2\.x2=\{ \.y1=2, \.\$pad1=5, \.y2=771, \.y3=1028 \} \(\{ 00000010, 00000101, 00000011 00000011, 00000100 00000100 \}\)$
712
--
813
^warning: ignoring
914
--

src/goto-symex/shadow_memory_util.cpp

Lines changed: 93 additions & 93 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,15 @@ Author: Peter Schrammel
1515
#include <util/bitvector_expr.h>
1616
#include <util/byte_operators.h>
1717
#include <util/c_types.h>
18+
#include <util/config.h>
1819
#include <util/format_expr.h>
1920
#include <util/invariant.h>
2021
#include <util/namespace.h>
2122
#include <util/pointer_offset_size.h>
2223
#include <util/ssa_expr.h>
2324
#include <util/std_expr.h>
2425

26+
#include <solvers/flattening/boolbv_width.h>
2527

2628
// TODO: change DEBUG_SM to DEBUG_SHADOW_MEMORY (it also appears in other files)
2729

@@ -428,119 +430,120 @@ exprt compute_or_over_cells(
428430
return or_values(values, field_type);
429431
}
430432

431-
// TODO: doxygen?
432-
static void
433-
max_element(const exprt &element, const typet &field_type, exprt &max)
433+
/// Create an expression comparing the element at `expr_iterator` with the
434+
/// following elements of the collection (or `nil_exprt`if none) and return a
435+
/// pair `(condition, element)` such that if the condition is `true`, then the
436+
/// element is the max of the collection in the interval denoted by
437+
/// `expr_iterator` and `end`.
438+
/// \param expr_iterator the iterator pointing at the element to compare to.
439+
/// \param end the end of collection iterator.
440+
/// \return A pair (cond, elem) containing the condition and the max element.
441+
std::pair<exprt, exprt> compare_to_collection(
442+
const std::vector<exprt>::const_iterator &expr_iterator,
443+
const std::vector<exprt>::const_iterator &end)
434444
{
435-
exprt value = typecast_exprt::conditional_cast(element, field_type);
436-
if(max.is_nil())
445+
// We need at least an element in the collection
446+
INVARIANT(expr_iterator != end, "Cannot compute max of an empty collection");
447+
const exprt &current_expr = *expr_iterator;
448+
449+
// Iterator for the other elements in the collection in the interval denoted
450+
// by `expr_iterator` and `end`.
451+
std::vector<exprt>::const_iterator expr_to_compare_to =
452+
std::next(expr_iterator);
453+
if(expr_to_compare_to == end)
437454
{
438-
max = value;
455+
return {nil_exprt{}, current_expr};
439456
}
440-
else
457+
458+
std::vector<exprt> comparisons;
459+
for(; expr_to_compare_to != end; ++expr_to_compare_to)
441460
{
442-
max = if_exprt(binary_predicate_exprt(value, ID_gt, max), value, max);
461+
// Compare the current element with the n-th following it
462+
comparisons.emplace_back(
463+
binary_predicate_exprt(current_expr, ID_gt, *expr_to_compare_to));
443464
}
465+
466+
return {and_exprt(comparisons), current_expr};
444467
}
445468

446-
// TODO: doxygen?
447-
static void max_over_bytes(
448-
const exprt &value,
449-
const typet &type,
450-
const typet &field_type,
451-
exprt &max)
469+
/// Combine each (condition, value) element in the input collection into a
470+
/// if-then-else expression such as
471+
/// (cond_1 ? val_1 : (cond_2 ? val_2 : ... : val_n))
472+
/// \param conditions_and_values collection containing codnition-value pairs
473+
/// \return the combined max expression
474+
static exprt combine_condition_and_max_values(
475+
const std::vector<std::pair<exprt, exprt>> &conditions_and_values)
452476
{
453-
const size_t size = to_bitvector_type(type).get_width() / 8;
454-
max_element(value, field_type, max);
455-
for(size_t i = 1; i < size; ++i)
477+
// We need at least one element
478+
INVARIANT(
479+
conditions_and_values.size() > 0,
480+
"Cannot compute max of an empty collection");
481+
482+
// We use reverse-iterator, so the last element is the one in the last else
483+
// case.
484+
auto reverse_ite = conditions_and_values.rbegin();
485+
486+
// The last element must have `nil_exprt` as condition
487+
INVARIANT(
488+
reverse_ite->first == nil_exprt{},
489+
"Last element of condition-value list must have nil_exprt condition.");
490+
491+
exprt res = std::move(reverse_ite->second);
492+
493+
for(++reverse_ite; reverse_ite != conditions_and_values.rend(); ++reverse_ite)
456494
{
457-
max_element(
458-
lshr_exprt(value, from_integer(8 * i, size_type())), field_type, max);
495+
res = if_exprt(reverse_ite->first, reverse_ite->second, res);
459496
}
497+
498+
return res;
460499
}
461500

462-
// TODO: doxygen?
463-
static void max_elements(
464-
exprt element,
465-
const typet &field_type,
466-
const namespacet &ns,
467-
const messaget &log,
468-
const bool is_union,
469-
exprt &max)
501+
/// Create an expression encoding the max operation over the collection `values`
502+
/// \param values an `exprt` that encodes the max of `values`
503+
/// \return an `exprt` encoding the max operation over the collection `values`
504+
static exprt create_max_expr(const std::vector<exprt> &values)
470505
{
471-
element = conditional_cast_floatbv_to_unsignedbv(element);
472-
if(element.type().id() == ID_unsignedbv || element.type().id() == ID_signedbv)
473-
{
474-
if(is_union)
475-
{
476-
max_over_bytes(element, element.type(), field_type, max);
477-
}
478-
else
479-
{
480-
max_element(element, field_type, max);
481-
}
482-
}
483-
else
506+
std::vector<std::pair<exprt, exprt>> rows;
507+
rows.reserve(values.size());
508+
for(auto byte_it = values.begin(); byte_it != values.end(); ++byte_it)
484509
{
485-
exprt value =
486-
compute_max_over_cells(element, field_type, ns, log, is_union);
487-
max_element(value, field_type, max);
510+
// Create a pair condition-element where the condition is the comparison of
511+
// the element with all the ones contained in the rest of the collection.
512+
rows.emplace_back(compare_to_collection(byte_it, values.end()));
488513
}
514+
515+
return combine_condition_and_max_values(rows);
489516
}
490517

491518
exprt compute_max_over_cells(
492519
const exprt &expr,
493520
const typet &field_type,
494-
const namespacet &ns,
495-
const messaget &log,
496-
const bool is_union)
521+
const namespacet &ns)
497522
{
498-
const typet type = ns.follow(expr.type());
523+
// Compute the bit-width of the type of `expr`.
524+
std::size_t size = boolbv_widtht{ns}(expr.type());
499525

500-
if(type.id() == ID_struct || type.id() == ID_union)
526+
// Compute how many bytes are in `expr`
527+
std::size_t byte_count = size / config.ansi_c.char_width;
528+
529+
// Extract each byte of `expr` by using byte_extract.
530+
std::vector<exprt> extracted_bytes;
531+
extracted_bytes.reserve(byte_count);
532+
for(std::size_t i = 0; i < byte_count; ++i)
501533
{
502-
exprt max = nil_exprt();
503-
for(const auto &component : to_struct_union_type(type).components())
504-
{
505-
if(component.get_is_padding())
506-
{
507-
continue;
508-
}
509-
max_elements(
510-
member_exprt(expr, component), field_type, ns, log, is_union, max);
511-
}
512-
return max;
534+
extracted_bytes.emplace_back(make_byte_extract(
535+
expr, from_integer(i, unsigned_long_int_type()), field_type));
513536
}
514-
else if(type.id() == ID_array)
515-
{
516-
const array_typet &array_type = to_array_type(type);
517-
if(array_type.size().is_constant())
518-
{
519-
exprt max = nil_exprt();
520-
const mp_integer size =
521-
numeric_cast_v<mp_integer>(to_constant_expr(array_type.size()));
522-
for(mp_integer index = 0; index < size; ++index)
523-
{
524-
max_elements(
525-
index_exprt(expr, from_integer(index, index_type())),
526-
field_type,
527-
ns,
528-
log,
529-
is_union,
530-
max);
531-
}
532-
return max;
533-
}
534-
else
535-
{
536-
log.warning()
537-
<< "Shadow memory: cannot compute max over variable-size array "
538-
<< format(expr) << messaget::eom;
539-
}
540-
}
541-
// TODO: This is incorrect when accessing non-0 offsets of scalars.
542-
return typecast_exprt::conditional_cast(
543-
conditional_cast_floatbv_to_unsignedbv(expr), field_type);
537+
538+
// Compute the max of the bytes extracted from `expr`.
539+
exprt max_expr = create_max_expr(extracted_bytes);
540+
541+
INVARIANT(
542+
max_expr.type() == field_type,
543+
"Aggregated max value type must be the same as shadow memory field's "
544+
"type.");
545+
546+
return max_expr;
544547
}
545548

546549
// TODO: doxygen?
@@ -819,10 +822,7 @@ std::vector<std::pair<exprt, exprt>> get_shadow_dereference_candidates(
819822
else
820823
{
821824
// Value is of other (bitvector) type, so aggregate with max
822-
value = typecast_exprt::conditional_cast(
823-
compute_max_over_cells(
824-
shadow_dereference.value, field_type, ns, log, is_union),
825-
lhs_type);
825+
value = compute_max_over_cells(shadow_dereference.value, field_type, ns);
826826
}
827827

828828
const exprt base_cond = get_matched_base_cond(

0 commit comments

Comments
 (0)