Skip to content

Commit 5b8028a

Browse files
committed
Fix pointer subtraction semantics
Subtracting pointers over the same object is subtraction of the offset (divided by the element size). Subtracting pointers over different objects should yield a non-deterministic result.
1 parent cc2346c commit 5b8028a

File tree

4 files changed

+105
-35
lines changed

4 files changed

+105
-35
lines changed
+23-1
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,31 @@
11
int main()
22
{
3-
int array[2];
3+
int array[4];
44
int other_array[2];
55

6+
__CPROVER_assert(&array[0] - &array[2] == -2, "correct");
7+
68
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");
729

830
return 0;
931
}

regression/cbmc/Pointer_difference2/test.desc

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,15 @@
1-
CORE
1+
CORE broken-smt-backend
22
main.c
33
--pointer-check
4-
^\[main.pointer.1\] line 6 same object violation in array - other_array: FAILURE$
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
513
^VERIFICATION FAILED$
614
^EXIT=10$
715
^SIGNAL=0$

regression/cbmc/void_pointer3/main.c

+4-4
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/solvers/flattening/bv_pointers.cpp

+68-28
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)