@@ -413,6 +413,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
413413 /// to the allocation it points to. Supports both shared and mutable references, as the actual
414414 /// checking is offloaded to a helper closure.
415415 ///
416+ /// `alloc_size` will only get called for non-zero-sized accesses.
417+ ///
416418 /// Returns `None` if and only if the size is 0.
417419 fn check_and_deref_ptr < T > (
418420 & self ,
@@ -425,18 +427,19 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
425427 M :: ProvenanceExtra ,
426428 ) -> InterpResult < ' tcx , ( Size , Align , T ) > ,
427429 ) -> InterpResult < ' tcx , Option < T > > {
430+ // Everything is okay with size 0.
431+ if size. bytes ( ) == 0 {
432+ return Ok ( None ) ;
433+ }
434+
428435 Ok ( match self . ptr_try_get_alloc_id ( ptr) {
429436 Err ( addr) => {
430- // We couldn't get a proper allocation. This is only okay if the access size is 0,
431- // and the address is not null.
432- if size. bytes ( ) > 0 || addr == 0 {
433- throw_ub ! ( DanglingIntPointer ( addr, msg) ) ;
434- }
435- None
437+ // We couldn't get a proper allocation.
438+ throw_ub ! ( DanglingIntPointer ( addr, msg) ) ;
436439 }
437440 Ok ( ( alloc_id, offset, prov) ) => {
438441 let ( alloc_size, _alloc_align, ret_val) = alloc_size ( alloc_id, offset, prov) ?;
439- // Test bounds. This also ensures non-null.
442+ // Test bounds.
440443 // It is sufficient to check this for the end pointer. Also check for overflow!
441444 if offset. checked_add ( size, & self . tcx ) . map_or ( true , |end| end > alloc_size) {
442445 throw_ub ! ( PointerOutOfBounds {
@@ -447,14 +450,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
447450 msg,
448451 } )
449452 }
450- // Ensure we never consider the null pointer dereferenceable.
451- if M :: Provenance :: OFFSET_IS_ADDR {
452- assert_ne ! ( ptr. addr( ) , Size :: ZERO ) ;
453- }
454453
455- // We can still be zero-sized in this branch, in which case we have to
456- // return `None`.
457- if size. bytes ( ) == 0 { None } else { Some ( ret_val) }
454+ Some ( ret_val)
458455 }
459456 } )
460457 }
@@ -641,16 +638,18 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
641638 size,
642639 CheckInAllocMsg :: MemoryAccessTest ,
643640 |alloc_id, offset, prov| {
644- if !self . memory . validation_in_progress . get ( ) {
645- // We want to call the hook on *all* accesses that involve an AllocId,
646- // including zero-sized accesses. That means we have to do it here
647- // rather than below in the `Some` branch.
648- M :: before_alloc_read ( self , alloc_id) ?;
649- }
650641 let alloc = self . get_alloc_raw ( alloc_id) ?;
651642 Ok ( ( alloc. size ( ) , alloc. align , ( alloc_id, offset, prov, alloc) ) )
652643 } ,
653644 ) ?;
645+ // We want to call the hook on *all* accesses that involve an AllocId, including zero-sized
646+ // accesses. That means we cannot rely on the closure above or the `Some` branch below. We
647+ // do this after `check_and_deref_ptr` to ensure some basic sanity has already been checked.
648+ if !self . memory . validation_in_progress . get ( ) {
649+ if let Ok ( ( alloc_id, ..) ) = self . ptr_try_get_alloc_id ( ptr) {
650+ M :: before_alloc_read ( self , alloc_id) ?;
651+ }
652+ }
654653
655654 if let Some ( ( alloc_id, offset, prov, alloc) ) = ptr_and_alloc {
656655 let range = alloc_range ( offset, size) ;
0 commit comments