@@ -9,11 +9,12 @@ use std::num::NonZeroUsize;
99
1010use either:: { Left , Right } ;
1111
12+ use hir:: def:: DefKind ;
1213use rustc_ast:: Mutability ;
1314use rustc_data_structures:: fx:: FxHashSet ;
1415use rustc_hir as hir;
1516use rustc_middle:: mir:: interpret:: {
16- ExpectedKind , InterpError , InvalidMetaKind , PointerKind , ValidationErrorInfo ,
17+ ExpectedKind , InterpError , InvalidMetaKind , PointerKind , Provenance , ValidationErrorInfo ,
1718 ValidationErrorKind , ValidationErrorKind :: * ,
1819} ;
1920use rustc_middle:: ty;
@@ -123,15 +124,34 @@ pub enum PathElem {
123124}
124125
125126/// Extra things to check for during validation of CTFE results.
127+ #[ derive( Copy , Clone ) ]
126128pub enum CtfeValidationMode {
127129 /// Regular validation, nothing special happening.
128130 Regular ,
129131 /// Validation of a `const`.
130- /// `inner ` says if this is an inner, indirect allocation (as opposed to the top-level const
131- /// allocation). Being an inner allocation makes a difference because the top-level allocation
132- /// of a `const` is copied for each use, but the inner allocations are implicitly shared .
132+ /// `allow_immutable_unsafe_cell ` says whether we allow `UnsafeCell` in immutable memory (which is the
133+ /// case for the top-level allocation of a `const`, where this is fine because the allocation will be
134+ /// copied at each use site) .
133135 /// `allow_static_ptrs` says if pointers to statics are permitted (which is the case for promoteds in statics).
134- Const { inner : bool , allow_static_ptrs : bool } ,
136+ Const { allow_immutable_unsafe_cell : bool , allow_static_ptrs : bool } ,
137+ }
138+
139+ impl CtfeValidationMode {
140+ fn allow_immutable_unsafe_cell ( self ) -> bool {
141+ match self {
142+ CtfeValidationMode :: Regular => false ,
143+ CtfeValidationMode :: Const { allow_immutable_unsafe_cell, .. } => {
144+ allow_immutable_unsafe_cell
145+ }
146+ }
147+ }
148+
149+ fn allow_static_ptrs ( self ) -> bool {
150+ match self {
151+ CtfeValidationMode :: Regular => true , // statics can point to statics
152+ CtfeValidationMode :: Const { allow_static_ptrs, .. } => allow_static_ptrs,
153+ }
154+ }
135155}
136156
137157/// State for tracking recursive validation of references
@@ -412,6 +432,21 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
412432 }
413433 // Recursive checking
414434 if let Some ( ref_tracking) = self . ref_tracking . as_deref_mut ( ) {
435+ // Determine whether this pointer expects to be pointing to something mutable.
436+ let ptr_expected_mutbl = match ptr_kind {
437+ PointerKind :: Box => Mutability :: Mut ,
438+ PointerKind :: Ref => {
439+ let tam = value. layout . ty . builtin_deref ( false ) . unwrap ( ) ;
440+ // ZST never require mutability. We do not take into account interior mutability here
441+ // since we cannot know if there really is an `UnsafeCell` -- so we check that
442+ // in the recursive descent behind this reference.
443+ if size == Size :: ZERO || tam. mutbl == Mutability :: Not {
444+ Mutability :: Not
445+ } else {
446+ Mutability :: Mut
447+ }
448+ }
449+ } ;
415450 // Proceed recursively even for ZST, no reason to skip them!
416451 // `!` is a ZST and we want to validate it.
417452 if let Ok ( ( alloc_id, _offset, _prov) ) = self . ecx . ptr_try_get_alloc_id ( place. ptr ( ) ) {
@@ -422,16 +457,29 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
422457 // Special handling for pointers to statics (irrespective of their type).
423458 assert ! ( !self . ecx. tcx. is_thread_local_static( did) ) ;
424459 assert ! ( self . ecx. tcx. is_static( did) ) ;
425- if matches ! (
426- self . ctfe_mode,
427- Some ( CtfeValidationMode :: Const { allow_static_ptrs: false , .. } )
428- ) {
460+ if self . ctfe_mode . is_some_and ( |c| !c. allow_static_ptrs ( ) ) {
429461 // See const_eval::machine::MemoryExtra::can_access_statics for why
430462 // this check is so important.
431463 // This check is reachable when the const just referenced the static,
432464 // but never read it (so we never entered `before_access_global`).
433465 throw_validation_failure ! ( self . path, PtrToStatic { ptr_kind } ) ;
434466 }
467+ // Mutability check.
468+ if ptr_expected_mutbl == Mutability :: Mut {
469+ if matches ! (
470+ self . ecx. tcx. def_kind( did) ,
471+ DefKind :: Static ( Mutability :: Not )
472+ ) && self
473+ . ecx
474+ . tcx
475+ . type_of ( did)
476+ . no_bound_vars ( )
477+ . expect ( "statics should not have generic parameters" )
478+ . is_freeze ( * self . ecx . tcx , ty:: ParamEnv :: reveal_all ( ) )
479+ {
480+ throw_validation_failure ! ( self . path, MutableRefToImmutable ) ;
481+ }
482+ }
435483 // We skip recursively checking other statics. These statics must be sound by
436484 // themselves, and the only way to get broken statics here is by using
437485 // unsafe code.
@@ -453,9 +501,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
453501 // and we would catch that here.
454502 throw_validation_failure ! ( self . path, PtrToMut { ptr_kind } ) ;
455503 }
504+ if ptr_expected_mutbl == Mutability :: Mut
505+ && alloc. inner ( ) . mutability == Mutability :: Not
506+ {
507+ throw_validation_failure ! ( self . path, MutableRefToImmutable ) ;
508+ }
456509 }
457- // Nothing to check for these.
458- None | Some ( GlobalAlloc :: Function ( ..) | GlobalAlloc :: VTable ( ..) ) => { }
510+ Some ( GlobalAlloc :: Function ( ..) | GlobalAlloc :: VTable ( ..) ) => {
511+ // These are immutable, we better don't allow mutable pointers here.
512+ if ptr_expected_mutbl == Mutability :: Mut {
513+ throw_validation_failure ! ( self . path, MutableRefToImmutable ) ;
514+ }
515+ }
516+ // Dangling, already handled.
517+ None => bug ! ( ) ,
459518 }
460519 }
461520 let path = & self . path ;
@@ -525,17 +584,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
525584 }
526585 Ok ( true )
527586 }
528- ty:: Ref ( _, ty, mutbl) => {
529- if matches ! ( self . ctfe_mode, Some ( CtfeValidationMode :: Const { .. } ) )
530- && * mutbl == Mutability :: Mut
531- {
532- // A mutable reference inside a const? That does not seem right (except if it is
533- // a ZST).
534- let layout = self . ecx . layout_of ( * ty) ?;
535- if !layout. is_zst ( ) {
536- throw_validation_failure ! ( self . path, MutableRefInConst ) ;
537- }
538- }
587+ ty:: Ref ( ..) => {
539588 self . check_safe_pointer ( value, PointerKind :: Ref ) ?;
540589 Ok ( true )
541590 }
@@ -636,6 +685,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
636685 )
637686 }
638687 }
688+
689+ fn in_mutable_memory ( & self , op : & OpTy < ' tcx , M :: Provenance > ) -> bool {
690+ if let Some ( mplace) = op. as_mplace_or_imm ( ) . left ( ) {
691+ if let Some ( alloc_id) = mplace. ptr ( ) . provenance . and_then ( |p| p. get_alloc_id ( ) ) {
692+ if self . ecx . tcx . global_alloc ( alloc_id) . unwrap_memory ( ) . inner ( ) . mutability
693+ == Mutability :: Mut
694+ {
695+ return true ;
696+ }
697+ }
698+ }
699+ false
700+ }
639701}
640702
641703impl < ' rt , ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > ValueVisitor < ' mir , ' tcx , M >
@@ -699,10 +761,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
699761 op : & OpTy < ' tcx , M :: Provenance > ,
700762 _fields : NonZeroUsize ,
701763 ) -> InterpResult < ' tcx > {
702- // Special check preventing `UnsafeCell` inside unions in the inner part of constants .
703- if matches ! ( self . ctfe_mode, Some ( CtfeValidationMode :: Const { inner : true , .. } ) ) {
764+ // Special check for CTFE validation, preventing `UnsafeCell` inside unions in immutable memory .
765+ if self . ctfe_mode . is_some_and ( |c| !c . allow_immutable_unsafe_cell ( ) ) {
704766 if !op. layout . ty . is_freeze ( * self . ecx . tcx , self . ecx . param_env ) {
705- throw_validation_failure ! ( self . path, UnsafeCell ) ;
767+ if !self . in_mutable_memory ( op) {
768+ throw_validation_failure ! ( self . path, UnsafeCellInImmutable ) ;
769+ }
706770 }
707771 }
708772 Ok ( ( ) )
@@ -724,11 +788,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
724788 }
725789
726790 // Special check preventing `UnsafeCell` in the inner part of constants
727- if let Some ( def ) = op . layout . ty . ty_adt_def ( ) {
728- if matches ! ( self . ctfe_mode , Some ( CtfeValidationMode :: Const { inner : true , .. } ) )
729- && def . is_unsafe_cell ( )
730- {
731- throw_validation_failure ! ( self . path , UnsafeCell ) ;
791+ if self . ctfe_mode . is_some_and ( |c| !c . allow_immutable_unsafe_cell ( ) ) {
792+ if let Some ( def ) = op . layout . ty . ty_adt_def ( ) && def . is_unsafe_cell ( ) {
793+ if ! self . in_mutable_memory ( op ) {
794+ throw_validation_failure ! ( self . path , UnsafeCellInImmutable ) ;
795+ }
732796 }
733797 }
734798
0 commit comments