@@ -113,6 +113,15 @@ pub enum PathElem {
113113 DynDowncast ,
114114}
115115
116+ /// Extra things to check for during validation of CTFE results.
117+ pub enum CtfeValidationMode {
118+ /// Regular validation, nothing special happening.
119+ Regular ,
120+ /// Validation of a `const`. `inner` says if this is an inner, indirect allocation (as opposed
121+ /// to the top-level const allocation).
122+ Const { inner : bool } ,
123+ }
124+
116125/// State for tracking recursive validation of references
117126pub struct RefTracking < T , PATH = ( ) > {
118127 pub seen : FxHashSet < T > ,
@@ -202,9 +211,9 @@ struct ValidityVisitor<'rt, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
202211 /// starts must not be changed! `visit_fields` and `visit_array` rely on
203212 /// this stack discipline.
204213 path : Vec < PathElem > ,
205- ref_tracking_for_consts :
206- Option < & ' rt mut RefTracking < MPlaceTy < ' tcx , M :: PointerTag > , Vec < PathElem > > > ,
207- may_ref_to_static : bool ,
214+ ref_tracking : Option < & ' rt mut RefTracking < MPlaceTy < ' tcx , M :: PointerTag > , Vec < PathElem > > > ,
215+ /// `None` indicates this is not validating for CTFE (but for runtime).
216+ ctfe_mode : Option < CtfeValidationMode > ,
208217 ecx : & ' rt InterpCx < ' mir , ' tcx , M > ,
209218}
210219
@@ -418,27 +427,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
418427 { "a dangling {} (use-after-free)" , kind } ,
419428 ) ;
420429 // Recursive checking
421- if let Some ( ref mut ref_tracking) = self . ref_tracking_for_consts {
430+ if let Some ( ref mut ref_tracking) = self . ref_tracking {
422431 if let Some ( ptr) = ptr {
423432 // not a ZST
424433 // Skip validation entirely for some external statics
425434 let alloc_kind = self . ecx . tcx . get_global_alloc ( ptr. alloc_id ) ;
426435 if let Some ( GlobalAlloc :: Static ( did) ) = alloc_kind {
427436 assert ! ( !self . ecx. tcx. is_thread_local_static( did) ) ;
428437 assert ! ( self . ecx. tcx. is_static( did) ) ;
429- if self . may_ref_to_static {
430- // We skip checking other statics. These statics must be sound by
431- // themselves, and the only way to get broken statics here is by using
432- // unsafe code.
433- // The reasons we don't check other statics is twofold. For one, in all
434- // sound cases, the static was already validated on its own, and second, we
435- // trigger cycle errors if we try to compute the value of the other static
436- // and that static refers back to us.
437- // We might miss const-invalid data,
438- // but things are still sound otherwise (in particular re: consts
439- // referring to statics).
440- return Ok ( ( ) ) ;
441- } else {
438+ if matches ! ( self . ctfe_mode, Some ( CtfeValidationMode :: Const { .. } ) ) {
442439 // See const_eval::machine::MemoryExtra::can_access_statics for why
443440 // this check is so important.
444441 // This check is reachable when the const just referenced the static,
@@ -447,6 +444,17 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
447444 { "a {} pointing to a static variable" , kind }
448445 ) ;
449446 }
447+ // We skip checking other statics. These statics must be sound by
448+ // themselves, and the only way to get broken statics here is by using
449+ // unsafe code.
450+ // The reasons we don't check other statics is twofold. For one, in all
451+ // sound cases, the static was already validated on its own, and second, we
452+ // trigger cycle errors if we try to compute the value of the other static
453+ // and that static refers back to us.
454+ // We might miss const-invalid data,
455+ // but things are still sound otherwise (in particular re: consts
456+ // referring to statics).
457+ return Ok ( ( ) ) ;
450458 }
451459 }
452460 // Proceed recursively even for ZST, no reason to skip them!
@@ -504,7 +512,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
504512 let value = self . ecx . read_scalar ( value) ?;
505513 // NOTE: Keep this in sync with the array optimization for int/float
506514 // types below!
507- if self . ref_tracking_for_consts . is_some ( ) {
515+ if self . ctfe_mode . is_some ( ) {
508516 // Integers/floats in CTFE: Must be scalar bits, pointers are dangerous
509517 let is_bits = value. check_init ( ) . map_or ( false , |v| v. is_bits ( ) ) ;
510518 if !is_bits {
@@ -723,6 +731,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
723731 // Sanity check: `builtin_deref` does not know any pointers that are not primitive.
724732 assert ! ( op. layout. ty. builtin_deref( true ) . is_none( ) ) ;
725733
734+ // Special check preventing `UnsafeCell` in constants
735+ if let Some ( def) = op. layout . ty . ty_adt_def ( ) {
736+ if matches ! ( self . ctfe_mode, Some ( CtfeValidationMode :: Const { inner: true } ) )
737+ && Some ( def. did ) == self . ecx . tcx . lang_items ( ) . unsafe_cell_type ( )
738+ {
739+ throw_validation_failure ! ( self . path, { "`UnsafeCell` in a `const`" } ) ;
740+ }
741+ }
742+
726743 // Recursively walk the value at its type.
727744 self . walk_value ( op) ?;
728745
@@ -814,7 +831,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValueVisitor<'mir, 'tcx, M>
814831 self . ecx ,
815832 ptr,
816833 size,
817- /*allow_uninit_and_ptr*/ self . ref_tracking_for_consts . is_none ( ) ,
834+ /*allow_uninit_and_ptr*/ self . ctfe_mode . is_none ( ) ,
818835 ) {
819836 // In the happy case, we needn't check anything else.
820837 Ok ( ( ) ) => { }
@@ -865,16 +882,13 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
865882 & self ,
866883 op : OpTy < ' tcx , M :: PointerTag > ,
867884 path : Vec < PathElem > ,
868- ref_tracking_for_consts : Option <
869- & mut RefTracking < MPlaceTy < ' tcx , M :: PointerTag > , Vec < PathElem > > ,
870- > ,
871- may_ref_to_static : bool ,
885+ ref_tracking : Option < & mut RefTracking < MPlaceTy < ' tcx , M :: PointerTag > , Vec < PathElem > > > ,
886+ ctfe_mode : Option < CtfeValidationMode > ,
872887 ) -> InterpResult < ' tcx > {
873888 trace ! ( "validate_operand_internal: {:?}, {:?}" , * op, op. layout. ty) ;
874889
875890 // Construct a visitor
876- let mut visitor =
877- ValidityVisitor { path, ref_tracking_for_consts, may_ref_to_static, ecx : self } ;
891+ let mut visitor = ValidityVisitor { path, ref_tracking, ctfe_mode, ecx : self } ;
878892
879893 // Try to cast to ptr *once* instead of all the time.
880894 let op = self . force_op_ptr ( op) . unwrap_or ( op) ;
@@ -902,23 +916,25 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
902916 /// `ref_tracking` is used to record references that we encounter so that they
903917 /// can be checked recursively by an outside driving loop.
904918 ///
905- /// `may_ref_to_static` controls whether references are allowed to point to statics.
919+ /// `constant` controls whether this must satisfy the rules for constants:
920+ /// - no pointers to statics.
921+ /// - no `UnsafeCell` or non-ZST `&mut`.
906922 #[ inline( always) ]
907923 pub fn const_validate_operand (
908924 & self ,
909925 op : OpTy < ' tcx , M :: PointerTag > ,
910926 path : Vec < PathElem > ,
911927 ref_tracking : & mut RefTracking < MPlaceTy < ' tcx , M :: PointerTag > , Vec < PathElem > > ,
912- may_ref_to_static : bool ,
928+ ctfe_mode : CtfeValidationMode ,
913929 ) -> InterpResult < ' tcx > {
914- self . validate_operand_internal ( op, path, Some ( ref_tracking) , may_ref_to_static )
930+ self . validate_operand_internal ( op, path, Some ( ref_tracking) , Some ( ctfe_mode ) )
915931 }
916932
917933 /// This function checks the data at `op` to be runtime-valid.
918934 /// `op` is assumed to cover valid memory if it is an indirect operand.
919935 /// It will error if the bits at the destination do not match the ones described by the layout.
920936 #[ inline( always) ]
921937 pub fn validate_operand ( & self , op : OpTy < ' tcx , M :: PointerTag > ) -> InterpResult < ' tcx > {
922- self . validate_operand_internal ( op, vec ! [ ] , None , false )
938+ self . validate_operand_internal ( op, vec ! [ ] , None , None )
923939 }
924940}
0 commit comments