Skip to content

Commit b1428a9

Browse files
authored
Merge pull request #5838 from tautschnig/pointer-diff
Fix pointer subtraction semantics
2 parents 751c986 + 5b8028a commit b1428a9

File tree

6 files changed

+156
-44
lines changed

6 files changed

+156
-44
lines changed

regression/cbmc-library/posix_memalign-02/test.desc

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,5 @@ main.c
66
^VERIFICATION FAILED$
77
\[main.precondition_instance.1\] .* memcpy src/dst overlap: FAILURE
88
\[main.precondition_instance.3\] .* memcpy destination region writeable: FAILURE
9-
\*\* 2 of 14 failed
109
--
1110
^warning: ignoring
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
int main()
2+
{
3+
int array[4];
4+
int other_array[2];
5+
6+
__CPROVER_assert(&array[0] - &array[2] == -2, "correct");
7+
8+
int diff = array - other_array;
9+
_Bool nondet;
10+
if(nondet)
11+
__CPROVER_assert(diff != 42, "undefined behavior");
12+
else
13+
__CPROVER_assert(diff == 42, "undefined behavior");
14+
15+
__CPROVER_assert(
16+
((char *)(&array[3])) - ((char *)(&array[1])) == 2 * sizeof(int),
17+
"casting works");
18+
19+
int *p = &array[3];
20+
++p;
21+
__CPROVER_assert(p - &array[0] == 4, "end plus one works");
22+
__CPROVER_assert(p - &array[0] != 3, "end plus one works");
23+
++p;
24+
_Bool nondet_branch;
25+
if(nondet_branch)
26+
__CPROVER_assert(p - &array[0] == 5, "end plus 2 is nondet");
27+
else
28+
__CPROVER_assert(p - &array[0] != 5, "end plus 2 is nondet");
29+
30+
return 0;
31+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
CORE broken-smt-backend
2+
main.c
3+
--pointer-check
4+
^\[main.assertion.1\] line 6 correct: SUCCESS
5+
^\[main.pointer.1\] line 8 same object violation in array - other_array: FAILURE$
6+
^\[main.assertion.2\] line 11 undefined behavior: FAILURE$
7+
^\[main.assertion.3\] line 13 undefined behavior: FAILURE$
8+
^\[main.assertion.7\] line 26 end plus 2 is nondet: FAILURE$
9+
^\[main.pointer_arithmetic.\d+\] line 26 pointer relation: pointer outside object bounds in p: FAILURE$
10+
^\[main.assertion.8\] line 28 end plus 2 is nondet: FAILURE$
11+
^\[main.pointer_arithmetic.\d+\] line 28 pointer relation: pointer outside object bounds in p: FAILURE$
12+
^\*\* 7 of \d+ failed
13+
^VERIFICATION FAILED$
14+
^EXIT=10$
15+
^SIGNAL=0$
16+
--
17+
^warning: ignoring
18+
^CONVERSION ERROR$

regression/cbmc/void_pointer3/main.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
int main()
44
{
5-
// make sure they are NULL objects
6-
void *p=0, *q=0;
5+
int array[1 << (sizeof(unsigned char) * 8)];
6+
7+
void *p = array, *q = array;
78
// with the object:offset encoding we need to make sure the address arithmetic
89
// below will only touch the offset part
910
__CPROVER_assume(sizeof(unsigned char)<sizeof(void*));
1011
unsigned char o1, o2;
11-
// there is ample undefined behaviour here, but the goal of this test solely
12-
// is exercising CBMC's pointer subtraction encoding
12+
1313
p+=o1;
1414
q+=o2;
1515

src/analyses/goto_check.cpp

Lines changed: 35 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ class goto_checkt
178178
void mod_overflow_check(const mod_exprt &, const guardt &);
179179
void enum_range_check(const exprt &, const guardt &);
180180
void undefined_shift_check(const shift_exprt &, const guardt &);
181-
void pointer_rel_check(const binary_relation_exprt &, const guardt &);
181+
void pointer_rel_check(const binary_exprt &, const guardt &);
182182
void pointer_overflow_check(const exprt &, const guardt &);
183183

184184
/// Generates VCCs for the validity of the given dereferencing operation.
@@ -1117,7 +1117,7 @@ void goto_checkt::nan_check(
11171117
}
11181118

11191119
void goto_checkt::pointer_rel_check(
1120-
const binary_relation_exprt &expr,
1120+
const binary_exprt &expr,
11211121
const guardt &guard)
11221122
{
11231123
if(!enable_pointer_check)
@@ -1128,17 +1128,33 @@ void goto_checkt::pointer_rel_check(
11281128
{
11291129
// add same-object subgoal
11301130

1131-
if(enable_pointer_check)
1131+
exprt same_object = ::same_object(expr.op0(), expr.op1());
1132+
1133+
add_guarded_property(
1134+
same_object,
1135+
"same object violation",
1136+
"pointer",
1137+
expr.find_source_location(),
1138+
expr,
1139+
guard);
1140+
1141+
for(const auto &pointer : expr.operands())
11321142
{
1133-
exprt same_object=::same_object(expr.op0(), expr.op1());
1143+
// just this particular byte must be within object bounds or one past the
1144+
// end
1145+
const auto size = from_integer(0, size_type());
1146+
auto conditions = get_pointer_dereferenceable_conditions(pointer, size);
11341147

1135-
add_guarded_property(
1136-
same_object,
1137-
"same object violation",
1138-
"pointer",
1139-
expr.find_source_location(),
1140-
expr,
1141-
guard);
1148+
for(const auto &c : conditions)
1149+
{
1150+
add_guarded_property(
1151+
c.assertion,
1152+
"pointer relation: " + c.description,
1153+
"pointer arithmetic",
1154+
expr.find_source_location(),
1155+
pointer,
1156+
guard);
1157+
}
11421158
}
11431159
}
11441160
}
@@ -1650,6 +1666,14 @@ void goto_checkt::check_rec_arithmetic_op(const exprt &expr, guardt &guard)
16501666
if(expr.type().id() == ID_signedbv || expr.type().id() == ID_unsignedbv)
16511667
{
16521668
integer_overflow_check(expr, guard);
1669+
1670+
if(
1671+
expr.operands().size() == 2 && expr.id() == ID_minus &&
1672+
expr.operands()[0].type().id() == ID_pointer &&
1673+
expr.operands()[1].type().id() == ID_pointer)
1674+
{
1675+
pointer_rel_check(to_binary_expr(expr), guard);
1676+
}
16531677
}
16541678
else if(expr.type().id() == ID_floatbv)
16551679
{

src/solvers/flattening/bv_pointers.cpp

Lines changed: 68 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Author: Daniel Kroening, [email protected]
1515
#include <util/exception_utils.h>
1616
#include <util/pointer_expr.h>
1717
#include <util/pointer_offset_size.h>
18+
#include <util/pointer_predicates.h>
1819

1920
/// Map bytes according to the configured endianness. The key difference to
2021
/// endianness_mapt is that bv_endianness_mapt is aware of the bit-level
@@ -555,43 +556,82 @@ bvt bv_pointerst::convert_bitvector(const exprt &expr)
555556

556557
if(is_pointer_subtraction(expr))
557558
{
558-
// pointer minus pointer
559-
const auto &minus_expr = to_minus_expr(expr);
560-
bvt lhs = convert_bv(minus_expr.lhs());
561-
bvt rhs = convert_bv(minus_expr.rhs());
562-
563559
std::size_t width=boolbv_width(expr.type());
564560

565561
if(width==0)
566562
return conversion_failed(expr);
567563

568-
// we do a zero extension
569-
lhs = bv_utils.zero_extension(lhs, width);
570-
rhs = bv_utils.zero_extension(rhs, width);
564+
// pointer minus pointer is subtraction over the offset divided by element
565+
// size, iff the pointers point to the same object
566+
const auto &minus_expr = to_minus_expr(expr);
571567

572-
bvt bv = bv_utils.sub(lhs, rhs);
568+
// do the same-object-test via an expression as this may permit re-using
569+
// already cached encodings of the equality test
570+
const exprt same_object = ::same_object(minus_expr.lhs(), minus_expr.rhs());
571+
const bvt &same_object_bv = convert_bv(same_object);
572+
CHECK_RETURN(same_object_bv.size() == 1);
573573

574-
typet pointer_sub_type = minus_expr.lhs().type().subtype();
575-
mp_integer element_size;
574+
// compute the object size (again, possibly using cached results)
575+
const exprt object_size = ::object_size(minus_expr.lhs());
576+
const bvt object_size_bv =
577+
bv_utils.zero_extension(convert_bv(object_size), width);
576578

577-
if(pointer_sub_type.id()==ID_empty)
579+
bvt bv;
580+
bv.reserve(width);
581+
582+
for(std::size_t i = 0; i < width; ++i)
583+
bv.push_back(prop.new_variable());
584+
585+
if(!same_object_bv[0].is_false())
578586
{
579-
// This is a gcc extension.
587+
const pointer_typet &lhs_pt = to_pointer_type(minus_expr.lhs().type());
588+
const bvt &lhs = convert_bv(minus_expr.lhs());
589+
const bvt lhs_offset =
590+
bv_utils.sign_extension(offset_literals(lhs, lhs_pt), width);
591+
592+
const literalt lhs_in_bounds = prop.land(
593+
!bv_utils.sign_bit(lhs_offset),
594+
bv_utils.rel(
595+
lhs_offset,
596+
ID_le,
597+
object_size_bv,
598+
bv_utilst::representationt::UNSIGNED));
599+
600+
const pointer_typet &rhs_pt = to_pointer_type(minus_expr.rhs().type());
601+
const bvt &rhs = convert_bv(minus_expr.rhs());
602+
const bvt rhs_offset =
603+
bv_utils.sign_extension(offset_literals(rhs, rhs_pt), width);
604+
605+
const literalt rhs_in_bounds = prop.land(
606+
!bv_utils.sign_bit(rhs_offset),
607+
bv_utils.rel(
608+
rhs_offset,
609+
ID_le,
610+
object_size_bv,
611+
bv_utilst::representationt::UNSIGNED));
612+
613+
bvt difference = bv_utils.sub(lhs_offset, rhs_offset);
614+
615+
// Support for void* is a gcc extension, with the size treated as 1 byte
616+
// (no division required below).
580617
// https://gcc.gnu.org/onlinedocs/gcc-4.8.0/gcc/Pointer-Arith.html
581-
element_size = 1;
582-
}
583-
else
584-
{
585-
auto element_size_opt = pointer_offset_size(pointer_sub_type, ns);
586-
CHECK_RETURN(element_size_opt.has_value() && *element_size_opt > 0);
587-
element_size = *element_size_opt;
588-
}
618+
if(lhs_pt.subtype().id() != ID_empty)
619+
{
620+
auto element_size_opt = pointer_offset_size(lhs_pt.subtype(), ns);
621+
CHECK_RETURN(element_size_opt.has_value() && *element_size_opt > 0);
589622

590-
if(element_size != 1)
591-
{
592-
bvt element_size_bv = bv_utils.build_constant(element_size, bv.size());
593-
bv=bv_utils.divider(
594-
bv, element_size_bv, bv_utilst::representationt::SIGNED);
623+
if(*element_size_opt != 1)
624+
{
625+
bvt element_size_bv =
626+
bv_utils.build_constant(*element_size_opt, width);
627+
difference = bv_utils.divider(
628+
difference, element_size_bv, bv_utilst::representationt::SIGNED);
629+
}
630+
}
631+
632+
prop.l_set_to_true(prop.limplies(
633+
prop.land(same_object_bv[0], prop.land(lhs_in_bounds, rhs_in_bounds)),
634+
bv_utils.equal(difference, bv)));
595635
}
596636

597637
return bv;
@@ -794,11 +834,11 @@ bvt bv_pointerst::offset_arithmetic(
794834
else
795835
{
796836
bvt bv_factor=bv_utils.build_constant(factor, index.size());
797-
bv_index=bv_utils.unsigned_multiplier(index, bv_factor);
837+
bv_index = bv_utils.signed_multiplier(index, bv_factor);
798838
}
799839

800840
const std::size_t offset_bits = bv_pointers_width.get_offset_width(type);
801-
bv_index = bv_utils.zero_extension(bv_index, offset_bits);
841+
bv_index = bv_utils.sign_extension(bv_index, offset_bits);
802842

803843
bvt offset_bv = offset_literals(bv, type);
804844

0 commit comments

Comments
 (0)