Skip to content

Commit ebb0827

Browse files
committed
make pointer_offset_exprt unsigned
This commit changes the type of __CPROVER_POINTER_OFFSET from ssize_t to size_t. To match, relational operators on pointers are now unsigned, to enable p+PTRDIFF_MAX+1 > p. The rationale is subtle. The ISO C 11 standard 6.5.8 par 5 suggests that a pair of pointers can be compared if either the pointers point into the object, or one beyond the end of the sequence. The behavior in the case of a pointer that points before the sequence is undefined. There is a similar narrative for pointer arithmetic. A pointer with an offset that has a set most significant bit should thus compare "less than" a pointer with an offset that has a zero MSB. This suggests an unsigned encoding.
1 parent dcb99a2 commit ebb0827

File tree

12 files changed

+78
-52
lines changed

12 files changed

+78
-52
lines changed

regression/cbmc-library/string-abstraction/test-c-with-string-abstraction.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
CORE
22
test.c
33
--string-abstraction --show-goto-functions
4-
ASSIGN strlen#return_value := cast\(cast\(\*strlen::s::s#str\.length, signedbv\[.*\]\) - pointer_offset\(strlen::s\), unsignedbv\[.*\]\)
4+
ASSIGN strlen#return_value := \*strlen::s::s#str\.length - pointer_offset\(strlen::s\)
55
^EXIT=0$
66
^SIGNAL=0$
77
--

regression/cbmc-primitives/pointer-offset-01/test-no-cp.desc

+4-4
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ main.c
33
--no-simplify --no-propagation
44
^EXIT=10$
55
^SIGNAL=0$
6-
\[main.assertion.1\] line \d+ assertion __CPROVER_POINTER_OFFSET\(p\) >= 0: FAILURE
6+
\[main.assertion.1\] line \d+ assertion __CPROVER_POINTER_OFFSET\(p\) >= 0: SUCCESS
77
\[main.assertion.2\] line \d+ assertion __CPROVER_POINTER_OFFSET\(p\) < 0: FAILURE
88
--
99
^warning: ignoring
1010
--
11-
Check that both positive and negative offsets can be chosen for uninitialized
12-
pointers. We use --no-simplify and --no-propagation to ensure that the case is
13-
not solved by the constant propagation and thus tests the constraint encoding.
11+
Check that positive offsets can be chosen for uninitialized pointers. We
12+
use --no-simplify and --no-propagation to ensure that the case is not solved
13+
by the constant propagation and thus tests the constraint encoding.

regression/cbmc-primitives/pointer-offset-01/test.desc

+6-8
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,13 @@ main.c
33

44
^EXIT=10$
55
^SIGNAL=0$
6-
\[main.assertion.1\] line \d+ assertion __CPROVER_POINTER_OFFSET\(p\) >= 0: FAILURE
6+
\[main.assertion.1\] line \d+ assertion __CPROVER_POINTER_OFFSET\(p\) >= 0: SUCCESS
77
\[main.assertion.2\] line \d+ assertion __CPROVER_POINTER_OFFSET\(p\) < 0: FAILURE
88
--
99
^warning: ignoring
1010
--
11-
For uninitialised pointers, CBMC chooses a nondeterministic value (though no memory
12-
is allocated). Since the offset of pointers is signed, negative offsets should be
13-
able to be chosen (along with positive ones) non-deterministically.
14-
`__CPROVER_POINTER_OFFSET` is the CBMC primitive that gets the pointer offset
15-
from the base address of the object. This test guards this, and especially
16-
against the case where we could only observe some cases where offsets were only
17-
positive (in some CI configurations, for instance).
11+
For uninitialised pointers, CBMC chooses a nondeterministic value (though no
12+
memory is allocated). Since the offset of pointers is unsigned, negative
13+
offsets cannot be chosen. `__CPROVER_POINTER_OFFSET` is the CBMC primitive
14+
that gets the pointer offset from the base address of the object. This test
15+
guards this.

