@@ -4,6 +4,11 @@ use rustc::mir;
4
4
use crate :: * ;
5
5
6
6
pub trait EvalContextExt < ' tcx > {
7
+ fn pointer_inbounds (
8
+ & self ,
9
+ ptr : Pointer < Tag >
10
+ ) -> InterpResult < ' tcx > ;
11
+
7
12
fn ptr_op (
8
13
& self ,
9
14
bin_op : mir:: BinOp ,
@@ -34,6 +39,13 @@ pub trait EvalContextExt<'tcx> {
34
39
}
35
40
36
41
impl < ' mir , ' tcx > EvalContextExt < ' tcx > for super :: MiriEvalContext < ' mir , ' tcx > {
42
+ /// Test if the pointer is in-bounds of a live allocation.
43
+ #[ inline]
44
+ fn pointer_inbounds ( & self , ptr : Pointer < Tag > ) -> InterpResult < ' tcx > {
45
+ let ( size, _align) = self . memory ( ) . get_size_and_align ( ptr. alloc_id , AllocCheck :: Live ) ?;
46
+ ptr. check_in_alloc ( size, CheckInAllocMsg :: InboundsTest )
47
+ }
48
+
37
49
fn ptr_op (
38
50
& self ,
39
51
bin_op : mir:: BinOp ,
@@ -114,8 +126,8 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
114
126
let left = left. to_ptr ( ) . expect ( "we checked is_ptr" ) ;
115
127
let right = right. to_bits ( self . memory ( ) . pointer_size ( ) ) . expect ( "we checked is_bits" ) ;
116
128
let ( _alloc_size, alloc_align) = self . memory ( )
117
- . get_size_and_align ( left. alloc_id , InboundsCheck :: MaybeDead )
118
- . expect ( "determining size+align of dead ptr cannot fail" ) ;
129
+ . get_size_and_align ( left. alloc_id , AllocCheck :: MaybeDead )
130
+ . expect ( "alloc info with MaybeDead cannot fail" ) ;
119
131
let min_ptr_val = u128:: from ( alloc_align. bytes ( ) ) + u128:: from ( left. offset . bytes ( ) ) ;
120
132
let result = match bin_op {
121
133
Gt => min_ptr_val > right,
@@ -170,6 +182,7 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
170
182
if left. alloc_id == right. alloc_id {
171
183
left. offset == right. offset
172
184
} else {
185
+ // Make sure both pointers are in-bounds.
173
186
// This accepts one-past-the end. Thus, there is still technically
174
187
// some non-determinism that we do not fully rule out when two
175
188
// allocations sit right next to each other. The C/C++ standards are
@@ -179,10 +192,12 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
179
192
// Dead allocations in miri cannot overlap with live allocations, but
180
193
// on read hardware this can easily happen. Thus for comparisons we require
181
194
// both pointers to be live.
182
- self . memory ( ) . check_bounds_ptr ( left, InboundsCheck :: Live , CheckInAllocMsg :: InboundsTest ) ?;
183
- self . memory ( ) . check_bounds_ptr ( right, InboundsCheck :: Live , CheckInAllocMsg :: InboundsTest ) ?;
184
- // Two in-bounds pointers, we can compare across allocations.
185
- left == right
195
+ if self . pointer_inbounds ( left) . is_ok ( ) && self . pointer_inbounds ( right) . is_ok ( ) {
196
+ // Two in-bounds pointers in different allocations are different.
197
+ false
198
+ } else {
199
+ return err ! ( InvalidPointerMath ) ;
200
+ }
186
201
}
187
202
}
188
203
// Comparing ptr and integer.
@@ -202,16 +217,16 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
202
217
// alignment 32 or higher, hence the limit of 32.
203
218
// FIXME: Once we support intptrcast, we could try to fix these holes.
204
219
if bits < 32 {
205
- // Test if the ptr is in-bounds. Then it cannot be NULL .
206
- // Even dangling pointers cannot be NULL.
207
- if self . memory ( ) . check_bounds_ptr ( ptr, InboundsCheck :: MaybeDead , CheckInAllocMsg :: NullPointerTest ) . is_ok ( ) {
220
+ // Test if the pointer can be different from NULL or not .
221
+ // We assume that pointers that are not NULL are also not "small" .
222
+ if ! self . memory ( ) . ptr_may_be_null ( ptr) {
208
223
return Ok ( false ) ;
209
224
}
210
225
}
211
226
212
227
let ( alloc_size, alloc_align) = self . memory ( )
213
- . get_size_and_align ( ptr. alloc_id , InboundsCheck :: MaybeDead )
214
- . expect ( "determining size+align of dead ptr cannot fail" ) ;
228
+ . get_size_and_align ( ptr. alloc_id , AllocCheck :: MaybeDead )
229
+ . expect ( "alloc info with MaybeDead cannot fail" ) ;
215
230
216
231
// Case II: Alignment gives it away
217
232
if ptr. offset . bytes ( ) % alloc_align. bytes ( ) == 0 {
@@ -359,9 +374,9 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
359
374
if let Scalar :: Ptr ( ptr) = ptr {
360
375
// Both old and new pointer must be in-bounds of a *live* allocation.
361
376
// (Of the same allocation, but that part is trivial with our representation.)
362
- self . memory ( ) . check_bounds_ptr ( ptr, InboundsCheck :: Live , CheckInAllocMsg :: InboundsTest ) ?;
377
+ self . pointer_inbounds ( ptr) ?;
363
378
let ptr = ptr. signed_offset ( offset, self ) ?;
364
- self . memory ( ) . check_bounds_ptr ( ptr, InboundsCheck :: Live , CheckInAllocMsg :: InboundsTest ) ?;
379
+ self . pointer_inbounds ( ptr) ?;
365
380
Ok ( Scalar :: Ptr ( ptr) )
366
381
} else {
367
382
// An integer pointer. They can only be offset by 0, and we pretend there
0 commit comments