Skip to content

Commit b1f1e81

Browse files
authored
Merge pull request #6143 from tautschnig/pointer-comparison
Binary comparison <, <=, >, >= over pointers requires same-object
2 parents 62dc70a + 7b4084c commit b1f1e81

File tree

6 files changed

+92
-6
lines changed

6 files changed

+92
-6
lines changed

regression/cbmc/Pointer29/main.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
int main()
44
{
55
unsigned int *s_pdt = (unsigned int *)(0xdeadbeef);
6-
assert(s_pdt > 1);
6+
// this is well defined
7+
assert((unsigned long long)s_pdt > 1);
8+
// this is undefined as it could both be a comparison over integers as well as
9+
// over pointers; the latter may mean comparing pointers to different objects,
10+
// so guard against that
11+
assert(
12+
__CPROVER_POINTER_OBJECT(s_pdt) !=
13+
__CPROVER_POINTER_OBJECT((unsigned int *)1) ||
14+
s_pdt > 1);
715
return 0;
816
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,29 @@
11
#include <assert.h>
22
#include <stdlib.h>
33

4+
char *nondet_pointer();
5+
46
int main()
57
{
68
int *p1 = malloc(sizeof(int));
79

810
assert(p1 < p1 + 1);
11+
12+
int *p2 = malloc(sizeof(int));
13+
14+
assert(p1 != p2);
15+
16+
_Bool nondet;
17+
// In the current implementation, CBMC always produces "false" for a
18+
// comparison over different objects. This could change at any time, which
19+
// would require updating this test.
20+
if(nondet)
21+
__CPROVER_assert(p1 < p2, "always false for different objects");
22+
else
23+
__CPROVER_assert(p1 > p2, "always false for different objects");
24+
25+
char *p = nondet_pointer();
26+
27+
__CPROVER_assume(p < p1 + 1);
28+
assert(__CPROVER_POINTER_OBJECT(p) == __CPROVER_POINTER_OBJECT(p1));
929
}
Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
CORE
22
main.c
33

4-
^VERIFICATION SUCCESSFUL$
5-
^EXIT=0$
4+
^\[main.assertion.3\] line 21 always false for different objects: FAILURE$
5+
^\[main.assertion.4\] line 23 always false for different objects: FAILURE$
6+
^\*\* 2 of 7 failed
7+
^VERIFICATION FAILED$
8+
^EXIT=10$
69
^SIGNAL=0$
710
--
811
^warning: ignoring

src/solvers/flattening/bv_pointers.cpp

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,8 +172,30 @@ literalt bv_pointerst::convert_rest(const exprt &expr)
172172
const bvt &bv0=convert_bv(operands[0]);
173173
const bvt &bv1=convert_bv(operands[1]);
174174

175-
return bv_utils.rel(
176-
bv0, expr.id(), bv1, bv_utilst::representationt::UNSIGNED);
175+
const pointer_typet &type0 = to_pointer_type(operands[0].type());
176+
bvt offset_bv0 = offset_literals(bv0, type0);
177+
178+
const pointer_typet &type1 = to_pointer_type(operands[1].type());
179+
bvt offset_bv1 = offset_literals(bv1, type1);
180+
181+
// Comparison over pointers to distinct objects is undefined behavior in
182+
// C; we choose to always produce "false" in such a case. Alternatively,
183+
// we could do a comparison over the integer representation of a pointer
184+
185+
// do the same-object-test via an expression as this may permit re-using
186+
// already cached encodings of the equality test
187+
const exprt same_object = ::same_object(operands[0], operands[1]);
188+
const literalt same_object_lit = convert(same_object);
189+
if(same_object_lit.is_false())
190+
return same_object_lit;
191+
192+
return prop.land(
193+
same_object_lit,
194+
bv_utils.rel(
195+
offset_bv0,
196+
expr.id(),
197+
offset_bv1,
198+
bv_utilst::representationt::SIGNED));
177199
}
178200
}
179201

src/solvers/smt2/smt2_conv.cpp

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Author: Daniel Kroening, [email protected]
2727
#include <util/namespace.h>
2828
#include <util/pointer_expr.h>
2929
#include <util/pointer_offset_size.h>
30+
#include <util/pointer_predicates.h>
3031
#include <util/prefix.h>
3132
#include <util/range.h>
3233
#include <util/simplify_expr.h>
@@ -3165,7 +3166,6 @@ void smt2_convt::convert_relation(const binary_relation_exprt &expr)
31653166
const typet &op_type=expr.op0().type();
31663167

31673168
if(op_type.id()==ID_unsignedbv ||
3168-
op_type.id()==ID_pointer ||
31693169
op_type.id()==ID_bv)
31703170
{
31713171
out << "(";
@@ -3238,6 +3238,31 @@ void smt2_convt::convert_relation(const binary_relation_exprt &expr)
32383238
convert_expr(expr.op1());
32393239
out << ")";
32403240
}
3241+
else if(op_type.id() == ID_pointer)
3242+
{
3243+
const exprt same_object = ::same_object(expr.op0(), expr.op1());
3244+
3245+
out << "(and ";
3246+
convert_expr(same_object);
3247+
3248+
out << " (";
3249+
if(expr.id() == ID_le)
3250+
out << "bvsle";
3251+
else if(expr.id() == ID_lt)
3252+
out << "bvslt";
3253+
else if(expr.id() == ID_ge)
3254+
out << "bvsge";
3255+
else if(expr.id() == ID_gt)
3256+
out << "bvsgt";
3257+
3258+
out << ' ';
3259+
convert_expr(pointer_offset(expr.op0()));
3260+
out << ' ';
3261+
convert_expr(pointer_offset(expr.op1()));
3262+
out << ')';
3263+
3264+
out << ')';
3265+
}
32413266
else
32423267
UNEXPECTEDCASE(
32433268
"unsupported type for "+expr.id_string()+": "+op_type.id_string());

src/util/simplify_expr_int.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1473,6 +1473,14 @@ simplify_exprt::resultt<> simplify_exprt::simplify_inequality_no_constant(
14731473
if(expr.op0().type().id() == ID_floatbv)
14741474
return unchanged(expr);
14751475

1476+
// simplifications below require same-object, which we don't check for
1477+
if(
1478+
expr.op0().type().id() == ID_pointer && expr.id() != ID_equal &&
1479+
expr.id() != ID_notequal)
1480+
{
1481+
return unchanged(expr);
1482+
}
1483+
14761484
// eliminate strict inequalities
14771485
if(expr.id()==ID_notequal)
14781486
{

0 commit comments

Comments
 (0)