Skip to content

Commit 8d6f87b

Browse files
author
Enrico Steffinlongo
authored
Merge pull request #7404 from esteffin/esteffin/fix-trace-invariant-violation
Fix trace invariant violation on smt2 incremental backend
2 parents ce68d67 + 10806ee commit 8d6f87b

11 files changed

+123
-55
lines changed

regression/cbmc-incr-smt2/pointers-conversions/byte_extract_issue.c renamed to regression/cbmc-incr-smt2/pointers-conversions/byte_extract.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ int main()
66
uint32_t input;
77
uint32_t original = input;
88
uint8_t *ptr = (uint8_t *)(&input);
9-
assert(*ptr == 0); // falsifiable
9+
assert(*ptr != 42); // falsifiable
1010
*ptr = ~(*ptr);
1111
assert(input != original); // valid
1212
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
CORE
2+
byte_extract.c
3+
--trace
4+
Running incremental SMT2 solving via
5+
Building error trace
6+
\[main\.assertion\.\d+\] line \d+ assertion \*ptr != 42: FAILURE
7+
\[main\.assertion\.\d+\] line \d+ assertion input != original: SUCCESS
8+
input=42ul? \(00000000 00000000 00000000 00101010\)
9+
original=42ul? \(00000000 00000000 00000000 00101010\)
10+
Violated property:
11+
file byte_extract.c function main line \d+ thread \d+
12+
assertion \*ptr != 42
13+
^EXIT=10$
14+
^SIGNAL=0$
15+
--
16+
Reached unimplemented Generation of SMT formula for byte extract expression: byte_extract_little_endian
17+
--
18+
This test is here to document our lack of support for byte_extract_little_endian
19+
in the pointers support for the new SMT backend.

regression/cbmc-incr-smt2/pointers-conversions/byte_extract_issue.desc

-10
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
4+
int main()
5+
{
6+
uint32_t x = 0u;
7+
uint8_t *ptr = (uint8_t *)(&x);
8+
uint32_t offset;
9+
__CPROVER_assume(offset >= 0u && offset < 4u);
10+
*(ptr + offset) = 1u;
11+
assert(x != 256u);
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
CORE
2+
byte_update.c
3+
--trace
4+
Running incremental SMT2 solving via
5+
Building error trace
6+
\[main\.assertion\.\d+\] line \d+ assertion x != 256u: FAILURE
7+
State \d+ file byte_update\.c function main line \d+ thread \d
8+
offset=1ul? \(00000000 00000000 00000000 00000001\)
9+
Assumption:
10+
file byte_update\.c line \d+ function main
11+
offset >= 0u && offset < 4u
12+
State \d+ file byte_update\.c function main line \d+ thread \d+
13+
byte_extract_little_endian\(x, \(signed long( long)? int\)offset, uint8_t\)=1 \(00000001\)
14+
Violated property:
15+
file byte_update.c function main line \d+ thread \d+
16+
assertion x != 256u
17+
^EXIT=10$
18+
^SIGNAL=0$
19+
--
20+
Reached unimplemented Generation of SMT formula for byte extract expression: byte_update_little_endian
21+
--
22+
This test is here to document our lack of support for byte_update_little_endian
23+
in the pointers support for the new SMT backend.

regression/cbmc-incr-smt2/pointers-conversions/byte_update_issue.c

-12
This file was deleted.

regression/cbmc-incr-smt2/pointers-conversions/byte_update_issue.desc

-10
This file was deleted.

src/solvers/smt2_incremental/convert_expr_to_smt.cpp

+7-1
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ extension_for_type(const typet &type)
164164
return smt_bit_vector_theoryt::sign_extend;
165165
if(can_cast_type<unsignedbv_typet>(type))
166166
return smt_bit_vector_theoryt::zero_extend;
167+
if(can_cast_type<bv_typet>(type))
168+
return smt_bit_vector_theoryt::zero_extend;
167169
UNREACHABLE;
168170
}
169171

