Skip to content

Commit 6aa4277

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 7bc409b commit 6aa4277

File tree

12 files changed

+66
-43
lines changed

12 files changed

+66
-43
lines changed

regression/cbmc-library/strlen-02/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/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
@@ -59,7 +59,7 @@ void __CPROVER_loop_entry(const void *);
5959
__CPROVER_bool __CPROVER_LIVE_OBJECT(const void *);
6060
__CPROVER_bool __CPROVER_WRITEABLE_OBJECT(const void *);
6161
__CPROVER_size_t __CPROVER_POINTER_OBJECT(const void *);
62-
__CPROVER_ssize_t __CPROVER_POINTER_OFFSET(const void *);
62+
__CPROVER_size_t __CPROVER_POINTER_OFFSET(const void *);
6363
__CPROVER_size_t __CPROVER_OBJECT_SIZE(const void *);
6464
__CPROVER_bool __CPROVER_DYNAMIC_OBJECT(const void *);
6565
__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
@@ -1571,7 +1571,7 @@ void goto_check_ct::bounds_check_index(
15711571
effective_offset, p_offset.type())};
15721572
}
15731573

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

15761576
// the final offset must not be negative
15771577
binary_relation_exprt inequality(

src/ansi-c/library/cprover_contracts.c

+1-1
Original file line numberDiff line numberDiff line change
@@ -786,7 +786,7 @@ __CPROVER_HIDE:;
786786
size < __CPROVER_max_malloc_size,
787787
"CAR size is less than __CPROVER_max_malloc_size");
788788

789-
__CPROVER_ssize_t offset = __CPROVER_POINTER_OFFSET(ptr);
789+
__CPROVER_size_t offset = __CPROVER_POINTER_OFFSET(ptr);
790790

791791
__CPROVER_assert(
792792
!(offset > 0) |

src/pointer-analysis/value_set_dereference.cpp

+11-3
Original file line numberDiff line numberDiff line change
@@ -517,7 +517,9 @@ value_set_dereferencet::valuet value_set_dereferencet::build_reference_to(
517517

518518
const index_exprt index_expr(
519519
symbol_expr,
520-
pointer_offset(pointer_expr),
520+
typecast_exprt::conditional_cast(
521+
pointer_offset(pointer_expr),
522+
to_array_type(memory_symbol.type).index_type()),
521523
to_array_type(memory_symbol.type).element_type());
522524

523525
valuet result;
@@ -532,7 +534,9 @@ value_set_dereferencet::valuet value_set_dereferencet::build_reference_to(
532534
{
533535
const index_exprt index_expr(
534536
symbol_expr,
535-
pointer_offset(pointer_expr),
537+
typecast_exprt::conditional_cast(
538+
pointer_offset(pointer_expr),
539+
to_array_type(memory_symbol.type).index_type()),
536540
to_array_type(memory_symbol.type).element_type());
537541

538542
valuet result;
@@ -768,7 +772,11 @@ bool value_set_dereferencet::memory_model_bytes(
768772
{
769773
// yes, can use 'index', but possibly need to convert
770774
result = typecast_exprt::conditional_cast(
771-
index_exprt(value, offset, to_array_type(from_type).element_type()),
775+
index_exprt(
776+
value,
777+
typecast_exprt::conditional_cast(
778+
offset, to_array_type(from_type).index_type()),
779+
to_array_type(from_type).element_type()),
772780
to_type);
773781
}
774782
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)
@@ -73,30 +73,45 @@ exprt object_upper_bound(
7373
const exprt &pointer,
7474
const exprt &access_size)
7575
{
76-
// this is
77-
// POINTER_OFFSET(p)+access_size>OBJECT_SIZE(pointer)
76+
exprt object_offset = pointer_offset(pointer);
7877

7978
exprt object_size_expr=object_size(pointer);
8079

81-
exprt object_offset=pointer_offset(pointer);
82-
83-
// need to add size
84-
irep_idt op=ID_ge;
85-
exprt sum=object_offset;
80+
std::size_t max_width = std::max(
81+
to_bitvector_type(object_offset.type()).get_width(),
82+
to_bitvector_type(object_size_expr.type()).get_width());
8683

84+
// We add the size to the offset, if given.
8785
if(access_size.is_not_nil())
8886
{
89-
op=ID_gt;
87+
// This is
88+
// POINTER_OFFSET(p)+access_size>OBJECT_SIZE(pointer)
89+
// We enlarge all bit-vectors to avoid an overflow on the addition.
90+
91+
max_width =
92+
std::max(max_width, to_bitvector_type(access_size.type()).get_width());
93+
94+
auto type = unsignedbv_typet(max_width + 1);
95+
96+
auto sum = plus_exprt(
97+
typecast_exprt::conditional_cast(object_offset, type),
98+
typecast_exprt::conditional_cast(access_size, type));
9099

91-
sum = plus_exprt(
92-
typecast_exprt::conditional_cast(object_offset, access_size.type()),
93-
access_size);
100+
return binary_relation_exprt(
101+
sum, ID_gt, typecast_exprt::conditional_cast(object_size_expr, type));
94102
}
103+
else
104+
{
105+
// This is
106+
// POINTER_OFFSET(p)>=OBJECT_SIZE(pointer)
107+
108+
auto type = unsignedbv_typet(max_width);
95109

96-
return binary_relation_exprt(
97-
typecast_exprt::conditional_cast(sum, object_size_expr.type()),
98-
op,
99-
object_size_expr);
110+
return binary_relation_exprt(
111+
typecast_exprt::conditional_cast(object_offset, type),
112+
ID_ge,
113+
typecast_exprt::conditional_cast(object_size_expr, type));
114+
}
100115
}
101116

102117
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)