regression/cbmc/Pointer_Arithmetic19/test.desc

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
CORE new-smt-backend
22
main.c
33
--program-only
4-
ASSERT\(\{ 42, 43 \}\[POINTER_OFFSET\(p!0@1#2\) / \d+l*\] == 43\)$
4+
ASSERT\(\{ 42, 43 \}\[\(signed (long )?int\)POINTER_OFFSET\(p!0@1#2\) / \d+l*\] == 43\)$
55
^EXIT=0$
66
^SIGNAL=0$
77
--

regression/cbmc/memory_allocation2/test.desc

+3-3
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ main.c
33
--bounds-check
44
^EXIT=10$
55
^SIGNAL=0$
6-
^\[main\.array_bounds\.[1-5]\] .*: SUCCESS$
7-
^\[main\.array_bounds\.[67]\] line 38 array.buffer (dynamic object )?upper bound in buffers\[\(signed long (long )?int\)0\]->buffer\[\(signed long (long )?int\)100\]: FAILURE$
8-
^\*\* 1 of 6 failed
6+
^\[main\.array_bounds\.[1-2]\] .*: SUCCESS$
7+
^\[main\.array_bounds\.3\] line 38 array.buffer (dynamic object )?upper bound in buffers\[\(signed long (long )?int\)0\]->buffer\[\(signed long (long )?int\)100\]: FAILURE$
8+
^\*\* 1 of 3 failed
99
^VERIFICATION FAILED$
1010
--
1111
^warning: ignoring

regression/contracts/no_redudant_checks/test.desc

+3-2
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,8 @@ main.c
2323
^\[main.pointer_arithmetic.15\].*: SUCCESS
2424
^\[main.pointer_arithmetic.16\].*: SUCCESS
2525
^\[main.pointer_arithmetic.17\].*: SUCCESS
26-
^\*\* 0 of 20 failed \(1 iterations\)
26+
^\[main.pointer_arithmetic.18\].*: SUCCESS
27+
^\*\* 0 of 21 failed \(1 iterations\)
2728
^VERIFICATION SUCCESSFUL$
2829
--
2930
^\[main.overflow.\d+\].*: FAILURE
@@ -35,7 +36,7 @@ Checks that assertions generated by the first --pointer-overflow-check:
3536
We expect 20 assertions to be generated:
3637
In the goto-instrument run:
3738
- 2 for using malloc
38-
- 17 caused by --pointer-overflow-check
39+
- 18 caused by --pointer-overflow-check
3940

4041
In the final cbmc run:
4142
- 0 caused by --pointer-overflow-check

src/ansi-c/cprover_builtin_headers.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ void __CPROVER_loop_entry(const void *);
5656
__CPROVER_bool __CPROVER_LIVE_OBJECT(const void *);
5757
__CPROVER_bool __CPROVER_WRITEABLE_OBJECT(const void *);
5858
__CPROVER_size_t __CPROVER_POINTER_OBJECT(const void *);
59-
__CPROVER_ssize_t __CPROVER_POINTER_OFFSET(const void *);
59+
__CPROVER_size_t __CPROVER_POINTER_OFFSET(const void *);
6060
__CPROVER_size_t __CPROVER_OBJECT_SIZE(const void *);
6161
__CPROVER_bool __CPROVER_DYNAMIC_OBJECT(const void *);
6262
__CPROVER_bool __CPROVER_pointer_in_range(const void *, const void *, const void *);

src/ansi-c/goto_check_c.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1574,7 +1574,7 @@ void goto_check_ct::bounds_check_index(
15741574
effective_offset, p_offset.type())};
15751575
}
15761576

1577-
exprt zero = from_integer(0, ode.offset().type());
1577+
exprt zero = from_integer(0, effective_offset.type());
15781578

15791579
// the final offset must not be negative
15801580
binary_relation_exprt inequality(

src/pointer-analysis/value_set_dereference.cpp

+23-12
Original file line numberDiff line numberDiff line change
@@ -503,29 +503,30 @@ value_set_dereferencet::valuet value_set_dereferencet::build_reference_to(
503503

504504
const symbolt &memory_symbol=ns.lookup(CPROVER_PREFIX "memory");
505505
const symbol_exprt symbol_expr(memory_symbol.name, memory_symbol.type);
506+
const auto &memory_array_type = to_array_type(memory_symbol.type);
506507

507-
if(to_array_type(memory_symbol.type).element_type() == dereference_type)
508+
if(memory_array_type.element_type() == dereference_type)
508509
{
509510
// Types match already, what a coincidence!
510511
// We can use an index expression.
511512

512513
const index_exprt index_expr(
513514
symbol_expr,
514-
pointer_offset(pointer_expr),
515-
to_array_type(memory_symbol.type).element_type());
515+
typecast_exprt::conditional_cast(
516+
pointer_offset(pointer_expr), memory_array_type.index_type()),
517+
memory_array_type.element_type());
516518

517519
result.value=index_expr;
518520
result.pointer = address_of_exprt{index_expr};
519521
}
520522
else if(dereference_type_compare(
521-
to_array_type(memory_symbol.type).element_type(),
522-
dereference_type,
523-
ns))
523+
memory_array_type.element_type(), dereference_type, ns))
524524
{
525525
const index_exprt index_expr(
526526
symbol_expr,
527-
pointer_offset(pointer_expr),
528-
to_array_type(memory_symbol.type).element_type());
527+
typecast_exprt::conditional_cast(
528+
pointer_offset(pointer_expr), memory_array_type.index_type()),
529+
memory_array_type.element_type());
529530
result.value=typecast_exprt(index_expr, dereference_type);
530531
result.pointer =
531532
typecast_exprt{address_of_exprt{index_expr}, pointer_type};
@@ -585,6 +586,8 @@ value_set_dereferencet::valuet value_set_dereferencet::build_reference_to(
585586
pointer_offset_bits(to_array_type(root_object_type).element_type(), ns) ==
586587
pointer_offset_bits(dereference_type, ns))
587588
{
589+
const auto &array_type = to_array_type(root_object_type);
590+
588591
// We have an array with a subtype that matches
589592
// the dereferencing type.
590593
exprt offset;
@@ -593,11 +596,13 @@ value_set_dereferencet::valuet value_set_dereferencet::build_reference_to(
593596
if(o.offset().is_constant())
594597
offset=o.offset();
595598
else
596-
offset=pointer_offset(pointer_expr);
599+
{
600+
offset = typecast_exprt::conditional_cast(
601+
pointer_offset(pointer_expr), array_type.index_type());
602+
}
597603

598604
// are we doing a byte?
599-
auto element_size =
600-
pointer_offset_size(to_array_type(root_object_type).element_type(), ns);
605+
auto element_size = pointer_offset_size(array_type.element_type(), ns);
601606

602607
if(!element_size.has_value() || *element_size == 0)
603608
{
@@ -644,7 +649,9 @@ value_set_dereferencet::valuet value_set_dereferencet::build_reference_to(
644649
// this is relative to the root object
645650
exprt offset;
646651
if(o.offset().id()==ID_unknown)
652+
{
647653
offset=pointer_offset(pointer_expr);
654+
}
648655
else
649656
offset=o.offset();
650657

@@ -765,8 +772,12 @@ bool value_set_dereferencet::memory_model_bytes(
765772
is_a_bv_type(to_type))
766773
{
767774
// yes, can use 'index', but possibly need to convert
775+
const auto &from_array_type = to_array_type(from_type);
768776
result = typecast_exprt::conditional_cast(
769-
index_exprt(value, offset, to_array_type(from_type).element_type()),
777+
index_exprt(
778+
value,
779+
typecast_exprt::conditional_cast(offset, from_array_type.index_type()),
780+
from_array_type.element_type()),
770781
to_type);
771782
}
772783
else

src/solvers/flattening/bv_pointers.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -194,13 +194,14 @@ literalt bv_pointerst::convert_rest(const exprt &expr)
194194
if(same_object_lit.is_false())
195195
return same_object_lit;
196196

197+
// The comparison is UNSIGNED, to match the type of pointer_offsett
197198
return prop.land(
198199
same_object_lit,
199200
bv_utils.rel(
200201
offset_bv0,
201202
expr.id(),
202203
offset_bv1,
203-
bv_utilst::representationt::SIGNED));
204+
bv_utilst::representationt::UNSIGNED));
204205
}
205206
}
206207

src/util/pointer_predicates.cpp

+31-16
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ exprt object_size(const exprt &pointer)
3737

3838
exprt pointer_offset(const exprt &pointer)
3939
{
40-
return pointer_offset_exprt(pointer, signed_size_type());
40+
return pointer_offset_exprt(pointer, size_type());
4141
}
4242

4343
exprt deallocated(const exprt &pointer, const namespacet &ns)
@@ -107,30 +107,45 @@ exprt object_upper_bound(
107107
const exprt &pointer,
108108
const exprt &access_size)
109109
{
110-
// this is
111-
// POINTER_OFFSET(p)+access_size>OBJECT_SIZE(pointer)
110+
exprt object_offset = pointer_offset(pointer);
112111

113112
exprt object_size_expr=object_size(pointer);
114113

115-
exprt object_offset=pointer_offset(pointer);
116-
117-
// need to add size
118-
irep_idt op=ID_ge;
119-
exprt sum=object_offset;
114+
std::size_t max_width = std::max(
115+
to_bitvector_type(object_offset.type()).get_width(),
116+
to_bitvector_type(object_size_expr.type()).get_width());
120117

118+
// We add the size to the offset, if given.
121119
if(access_size.is_not_nil())
122120
{
123-
op=ID_gt;
121+
// This is
122+
// POINTER_OFFSET(p)+access_size>OBJECT_SIZE(pointer)
123+
// We enlarge all bit-vectors to avoid an overflow on the addition.
124+
125+
max_width =
126+
std::max(max_width, to_bitvector_type(access_size.type()).get_width());
127+
128+
auto type = unsignedbv_typet(max_width + 1);
129+
130+
auto sum = plus_exprt(
131+
typecast_exprt::conditional_cast(object_offset, type),
132+
typecast_exprt::conditional_cast(access_size, type));
124133

125-
sum = plus_exprt(
126-
typecast_exprt::conditional_cast(object_offset, access_size.type()),
127-
access_size);
134+
return binary_relation_exprt(
135+
sum, ID_gt, typecast_exprt::conditional_cast(object_size_expr, type));
128136
}
137+
else
138+
{
139+
// This is
140+
// POINTER_OFFSET(p)>=OBJECT_SIZE(pointer)
141+
142+
auto type = unsignedbv_typet(max_width);
129143

130-
return binary_relation_exprt(
131-
typecast_exprt::conditional_cast(sum, object_size_expr.type()),
132-
op,
133-
object_size_expr);
144+
return binary_relation_exprt(
145+
typecast_exprt::conditional_cast(object_offset, type),
146+
ID_ge,
147+
typecast_exprt::conditional_cast(object_size_expr, type));
148+
}
134149
}
135150

136151
exprt object_lower_bound(

unit/util/pointer_expr.cpp

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,14 @@
1010
TEST_CASE("pointer_offset_exprt", "[core][util]")
1111
{
1212
const exprt pointer = symbol_exprt{"foo", pointer_type(void_type())};
13-
const pointer_offset_exprt pointer_offset{pointer, signed_size_type()};
13+
const pointer_offset_exprt pointer_offset{pointer, size_type()};
1414
SECTION("Is equivalent to free function.")
1515
{
1616
CHECK(::pointer_offset(pointer) == pointer_offset);
1717
}
1818
SECTION("Result type")
1919
{
20-
CHECK(pointer_offset.type() == signed_size_type());
20+
CHECK(pointer_offset.type() == size_type());
2121
}
2222
SECTION("Pointer operand accessor")
2323
{

0 commit comments

Comments
 (0)