@@ -19,7 +19,7 @@ use syntax::ast::Mutability;
1919use super :: {
2020 Pointer , AllocId , Allocation , GlobalId , AllocationExtra ,
2121 InterpResult , Scalar , InterpError , GlobalAlloc , PointerArithmetic ,
22- Machine , AllocMap , MayLeak , ErrorHandled , CheckInAllocMsg , InboundsCheck ,
22+ Machine , AllocMap , MayLeak , ErrorHandled , CheckInAllocMsg ,
2323 InterpError :: ValidationFailure ,
2424} ;
2525
@@ -44,6 +44,17 @@ impl<T: MayLeak> MayLeak for MemoryKind<T> {
4444 }
4545}
4646
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+
4758// `Memory` has to depend on the `Machine` because some of its operations
4859// (e.g., `get`) call a `Machine` hook.
4960pub struct Memory < ' mir , ' tcx , M : Machine < ' mir , ' tcx > > {
@@ -248,66 +259,6 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
248259 Ok ( ( ) )
249260 }
250261
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-
311262 /// Check if the given scalar is allowed to do a memory access of given `size`
312263 /// and `align`. On success, returns `None` for zero-sized accesses (where
313264 /// 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> {
350301 None
351302 }
352303 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+ }
365333
366334 // We can still be zero-sized in this branch, in which case we have to
367335 // return `None`.
@@ -375,8 +343,9 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
375343 & self ,
376344 ptr : Pointer < M :: PointerTag > ,
377345 ) -> 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 ( )
380349 }
381350}
382351
@@ -515,13 +484,14 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
515484 }
516485 }
517486
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.
519489 ///
520- /// If `liveness` is `InboundsCheck ::MaybeDead`, this function always returns `Ok`
490+ /// If `liveness` is `AllocCheck ::MaybeDead`, this function always returns `Ok`.
521491 pub fn get_size_and_align (
522492 & self ,
523493 id : AllocId ,
524- liveness : InboundsCheck ,
494+ liveness : AllocCheck ,
525495 ) -> InterpResult < ' static , ( Size , Align ) > {
526496 if let Ok ( alloc) = self . get ( id) {
527497 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> {
531501 let alloc = self . tcx . alloc_map . lock ( ) . get ( id) ;
532502 // Could also be a fn ptr or extern static
533503 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+ }
535512 // `self.get` would also work, but can cause cycles if a static refers to itself
536513 Some ( GlobalAlloc :: Static ( did) ) => {
537514 // 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> {
545522 if let Ok ( alloc) = self . get ( id) {
546523 return Ok ( ( Size :: from_bytes ( alloc. bytes . len ( ) as u64 ) , alloc. align ) ) ;
547524 }
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 )
557534 }
558535 } ,
559536 }
0 commit comments