Skip to content

Commit da7edc5

Browse files
authored
Merge pull request #6815 from NlightNFotis/pointer_objects_smt
Conversion of `pointer_object_exprt` and `pointer_offset_exprt` for new SMT backend
2 parents 91e6220 + e5f91f2 commit da7edc5

12 files changed

+295
-4
lines changed
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
int main()
2+
{
3+
int a = 10;
4+
int nondet_bool;
5+
int flag = 1;
6+
7+
int *b = &a;
8+
int *c = nondet_bool ? &a : 0;
9+
int *d = flag ? &a : 0;
10+
int *e;
11+
12+
// __CPROVER_same_object is True when
13+
// `__CPROVER_pointer_object(a) == __CPROVER_pointer_object(b)`
14+
__CPROVER_assert(
15+
__CPROVER_same_object(b, c), "expected fail as c can be null");
16+
__CPROVER_assert(
17+
__CPROVER_same_object(b, d), "expected success because d is &a");
18+
__CPROVER_assert(
19+
__CPROVER_same_object(b, e), "expected fail as e can be null");
20+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
CORE
2+
pointer_object.c
3+
--trace --verbosity 10
4+
\[main\.assertion\.1\] line \d+ expected fail as c can be null: FAILURE
5+
\[main\.assertion\.2\] line \d+ expected success because d is &a: SUCCESS
6+
\[main\.assertion\.3\] line \d+ expected fail as e can be null: FAILURE
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
--
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include <assert.h>
2+
#include <stdbool.h>
3+
4+
int main()
5+
{
6+
int x;
7+
int y;
8+
int z;
9+
bool nondet1;
10+
bool nondet2;
11+
int *a = nondet1 ? &x : &y;
12+
int *b = nondet2 ? &y : &z;
13+
__CPROVER_assert(!__CPROVER_same_object(a, b), "Can be violated.");
14+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
CORE
2+
pointer_object2.c
3+
--trace --verbosity 10
4+
\[main\.assertion\.1\] line 13 Can be violated.: FAILURE
5+
nondet1=FALSE
6+
nondet2=TRUE
7+
^VERIFICATION FAILED$
8+
^EXIT=10$
9+
^SIGNAL=0$
10+
--
11+
--
12+
Ensure that two variables which can get assigned the address of the
13+
same object satisfy the __CPROVER_same_object predicate. In the code
14+
under test, we negate the predicate to be able to get a failure and a
15+
trace which we can then match against expected values which guide
16+
through the path that leads to the two variables getting assigned the
17+
same object.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#define NULL (void *)0
2+
3+
int main()
4+
{
5+
int foo;
6+
7+
// The identifiers are allocated deterministically, so we want to check the
8+
// following properties hold:
9+
10+
// The pointer object of NULL is always going to be zero.
11+
__CPROVER_assert(
12+
__CPROVER_POINTER_OBJECT(NULL) != 0,
13+
"expected to fail with object ID == 0");
14+
// In the case where the program contains a single address of operation,
15+
// the pointer object is going to be 1.
16+
__CPROVER_assert(
17+
__CPROVER_POINTER_OBJECT(&foo) != 1,
18+
"expected to fail with object ID == 1");
19+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
CORE
2+
pointer_object3.c
3+
4+
\[main\.assertion\.1] line \d+ expected to fail with object ID == 0: FAILURE
5+
\[main\.assertion\.2] line \d+ expected to fail with object ID == 1: FAILURE
6+
^VERIFICATION FAILED$
7+
^EXIT=10$
8+
^SIGNAL=0$
9+
--
10+
--
11+
Test that the assignment of object IDs to objects is deterministic:
12+
* 0 for the NULL object, and
13+
* 1 for the single object which is the result of an address of operation
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
int main()
2+
{
3+
int a;
4+
int *p = &a;
5+
int *q = &a;
6+
7+
__CPROVER_assert(
8+
__CPROVER_POINTER_OFFSET(p) != __CPROVER_POINTER_OFFSET(q),
9+
"expected failure because offsets should be the same");
10+
11+
// TODO: Remove comments once pointer arithmetic works:
12+
13+
// *q = p + 2;
14+
15+
// __CPROVER_assert(__CPROVER_POINTER_OFFSET(p) != __CPROVER_POINTER_OFFSET(q), "expected failure");
16+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
CORE
2+
pointer_offset.c
3+
--trace
4+
\[main\.assertion\.1\] line \d+ expected failure because offsets should be the same: FAILURE
5+
^VERIFICATION FAILED$
6+
^EXIT=10$
7+
^SIGNAL=0$
8+
--
9+
--
10+
Test that the pointer offset bits of two pointers pointing to
11+
the same object are equal.
12+
13+
The test also contains a fragment of the test which doesn't work
14+
for now, but would be good to be added as soon as we get pointer
15+
arithmetic to work, so we can make sure that pointer offset fails
16+
appropriately.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
int main()
2+
{
3+
int a = 10;
4+
int *b = &a;
5+
int c;
6+
7+
*b = 12;
8+
9+
__CPROVER_assert(a != *b, "a should be different than b");
10+
__CPROVER_assert(a == *b, "a should not be different than b");
11+
__CPROVER_assert(
12+
*b != c,
13+
"c can get assigned a value that makes it the same what b points to");
14+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CORE
2+
pointers_simple.c
3+
--trace
4+
Passing problem to incremental SMT2 solving
5+
\[main\.assertion.\d\] line \d a should be different than b: FAILURE
6+
\[main\.assertion.\d\] line \d+ a should not be different than b: SUCCESS
7+
\[main\.assertion.\d\] line \d+ c can get assigned a value that makes it the same what b points to: FAILURE
8+
c=12
9+
^EXIT=10$
10+
^SIGNAL=0$
11+
--
12+
--

src/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1142,6 +1142,50 @@ static smt_termt convert_expr_to_smt(
11421142
mult_overflow.pretty());
11431143
}
11441144

1145+
static smt_termt convert_expr_to_smt(
1146+
const pointer_object_exprt &pointer_object,
1147+
const sub_expression_mapt &converted)
1148+
{
1149+
const auto type =
1150+
type_try_dynamic_cast<bitvector_typet>(pointer_object.type());
1151+
INVARIANT(type, "Pointer object should have a bitvector-based type.");
1152+
const auto converted_expr = converted.at(pointer_object.pointer());
1153+
const std::size_t width = type->get_width();
1154+
const std::size_t object_bits = config.bv_encoding.object_bits;
1155+
INVARIANT(
1156+
width >= object_bits,
1157+
"Width should be at least as big as the number of object bits.");
1158+
const std::size_t ext = width - object_bits;
1159+
const auto extract_op = smt_bit_vector_theoryt::extract(
1160+
width - 1, width - object_bits)(converted_expr);
1161+
if(ext > 0)
1162+
{
1163+
return smt_bit_vector_theoryt::zero_extend(ext)(extract_op);
1164+
}
1165+
return extract_op;
1166+
}
1167+
1168+
static smt_termt convert_expr_to_smt(
1169+
const pointer_offset_exprt &pointer_offset,
1170+
const sub_expression_mapt &converted)
1171+
{
1172+
const auto type =
1173+
type_try_dynamic_cast<bitvector_typet>(pointer_offset.type());
1174+
INVARIANT(type, "Pointer offset should have a bitvector-based type.");
1175+
const auto converted_expr = converted.at(pointer_offset.pointer());
1176+
const std::size_t width = type->get_width();
1177+
std::size_t offset_bits = width - config.bv_encoding.object_bits;
1178+
if(offset_bits > width)
1179+
offset_bits = width;
1180+
const auto extract_op =
1181+
smt_bit_vector_theoryt::extract(offset_bits - 1, 0)(converted_expr);
1182+
if(width > offset_bits)
1183+
{
1184+
return smt_bit_vector_theoryt::zero_extend(width - offset_bits)(extract_op);
1185+
}
1186+
return extract_op;
1187+
}
1188+
11451189
static smt_termt convert_expr_to_smt(
11461190
const shl_overflow_exprt &shl_overflow,
11471191
const sub_expression_mapt &converted)
@@ -1433,14 +1477,18 @@ static smt_termt dispatch_expr_to_smt_conversion(
14331477
{
14341478
return convert_expr_to_smt(*member_extraction, converted);
14351479
}
1436-
#if 0
1437-
else if(expr.id()==ID_pointer_offset)
1480+
else if(
1481+
const auto pointer_offset =
1482+
expr_try_dynamic_cast<pointer_offset_exprt>(expr))
14381483
{
1484+
return convert_expr_to_smt(*pointer_offset, converted);
14391485
}
1440-
else if(expr.id()==ID_pointer_object)
1486+
else if(
1487+
const auto pointer_object =
1488+
expr_try_dynamic_cast<pointer_object_exprt>(expr))
14411489
{
1490+
return convert_expr_to_smt(*pointer_object, converted);
14421491
}
1443-
#endif
14441492
if(
14451493
const auto is_dynamic_object =
14461494
expr_try_dynamic_cast<is_dynamic_object_exprt>(expr))

unit/solvers/smt2_incremental/convert_expr_to_smt.cpp

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <util/constructor_of.h>
99
#include <util/format.h>
1010
#include <util/namespace.h>
11+
#include <util/pointer_predicates.h>
1112
#include <util/std_expr.h>
1213
#include <util/symbol_table.h>
1314

@@ -1186,3 +1187,94 @@ TEST_CASE(
11861187
smt_bit_vector_constant_termt{0, 56})));
11871188
}
11881189
}
1190+
1191+
TEST_CASE(
1192+
"expr to smt conversion for pointer object expression",
1193+
"[core][smt2_incremental]")
1194+
{
1195+
// The config lines are necessary to ensure that pointer width in configured.
1196+
config.ansi_c.mode = configt::ansi_ct::flavourt::GCC;
1197+
config.ansi_c.set_arch_spec_x86_64();
1198+
config.bv_encoding.object_bits = 8;
1199+
1200+
const auto pointer_type = pointer_typet(unsigned_int_type(), 64 /* bits */);
1201+
const pointer_object_exprt foo{
1202+
symbol_exprt{"foo", pointer_type}, pointer_type};
1203+
const pointer_object_exprt foobar{
1204+
symbol_exprt{"foobar", pointer_type}, pointer_type};
1205+
1206+
SECTION("Pointer object expression")
1207+
{
1208+
const auto converted = convert_expr_to_smt(foo);
1209+
const auto expected =
1210+
smt_bit_vector_theoryt::zero_extend(56)(smt_bit_vector_theoryt::extract(
1211+
63, 56)(smt_identifier_termt("foo", smt_bit_vector_sortt(64))));
1212+
CHECK(converted == expected);
1213+
}
1214+
1215+
SECTION("Invariant checks")
1216+
{
1217+
const cbmc_invariants_should_throwt invariants_throw;
1218+
SECTION("Pointer object's operand type should be a bitvector type")
1219+
{
1220+
auto copy_of_foo = foo;
1221+
copy_of_foo.type() = bool_typet{};
1222+
REQUIRE_THROWS_MATCHES(
1223+
convert_expr_to_smt(copy_of_foo),
1224+
invariant_failedt,
1225+
invariant_failure_containing(
1226+
"Pointer object should have a bitvector-based type."));
1227+
}
1228+
}
1229+
1230+
SECTION("Comparison of pointer objects.")
1231+
{
1232+
const exprt comparison = notequal_exprt{foobar, foo};
1233+
INFO("Expression " + comparison.pretty(1, 0));
1234+
const auto converted = convert_expr_to_smt(comparison);
1235+
const auto bv1 =
1236+
smt_bit_vector_theoryt::zero_extend(56)(smt_bit_vector_theoryt::extract(
1237+
63, 56)(smt_identifier_termt("foo", smt_bit_vector_sortt(64))));
1238+
const auto bv2 =
1239+
smt_bit_vector_theoryt::zero_extend(56)(smt_bit_vector_theoryt::extract(
1240+
63, 56)(smt_identifier_termt("foobar", smt_bit_vector_sortt(64))));
1241+
const auto expected = smt_core_theoryt::distinct(bv2, bv1);
1242+
CHECK(converted == expected);
1243+
}
1244+
}
1245+
1246+
TEST_CASE("pointer_offset_exprt to SMT conversion", "[core][smt2_incremental]")
1247+
{
1248+
// The config lines are necessary to ensure that pointer width in configured.
1249+
config.ansi_c.mode = configt::ansi_ct::flavourt::GCC;
1250+
config.ansi_c.set_arch_spec_x86_64();
1251+
config.bv_encoding.object_bits = 8;
1252+
1253+
const auto pointer_type = pointer_typet(unsigned_int_type(), 64 /* bits */);
1254+
const pointer_offset_exprt pointer_offset{
1255+
symbol_exprt{"foo", pointer_type}, pointer_type};
1256+
1257+
SECTION("simple pointer_offset_exprt conversion")
1258+
{
1259+
const auto converted = convert_expr_to_smt(pointer_offset);
1260+
const auto expected =
1261+
smt_bit_vector_theoryt::zero_extend(8)(smt_bit_vector_theoryt::extract(
1262+
55, 0)(smt_identifier_termt("foo", smt_bit_vector_sortt(64))));
1263+
CHECK(converted == expected);
1264+
}
1265+
1266+
SECTION("Invariant checks")
1267+
{
1268+
const cbmc_invariants_should_throwt invariants_throw;
1269+
SECTION("pointer_offset_exprt's operand type should be a bitvector type")
1270+
{
1271+
auto pointer_offset_copy = pointer_offset;
1272+
pointer_offset_copy.type() = bool_typet{};
1273+
REQUIRE_THROWS_MATCHES(
1274+
convert_expr_to_smt(pointer_offset_copy),
1275+
invariant_failedt,
1276+
invariant_failure_containing(
1277+
"Pointer offset should have a bitvector-based type."));
1278+
}
1279+
}
1280+
}

0 commit comments

Comments
 (0)