@@ -4,6 +4,11 @@ use rustc::mir;
44use crate :: * ;
55
66pub trait EvalContextExt < ' tcx > {
7+ fn pointer_inbounds (
8+ & self ,
9+ ptr : Pointer < Tag >
10+ ) -> InterpResult < ' tcx > ;
11+
712 fn ptr_op (
813 & self ,
914 bin_op : mir:: BinOp ,
@@ -34,6 +39,13 @@ pub trait EvalContextExt<'tcx> {
3439}
3540
3641impl < ' 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+
3749 fn ptr_op (
3850 & self ,
3951 bin_op : mir:: BinOp ,
@@ -114,8 +126,8 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
114126 let left = left. to_ptr ( ) . expect ( "we checked is_ptr" ) ;
115127 let right = right. to_bits ( self . memory ( ) . pointer_size ( ) ) . expect ( "we checked is_bits" ) ;
116128 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" ) ;
119131 let min_ptr_val = u128:: from ( alloc_align. bytes ( ) ) + u128:: from ( left. offset . bytes ( ) ) ;
120132 let result = match bin_op {
121133 Gt => min_ptr_val > right,
@@ -170,6 +182,7 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
170182 if left. alloc_id == right. alloc_id {
171183 left. offset == right. offset
172184 } else {
185+ // Make sure both pointers are in-bounds.
173186 // This accepts one-past-the end. Thus, there is still technically
174187 // some non-determinism that we do not fully rule out when two
175188 // 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> {
179192 // Dead allocations in miri cannot overlap with live allocations, but
180193 // on read hardware this can easily happen. Thus for comparisons we require
181194 // 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+ }
186201 }
187202 }
188203 // Comparing ptr and integer.
@@ -202,16 +217,16 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
202217 // alignment 32 or higher, hence the limit of 32.
203218 // FIXME: Once we support intptrcast, we could try to fix these holes.
204219 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) {
208223 return Ok ( false ) ;
209224 }
210225 }
211226
212227 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" ) ;
215230
216231 // Case II: Alignment gives it away
217232 if ptr. offset . bytes ( ) % alloc_align. bytes ( ) == 0 {
@@ -359,9 +374,9 @@ impl<'mir, 'tcx> EvalContextExt<'tcx> for super::MiriEvalContext<'mir, 'tcx> {
359374 if let Scalar :: Ptr ( ptr) = ptr {
360375 // Both old and new pointer must be in-bounds of a *live* allocation.
361376 // (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) ?;
363378 let ptr = ptr. signed_offset ( offset, self ) ?;
364- self . memory ( ) . check_bounds_ptr ( ptr, InboundsCheck :: Live , CheckInAllocMsg :: InboundsTest ) ?;
379+ self . pointer_inbounds ( ptr) ?;
365380 Ok ( Scalar :: Ptr ( ptr) )
366381 } else {
367382 // An integer pointer. They can only be offset by 0, and we pretend there
0 commit comments