@@ -10,6 +10,7 @@ use std::num::NonZero;
1010use either:: { Left , Right } ;
1111
1212use hir:: def:: DefKind ;
13+ use hir:: def_id:: DefId ;
1314use rustc_ast:: Mutability ;
1415use rustc_data_structures:: fx:: FxHashSet ;
1516use rustc_hir as hir;
@@ -449,73 +450,40 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
449450 // `!` is a ZST and we want to validate it.
450451 if let Ok ( ( alloc_id, _offset, _prov) ) = self . ecx . ptr_try_get_alloc_id ( place. ptr ( ) ) {
451452 let mut skip_recursive_check = false ;
452- // Let's see what kind of memory this points to.
453- // `unwrap` since dangling pointers have already been handled.
454- let alloc_kind = self . ecx . tcx . try_get_global_alloc ( alloc_id) . unwrap ( ) ;
455- let alloc_actual_mutbl = match alloc_kind {
456- GlobalAlloc :: Static ( did) => {
457- // Special handling for pointers to statics (irrespective of their type).
458- assert ! ( !self . ecx. tcx. is_thread_local_static( did) ) ;
459- assert ! ( self . ecx. tcx. is_static( did) ) ;
460- let DefKind :: Static { mutability, nested } = self . ecx . tcx . def_kind ( did)
461- else {
462- bug ! ( )
463- } ;
464- // Mode-specific checks
465- match self . ctfe_mode {
466- Some (
467- CtfeValidationMode :: Static { .. }
468- | CtfeValidationMode :: Promoted { .. } ,
469- ) => {
470- // We skip recursively checking other statics. These statics must be sound by
471- // themselves, and the only way to get broken statics here is by using
472- // unsafe code.
473- // The reasons we don't check other statics is twofold. For one, in all
474- // sound cases, the static was already validated on its own, and second, we
475- // trigger cycle errors if we try to compute the value of the other static
476- // and that static refers back to us (potentially through a promoted).
477- // This could miss some UB, but that's fine.
478- // We still walk nested allocations, as they are fundamentally part of this validation run.
479- // This means we will also recurse into nested statics of *other*
480- // statics, even though we do not recurse into other statics directly.
481- // That's somewhat inconsistent but harmless.
482- skip_recursive_check = !nested;
483- }
484- Some ( CtfeValidationMode :: Const { .. } ) => {
485- // We can't recursively validate `extern static`, so we better reject them.
486- if self . ecx . tcx . is_foreign_item ( did) {
487- throw_validation_failure ! ( self . path, ConstRefToExtern ) ;
488- }
489- }
490- None => { }
453+ let ( alloc_actual_mutbl, is_static) = mutability ( self . ecx , alloc_id) ;
454+ if let Some ( ( did, nested) ) = is_static {
455+ // Special handling for pointers to statics (irrespective of their type).
456+ assert ! ( !self . ecx. tcx. is_thread_local_static( did) ) ;
457+ assert ! ( self . ecx. tcx. is_static( did) ) ;
458+ // Mode-specific checks
459+ match self . ctfe_mode {
460+ Some (
461+ CtfeValidationMode :: Static { .. } | CtfeValidationMode :: Promoted { .. } ,
462+ ) => {
463+ // We skip recursively checking other statics. These statics must be sound by
464+ // themselves, and the only way to get broken statics here is by using
465+ // unsafe code.
466+ // The reasons we don't check other statics is twofold. For one, in all
467+ // sound cases, the static was already validated on its own, and second, we
468+ // trigger cycle errors if we try to compute the value of the other static
469+ // and that static refers back to us (potentially through a promoted).
470+ // This could miss some UB, but that's fine.
471+ // We still walk nested allocations, as they are fundamentally part of this validation run.
472+ // This means we will also recurse into nested statics of *other*
473+ // statics, even though we do not recurse into other statics directly.
474+ // That's somewhat inconsistent but harmless.
475+ skip_recursive_check = !nested;
491476 }
492- // Return alloc mutability. For "root" statics we look at the type to account for interior
493- // mutability; for nested statics we have no type and directly use the annotated mutability.
494- if nested {
495- mutability
496- } else {
497- match mutability {
498- Mutability :: Not
499- if !self
500- . ecx
501- . tcx
502- . type_of ( did)
503- . no_bound_vars ( )
504- . expect ( "statics should not have generic parameters" )
505- . is_freeze ( * self . ecx . tcx , ty:: ParamEnv :: reveal_all ( ) ) =>
506- {
507- Mutability :: Mut
508- }
509- _ => mutability,
477+ Some ( CtfeValidationMode :: Const { .. } ) => {
478+ // We can't recursively validate `extern static`, so we better reject them.
479+ if self . ecx . tcx . is_foreign_item ( did) {
480+ throw_validation_failure ! ( self . path, ConstRefToExtern ) ;
510481 }
511482 }
483+ None => { }
512484 }
513- GlobalAlloc :: Memory ( alloc) => alloc. inner ( ) . mutability ,
514- GlobalAlloc :: Function ( ..) | GlobalAlloc :: VTable ( ..) => {
515- // These are immutable, we better don't allow mutable pointers here.
516- Mutability :: Not
517- }
518- } ;
485+ }
486+
519487 // Mutability check.
520488 // If this allocation has size zero, there is no actual mutability here.
521489 let ( size, _align, _alloc_kind) = self . ecx . get_alloc_info ( alloc_id) ;
@@ -714,28 +682,56 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
714682 fn in_mutable_memory ( & self , op : & OpTy < ' tcx , M :: Provenance > ) -> bool {
715683 if let Some ( mplace) = op. as_mplace_or_imm ( ) . left ( ) {
716684 if let Some ( alloc_id) = mplace. ptr ( ) . provenance . and_then ( |p| p. get_alloc_id ( ) ) {
717- let mutability = match self . ecx . tcx . global_alloc ( alloc_id) {
718- GlobalAlloc :: Static ( did) => {
719- let DefKind :: Static { mutability, nested } = self . ecx . tcx . def_kind ( did)
720- else {
721- bug ! ( )
722- } ;
723- if nested {
724- mutability
725- } else {
726- self . ecx . memory . alloc_map . get ( alloc_id) . unwrap ( ) . 1 . mutability
727- }
728- }
729- GlobalAlloc :: Memory ( alloc) => alloc. inner ( ) . mutability ,
730- _ => span_bug ! ( self . ecx. tcx. span, "not a memory allocation" ) ,
731- } ;
732- return mutability == Mutability :: Mut ;
685+ return mutability ( self . ecx , alloc_id) . 0 . is_mut ( ) ;
733686 }
734687 }
735688 false
736689 }
737690}
738691
692+ /// Returns whether the allocation is mutable, and whether it's actually a static.
693+ /// For "root" statics we look at the type to account for interior
694+ /// mutability; for nested statics we have no type and directly use the annotated mutability.
695+ fn mutability < ' mir , ' tcx : ' mir > (
696+ ecx : & InterpCx < ' mir , ' tcx , impl Machine < ' mir , ' tcx > > ,
697+ alloc_id : AllocId ,
698+ ) -> ( Mutability , Option < ( DefId , bool ) > ) {
699+ // Let's see what kind of memory this points to.
700+ // We're not using `try_global_alloc` since dangling pointers have already been handled.
701+ match ecx. tcx . global_alloc ( alloc_id) {
702+ GlobalAlloc :: Static ( did) => {
703+ let DefKind :: Static { mutability, nested } = ecx. tcx . def_kind ( did) else { bug ! ( ) } ;
704+ let mutability = if nested {
705+ mutability
706+ } else {
707+ let mutability = match mutability {
708+ Mutability :: Not
709+ if !ecx
710+ . tcx
711+ . type_of ( did)
712+ . no_bound_vars ( )
713+ . expect ( "statics should not have generic parameters" )
714+ . is_freeze ( * ecx. tcx , ty:: ParamEnv :: reveal_all ( ) ) =>
715+ {
716+ Mutability :: Mut
717+ }
718+ _ => mutability,
719+ } ;
720+ if let Some ( ( _, alloc) ) = ecx. memory . alloc_map . get ( alloc_id) {
721+ assert_eq ! ( alloc. mutability, mutability) ;
722+ }
723+ mutability
724+ } ;
725+ ( mutability, Some ( ( did, nested) ) )
726+ }
727+ GlobalAlloc :: Memory ( alloc) => ( alloc. inner ( ) . mutability , None ) ,
728+ GlobalAlloc :: Function ( ..) | GlobalAlloc :: VTable ( ..) => {
729+ // These are immutable, we better don't allow mutable pointers here.
730+ ( Mutability :: Not , None )
731+ }
732+ }
733+ }
734+
739735impl < ' rt , ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > ValueVisitor < ' mir , ' tcx , M >
740736 for ValidityVisitor < ' rt , ' mir , ' tcx , M >
741737{
0 commit comments