@@ -947,6 +949,9 @@ static smt_termt convert_to_smt_shift(
947949
INVARIANT(
948950
first_bit_vector_sort && second_bit_vector_sort,
949951
"Shift expressions are expected to have bit vector operands.");
952+
INVARIANT(
953+
shift.type() == shift.op0().type(),
954+
"Shift expression type must be equals to first operand type.");
950955
const std::size_t first_width = first_bit_vector_sort->bit_width();
951956
const std::size_t second_width = second_bit_vector_sort->bit_width();
952957
if(first_width > second_width)
@@ -958,10 +963,11 @@ static smt_termt convert_to_smt_shift(
958963
}
959964
else if(first_width < second_width)
960965
{
961-
return factory(
966+
const auto result = factory(
962967
extension_for_type(shift.op0().type())(second_width - first_width)(
963968
first_operand),
964969
second_operand);
970+
return smt_bit_vector_theoryt::extract(first_width - 1, 0)(result);
965971
}
966972
else
967973
{

src/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

+36-10
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,29 @@ exprt smt2_incremental_decision_proceduret::get_expr(
411411
get_value_response->pairs()[0].get().value(), type);
412412
}
413413

414+
// This is a fall back which builds resulting expression based on getting the
415+
// values of its operands. It is used during trace building in the case where
416+
// certain kinds of expression appear on the left hand side of an
417+
// assignment. For example in the following trace assignment -
418+
// `byte_extract_little_endian(x, offset) = 1`
419+
// `::get` will be called on `byte_extract_little_endian(x, offset)` and
420+
// we build a resulting expression where `x` and `offset` are substituted
421+
// with their values.
422+
static exprt build_expr_based_on_getting_operands(
423+
const exprt &expr,
424+
const stack_decision_proceduret &decision_procedure)
425+
{
426+
exprt copy = expr;
427+
for(auto &op : copy.operands())
428+
{
429+
exprt eval_op = decision_procedure.get(op);
430+
if(eval_op.is_nil())
431+
return nil_exprt{};
432+
op = std::move(eval_op);
433+
}
434+
return copy;
435+
}
436+
414437
exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
415438
{
416439
log.conditional_output(log.debug(), [&](messaget::mstreamt &debug) {
@@ -431,29 +454,31 @@ exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
431454
return {};
432455
return smt_array_theoryt::select(*array, *index);
433456
}
434-
return get_identifier(
435-
expr, expression_handle_identifiers, expression_identifiers);
436-
}();
437-
if(!descriptor)
438-
{
457+
if(
458+
auto identifier_descriptor = get_identifier(
459+
expr, expression_handle_identifiers, expression_identifiers))
460+
{
461+
return identifier_descriptor;
462+
}
439463
if(gather_dependent_expressions(expr).empty())
440464
{
441465
INVARIANT(
442466
objects_are_already_tracked(expr, object_map),
443467
"Objects in expressions being read should already be tracked from "
444468
"point of being set/handled.");
445-
descriptor = ::convert_expr_to_smt(
469+
return ::convert_expr_to_smt(
446470
expr,
447471
object_map,
448472
pointer_sizes_map,
449473
object_size_function.make_application,
450474
is_dynamic_object_function.make_application);
451475
}
452-
else
476+
return {};
477+
}();
478+
if(!descriptor)
479+
{
480+
if(const auto symbol_expr = expr_try_dynamic_cast<symbol_exprt>(expr))
453481
{
454-
const auto symbol_expr = expr_try_dynamic_cast<symbol_exprt>(expr);
455-
INVARIANT(
456-
symbol_expr, "Unhandled expressions are expected to be symbols");
457482
// Note this case is currently expected to be encountered during trace
458483
// generation for -
459484
// * Steps which were removed via --slice-formula.
@@ -466,6 +491,7 @@ exprt smt2_incremental_decision_proceduret::get(const exprt &expr) const
466491
<< symbol_expr->get_identifier() << messaget::eom;
467492
return expr;
468493
}
494+
return build_expr_based_on_getting_operands(expr, *this);
469495
}
470496
if(const auto array_type = type_try_dynamic_cast<array_typet>(expr.type()))
471497
{

unit/solvers/smt2_incremental/convert_expr_to_smt.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -1209,10 +1209,11 @@ TEST_CASE(
12091209
symbol_exprt{"foo", make_type(8)},
12101210
symbol_exprt{"bar", make_type(32)});
12111211
INFO("Input expr: " + input.pretty(2, 0));
1212-
const smt_termt expected_result = make_shift_term(
1213-
make_extension(24)(
1214-
smt_identifier_termt{"foo", smt_bit_vector_sortt{8}}),
1215-
smt_identifier_termt{"bar", smt_bit_vector_sortt{32}});
1212+
const smt_termt expected_result =
1213+
smt_bit_vector_theoryt::extract(7, 0)(make_shift_term(
1214+
make_extension(24)(
1215+
smt_identifier_termt{"foo", smt_bit_vector_sortt{8}}),
1216+
smt_identifier_termt{"bar", smt_bit_vector_sortt{32}}));
12161217
CHECK(test.convert(input) == expected_result);
12171218
}
12181219
}

unit/solvers/smt2_incremental/smt2_incremental_decision_procedure.cpp

+20-7
Original file line numberDiff line numberDiff line change
@@ -539,13 +539,26 @@ TEST_CASE(
539539
}
540540
SECTION("Invariant violated due to expression in unexpected form.")
541541
{
542-
const mult_exprt unexpected{foo.symbol_expr(), from_integer(2, foo.type)};
543-
const cbmc_invariants_should_throwt invariants_throw;
544-
REQUIRE_THROWS_MATCHES(
545-
test.procedure.get(unexpected),
546-
invariant_failedt,
547-
invariant_failure_containing(
548-
"Unhandled expressions are expected to be symbols"));
542+
const auto offset = from_integer(2, signedbv_typet{64});
543+
const byte_extract_exprt byte_extract_expr{
544+
ID_byte_extract_little_endian,
545+
foo.symbol_expr(),
546+
offset,
547+
8,
548+
unsignedbv_typet{8}};
549+
test.mock_responses.push_back(
550+
smt_get_value_responset{{{foo_term, term_42}}});
551+
test.mock_responses.push_back(smt_get_value_responset{
552+
{{smt_bit_vector_constant_termt{2, 64},
553+
smt_bit_vector_constant_termt{2, 64}}}});
554+
REQUIRE(
555+
test.procedure.get(byte_extract_expr) ==
556+
byte_extract_exprt{
557+
ID_byte_extract_little_endian,
558+
expr_42,
559+
offset,
560+
8,
561+
unsignedbv_typet{8}});
549562
}
550563
SECTION("Error handling of mismatched response.")
551564
{

0 commit comments

Comments
 (0)