@@ -19,7 +19,7 @@ use syntax::ast::Mutability;
19
19
use super :: {
20
20
Pointer , AllocId , Allocation , GlobalId , AllocationExtra ,
21
21
InterpResult , Scalar , InterpError , GlobalAlloc , PointerArithmetic ,
22
- Machine , AllocMap , MayLeak , ErrorHandled , CheckInAllocMsg , InboundsCheck ,
22
+ Machine , AllocMap , MayLeak , ErrorHandled , CheckInAllocMsg ,
23
23
InterpError :: ValidationFailure ,
24
24
} ;
25
25
@@ -44,6 +44,17 @@ impl<T: MayLeak> MayLeak for MemoryKind<T> {
44
44
}
45
45
}
46
46
47
+ /// Used by `get_size_and_align` to indicate whether the allocation needs to be live.
48
+ #[ derive( Debug , Copy , Clone ) ]
49
+ pub enum AllocCheck {
50
+ /// Allocation must be live and not a function pointer.
51
+ Dereferencable ,
52
+ /// Allocations needs to be live, but may be a function pointer.
53
+ Live ,
54
+ /// Allocation may be dead.
55
+ MaybeDead ,
56
+ }
57
+
47
58
// `Memory` has to depend on the `Machine` because some of its operations
48
59
// (e.g., `get`) call a `Machine` hook.
49
60
pub struct Memory < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > {
@@ -248,66 +259,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
248
259
Ok ( ( ) )
249
260
}
250
261
251
- /// Checks that the pointer is aligned AND non-NULL. This supports ZSTs in two ways:
252
- /// You can pass a scalar, and a `Pointer` does not have to actually still be allocated.
253
- fn check_align (
254
- & self ,
255
- ptr : Scalar < M :: PointerTag > ,
256
- required_align : Align
257
- ) -> InterpResult < ' tcx > {
258
- // Check non-NULL/Undef, extract offset
259
- let ( offset, alloc_align) = match ptr. to_bits_or_ptr ( self . pointer_size ( ) , self ) {
260
- Err ( ptr) => {
261
- // check this is not NULL -- which we can ensure only if this is in-bounds
262
- // of some (potentially dead) allocation.
263
- let align = self . check_ptr_bounds ( ptr, InboundsCheck :: MaybeDead ,
264
- CheckInAllocMsg :: NullPointerTest ) ?;
265
- ( ptr. offset . bytes ( ) , align)
266
- }
267
- Ok ( data) => {
268
- // check this is not NULL
269
- if data == 0 {
270
- return err ! ( InvalidNullPointerUsage ) ;
271
- }
272
- // the "base address" is 0 and hence always aligned
273
- ( data as u64 , required_align)
274
- }
275
- } ;
276
- // Check alignment
277
- if alloc_align. bytes ( ) < required_align. bytes ( ) {
278
- return err ! ( AlignmentCheckFailed {
279
- has: alloc_align,
280
- required: required_align,
281
- } ) ;
282
- }
283
- if offset % required_align. bytes ( ) == 0 {
284
- Ok ( ( ) )
285
- } else {
286
- let has = offset % required_align. bytes ( ) ;
287
- err ! ( AlignmentCheckFailed {
288
- has: Align :: from_bytes( has) . unwrap( ) ,
289
- required: required_align,
290
- } )
291
- }
292
- }
293
-
294
- /// Checks if the pointer is "in-bounds" of *some* (live or dead) allocation. Notice that
295
- /// a pointer pointing at the end of an allocation (i.e., at the first *inaccessible* location)
296
- /// *is* considered in-bounds! This follows C's/LLVM's rules.
297
- /// `liveness` can be used to rule out dead allocations. Testing in-bounds with a dead
298
- /// allocation is useful e.g. to exclude the possibility of this pointer being NULL.
299
- /// If you want to check bounds before doing a memory access, call `check_ptr_access`.
300
- fn check_ptr_bounds (
301
- & self ,
302
- ptr : Pointer < M :: PointerTag > ,
303
- liveness : InboundsCheck ,
304
- msg : CheckInAllocMsg ,
305
- ) -> InterpResult < ' tcx , Align > {
306
- let ( allocation_size, align) = self . get_size_and_align ( ptr. alloc_id , liveness) ?;
307
- ptr. check_in_alloc ( allocation_size, msg) ?;
308
- Ok ( align)
309
- }
310
-
311
262
/// Check if the given scalar is allowed to do a memory access of given `size`
312
263
/// and `align`. On success, returns `None` for zero-sized accesses (where
313
264
/// nothing else is left to do) and a `Pointer` to use for the actual access otherwise.
@@ -350,18 +301,35 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
350
301
None
351
302
}
352
303
Err ( ptr) => {
353
- // Test bounds.
354
- self . check_ptr_bounds (
355
- ptr. offset ( size, self ) ?,
356
- InboundsCheck :: Live ,
357
- CheckInAllocMsg :: MemoryAccessTest ,
358
- ) ?;
359
- // Test align and non-NULL.
360
- self . check_align ( ptr. into ( ) , align) ?;
361
- // FIXME: Alignment check is too strict, depending on the base address that
362
- // got picked we might be aligned even if this check fails.
363
- // We instead have to fall back to converting to an integer and checking
364
- // the "real" alignment.
304
+ let ( allocation_size, alloc_align) =
305
+ self . get_size_and_align ( ptr. alloc_id , AllocCheck :: Dereferencable ) ?;
306
+ // Test bounds. This also ensures non-NULL.
307
+ // It is sufficient to check this for the end pointer. The addition
308
+ // checks for overflow.
309
+ let end_ptr = ptr. offset ( size, self ) ?;
310
+ end_ptr. check_in_alloc ( allocation_size, CheckInAllocMsg :: MemoryAccessTest ) ?;
311
+ // Test align. Check this last; if both bounds and alignment are violated
312
+ // we want the error to be about the bounds.
313
+ if alloc_align. bytes ( ) < align. bytes ( ) {
314
+ // The allocation itself is not aligned enough.
315
+ // FIXME: Alignment check is too strict, depending on the base address that
316
+ // got picked we might be aligned even if this check fails.
317
+ // We instead have to fall back to converting to an integer and checking
318
+ // the "real" alignment.
319
+ return err ! ( AlignmentCheckFailed {
320
+ has: alloc_align,
321
+ required: align,
322
+ } ) ;
323
+ }
324
+ let offset = ptr. offset . bytes ( ) ;
325
+ if offset % align. bytes ( ) != 0 {
326
+ // The offset os not aligned enough.
327
+ let has = offset % align. bytes ( ) ;
328
+ return err ! ( AlignmentCheckFailed {
329
+ has: Align :: from_bytes( has) . unwrap( ) ,
330
+ required: align,
331
+ } )
332
+ }
365
333
366
334
// We can still be zero-sized in this branch, in which case we have to
367
335
// return `None`.
@@ -375,8 +343,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
375
343
& self ,
376
344
ptr : Pointer < M :: PointerTag > ,
377
345
) -> bool {
378
- self . check_ptr_bounds ( ptr, InboundsCheck :: MaybeDead , CheckInAllocMsg :: NullPointerTest )
379
- . is_err ( )
346
+ let ( size, _align) = self . get_size_and_align ( ptr. alloc_id , AllocCheck :: MaybeDead )
347
+ . expect ( "alloc info with MaybeDead cannot fail" ) ;
348
+ ptr. check_in_alloc ( size, CheckInAllocMsg :: NullPointerTest ) . is_err ( )
380
349
}
381
350
}
382
351
@@ -515,13 +484,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
515
484
}
516
485
}
517
486
518
- /// Obtain the size and alignment of an allocation, even if that allocation has been deallocated
487
+ /// Obtain the size and alignment of an allocation, even if that allocation has
488
+ /// been deallocated.
519
489
///
520
- /// If `liveness` is `InboundsCheck ::MaybeDead`, this function always returns `Ok`
490
+ /// If `liveness` is `AllocCheck ::MaybeDead`, this function always returns `Ok`.
521
491
pub fn get_size_and_align (
522
492
& self ,
523
493
id : AllocId ,
524
- liveness : InboundsCheck ,
494
+ liveness : AllocCheck ,
525
495
) -> InterpResult < ' static , ( Size , Align ) > {
526
496
if let Ok ( alloc) = self . get ( id) {
527
497
return Ok ( ( Size :: from_bytes ( alloc. bytes . len ( ) as u64 ) , alloc. align ) ) ;
@@ -531,7 +501,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
531
501
let alloc = self . tcx . alloc_map . lock ( ) . get ( id) ;
532
502
// Could also be a fn ptr or extern static
533
503
match alloc {
534
- Some ( GlobalAlloc :: Function ( ..) ) => Ok ( ( Size :: ZERO , Align :: from_bytes ( 1 ) . unwrap ( ) ) ) ,
504
+ Some ( GlobalAlloc :: Function ( ..) ) => {
505
+ if let AllocCheck :: Dereferencable = liveness {
506
+ // The caller requested no function pointers.
507
+ err ! ( DerefFunctionPointer )
508
+ } else {
509
+ Ok ( ( Size :: ZERO , Align :: from_bytes ( 1 ) . unwrap ( ) ) )
510
+ }
511
+ }
535
512
// `self.get` would also work, but can cause cycles if a static refers to itself
536
513
Some ( GlobalAlloc :: Static ( did) ) => {
537
514
// The only way `get` couldn't have worked here is if this is an extern static
@@ -545,15 +522,15 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
545
522
if let Ok ( alloc) = self . get ( id) {
546
523
return Ok ( ( Size :: from_bytes ( alloc. bytes . len ( ) as u64 ) , alloc. align ) ) ;
547
524
}
548
- match liveness {
549
- InboundsCheck :: MaybeDead => {
550
- // Must be a deallocated pointer
551
- self . dead_alloc_map . get ( & id) . cloned ( ) . ok_or_else ( ||
552
- ValidationFailure ( "allocation missing in dead_alloc_map" . to_string ( ) )
553
- . into ( )
554
- )
555
- } ,
556
- InboundsCheck :: Live => err ! ( DanglingPointerDeref ) ,
525
+ if let AllocCheck :: MaybeDead = liveness {
526
+ // Deallocated pointers are allowed, we should be able to find
527
+ // them in the map.
528
+ self . dead_alloc_map . get ( & id) . copied ( ) . ok_or_else ( ||
529
+ ValidationFailure ( "allocation missing in dead_alloc_map" . to_string ( ) )
530
+ . into ( )
531
+ )
532
+ } else {
533
+ err ! ( DanglingPointerDeref )
557
534
}
558
535
} ,
559
536
}
0 commit comments