@@ -9,11 +9,12 @@ use std::num::NonZeroUsize;
9
9
10
10
use either:: { Left , Right } ;
11
11
12
+ use hir:: def:: DefKind ;
12
13
use rustc_ast:: Mutability ;
13
14
use rustc_data_structures:: fx:: FxHashSet ;
14
15
use rustc_hir as hir;
15
16
use rustc_middle:: mir:: interpret:: {
16
- ExpectedKind , InterpError , InvalidMetaKind , PointerKind , ValidationErrorInfo ,
17
+ ExpectedKind , InterpError , InvalidMetaKind , PointerKind , Provenance , ValidationErrorInfo ,
17
18
ValidationErrorKind , ValidationErrorKind :: * ,
18
19
} ;
19
20
use rustc_middle:: ty;
@@ -123,15 +124,34 @@ pub enum PathElem {
123
124
}
124
125
125
126
/// Extra things to check for during validation of CTFE results.
127
+ #[ derive( Copy , Clone ) ]
126
128
pub enum CtfeValidationMode {
127
129
/// Regular validation, nothing special happening.
128
130
Regular ,
129
131
/// 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) .
133
135
/// `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
+ }
135
155
}
136
156
137
157
/// State for tracking recursive validation of references
@@ -412,6 +432,21 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
412
432
}
413
433
// Recursive checking
414
434
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
+ } ;
415
450
// Proceed recursively even for ZST, no reason to skip them!
416
451
// `!` is a ZST and we want to validate it.
417
452
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, '
422
457
// Special handling for pointers to statics (irrespective of their type).
423
458
assert ! ( !self . ecx. tcx. is_thread_local_static( did) ) ;
424
459
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 ( ) ) {
429
461
// See const_eval::machine::MemoryExtra::can_access_statics for why
430
462
// this check is so important.
431
463
// This check is reachable when the const just referenced the static,
432
464
// but never read it (so we never entered `before_access_global`).
433
465
throw_validation_failure ! ( self . path, PtrToStatic { ptr_kind } ) ;
434
466
}
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
+ }
435
483
// We skip recursively checking other statics. These statics must be sound by
436
484
// themselves, and the only way to get broken statics here is by using
437
485
// unsafe code.
@@ -453,9 +501,20 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
453
501
// and we would catch that here.
454
502
throw_validation_failure ! ( self . path, PtrToMut { ptr_kind } ) ;
455
503
}
504
+ if ptr_expected_mutbl == Mutability :: Mut
505
+ && alloc. inner ( ) . mutability == Mutability :: Not
506
+ {
507
+ throw_validation_failure ! ( self . path, MutableRefToImmutable ) ;
508
+ }
456
509
}
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 ! ( ) ,
459
518
}
460
519
}
461
520
let path = & self . path ;
@@ -525,17 +584,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
525
584
}
526
585
Ok ( true )
527
586
}
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 ( ..) => {
539
588
self . check_safe_pointer ( value, PointerKind :: Ref ) ?;
540
589
Ok ( true )
541
590
}
@@ -636,6 +685,19 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
636
685
)
637
686
}
638
687
}
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
+ }
639
701
}
640
702
641
703
impl < ' 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>
699
761
op : & OpTy < ' tcx , M :: Provenance > ,
700
762
_fields : NonZeroUsize ,
701
763
) -> 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 ( ) ) {
704
766
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
+ }
706
770
}
707
771
}
708
772
Ok ( ( ) )
@@ -724,11 +788,11 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
724
788
}
725
789
726
790
// 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
+ }
732
796
}
733
797
}
734
798
0 commit comments