@@ -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;
@@ -27,9 +28,9 @@ use rustc_target::abi::{
2728use std:: hash:: Hash ;
2829
2930use super :: {
30- format_interp_error, machine :: AllocMap , AllocId , CheckInAllocMsg , GlobalAlloc , ImmTy ,
31- Immediate , InterpCx , InterpResult , MPlaceTy , Machine , MemPlaceMeta , OpTy , Pointer , Projectable ,
32- Scalar , ValueVisitor ,
31+ format_interp_error, AllocId , CheckInAllocMsg , GlobalAlloc , ImmTy , Immediate , InterpCx ,
32+ InterpResult , MPlaceTy , Machine , MemPlaceMeta , OpTy , Pointer , Projectable , Scalar ,
33+ ValueVisitor ,
3334} ;
3435
3536// for the validation errors
@@ -433,7 +434,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
433434 throw_validation_failure ! ( self . path, PtrToUninhabited { ptr_kind, ty } )
434435 }
435436 // Recursive checking
436- if let Some ( ref_tracking ) = self . ref_tracking . as_deref_mut ( ) {
437+ if self . ref_tracking . is_some ( ) {
437438 // Determine whether this pointer expects to be pointing to something mutable.
438439 let ptr_expected_mutbl = match ptr_kind {
439440 PointerKind :: Box => Mutability :: Mut ,
@@ -457,6 +458,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
457458 // Special handling for pointers to statics (irrespective of their type).
458459 assert ! ( !self . ecx. tcx. is_thread_local_static( did) ) ;
459460 assert ! ( self . ecx. tcx. is_static( did) ) ;
461+ // Return alloc mutability. For "root" statics we look at the type to account for interior
462+ // mutability; for nested statics we have no type and directly use the annotated mutability.
463+ let DefKind :: Static { mutability, nested } = self . ecx . tcx . def_kind ( did)
464+ else {
465+ bug ! ( )
466+ } ;
460467 // Mode-specific checks
461468 match self . ctfe_mode {
462469 Some (
@@ -471,7 +478,8 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
471478 // trigger cycle errors if we try to compute the value of the other static
472479 // and that static refers back to us (potentially through a promoted).
473480 // This could miss some UB, but that's fine.
474- skip_recursive_check = true ;
481+ // We still walk nested allocations, as they are fundamentally part of this validation run.
482+ skip_recursive_check = !nested;
475483 }
476484 Some ( CtfeValidationMode :: Const { .. } ) => {
477485 // We can't recursively validate `extern static`, so we better reject them.
@@ -481,28 +489,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
481489 }
482490 None => { }
483491 }
484- // Return alloc mutability. For "root" statics we look at the type to account for interior
485- // mutability; for nested statics we have no type and directly use the annotated mutability.
486- let DefKind :: Static { mutability, nested } = self . ecx . tcx . def_kind ( did)
487- else {
488- bug ! ( )
489- } ;
490- match ( mutability, nested) {
491- ( Mutability :: Mut , _) => Mutability :: Mut ,
492- ( Mutability :: Not , true ) => Mutability :: Not ,
493- ( Mutability :: Not , false )
494- if !self
495- . ecx
496- . tcx
497- . type_of ( did)
498- . no_bound_vars ( )
499- . expect ( "statics should not have generic parameters" )
500- . is_freeze ( * self . ecx . tcx , ty:: ParamEnv :: reveal_all ( ) ) =>
501- {
502- Mutability :: Mut
503- }
504- ( Mutability :: Not , false ) => Mutability :: Not ,
505- }
492+ self . static_mutability ( mutability, nested, did)
506493 }
507494 GlobalAlloc :: Memory ( alloc) => alloc. inner ( ) . mutability ,
508495 GlobalAlloc :: Function ( ..) | GlobalAlloc :: VTable ( ..) => {
@@ -535,7 +522,7 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
535522 }
536523 }
537524 let path = & self . path ;
538- ref_tracking. track ( place, || {
525+ self . ref_tracking . as_deref_mut ( ) . unwrap ( ) . track ( place, || {
539526 // We need to clone the path anyway, make sure it gets created
540527 // with enough space for the additional `Deref`.
541528 let mut new_path = Vec :: with_capacity ( path. len ( ) + 1 ) ;
@@ -547,6 +534,25 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
547534 Ok ( ( ) )
548535 }
549536
537+ fn static_mutability ( & self , mutability : Mutability , nested : bool , did : DefId ) -> Mutability {
538+ match ( mutability, nested) {
539+ ( Mutability :: Mut , _) => Mutability :: Mut ,
540+ ( Mutability :: Not , true ) => Mutability :: Not ,
541+ ( Mutability :: Not , false )
542+ if !self
543+ . ecx
544+ . tcx
545+ . type_of ( did)
546+ . no_bound_vars ( )
547+ . expect ( "statics should not have generic parameters" )
548+ . is_freeze ( * self . ecx . tcx , ty:: ParamEnv :: reveal_all ( ) ) =>
549+ {
550+ Mutability :: Mut
551+ }
552+ ( Mutability :: Not , false ) => Mutability :: Not ,
553+ }
554+ }
555+
550556 /// Check if this is a value of primitive type, and if yes check the validity of the value
551557 /// at that type. Return `true` if the type is indeed primitive.
552558 ///
@@ -599,6 +605,15 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
599605 if place. layout . is_unsized ( ) {
600606 self . check_wide_ptr_meta ( place. meta ( ) , place. layout ) ?;
601607 }
608+ if self . ref_tracking . is_some ( )
609+ && let Some ( alloc_id) = place. ptr ( ) . provenance . and_then ( |p| p. get_alloc_id ( ) )
610+ && self . ecx . tcx . try_get_global_alloc ( alloc_id) . is_none ( )
611+ {
612+ throw_validation_failure ! (
613+ self . path,
614+ DanglingPtrUseAfterFree { ptr_kind: PointerKind :: Ref ( Mutability :: Not ) }
615+ )
616+ }
602617 Ok ( true )
603618 }
604619 ty:: Ref ( _, _ty, mutbl) => {
@@ -708,9 +723,12 @@ impl<'rt, 'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> ValidityVisitor<'rt, 'mir, '
708723 if let Some ( mplace) = op. as_mplace_or_imm ( ) . left ( ) {
709724 if let Some ( alloc_id) = mplace. ptr ( ) . provenance . and_then ( |p| p. get_alloc_id ( ) ) {
710725 let mutability = match self . ecx . tcx . global_alloc ( alloc_id) {
711- GlobalAlloc :: Static ( _) => {
712- self . ecx . memory . alloc_map . get ( alloc_id) . unwrap ( ) . 1 . mutability
713- }
726+ GlobalAlloc :: Static ( id) => match self . ecx . tcx . def_kind ( id) {
727+ DefKind :: Static { nested, mutability } => {
728+ self . static_mutability ( mutability, nested, id)
729+ }
730+ _ => bug ! ( ) ,
731+ } ,
714732 GlobalAlloc :: Memory ( alloc) => alloc. inner ( ) . mutability ,
715733 _ => span_bug ! ( self . ecx. tcx. span, "not a memory allocation" ) ,
716734 } ;
@@ -975,15 +993,15 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
975993 path : Vec < PathElem > ,
976994 ref_tracking : Option < & mut RefTracking < MPlaceTy < ' tcx , M :: Provenance > , Vec < PathElem > > > ,
977995 ctfe_mode : Option < CtfeValidationMode > ,
978- ) -> InterpResult < ' tcx > {
996+ ) -> InterpResult < ' tcx , Vec < PathElem > > {
979997 trace ! ( "validate_operand_internal: {:?}, {:?}" , * op, op. layout. ty) ;
980998
981999 // Construct a visitor
9821000 let mut visitor = ValidityVisitor { path, ref_tracking, ctfe_mode, ecx : self } ;
9831001
9841002 // Run it.
9851003 match self . run_for_validation ( || visitor. visit_value ( op) ) {
986- Ok ( ( ) ) => Ok ( ( ) ) ,
1004+ Ok ( ( ) ) => Ok ( visitor . path ) ,
9871005 // Pass through validation failures and "invalid program" issues.
9881006 Err ( err)
9891007 if matches ! (
@@ -1003,7 +1021,9 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10031021 }
10041022 }
10051023 }
1024+ }
10061025
1026+ impl < ' mir , ' tcx > InterpCx < ' mir , ' tcx , crate :: const_eval:: CompileTimeInterpreter < ' mir , ' tcx > > {
10071027 /// This function checks the data at `op` to be const-valid.
10081028 /// `op` is assumed to cover valid memory if it is an indirect operand.
10091029 /// It will error if the bits at the destination do not match the ones described by the layout.
@@ -1017,14 +1037,38 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10171037 #[ inline( always) ]
10181038 pub ( crate ) fn const_validate_operand (
10191039 & self ,
1020- op : & OpTy < ' tcx , M :: Provenance > ,
1040+ mplace : MPlaceTy < ' tcx > ,
10211041 path : Vec < PathElem > ,
1022- ref_tracking : & mut RefTracking < MPlaceTy < ' tcx , M :: Provenance > , Vec < PathElem > > ,
1042+ ref_tracking : & mut RefTracking < MPlaceTy < ' tcx > , Vec < PathElem > > ,
10231043 ctfe_mode : CtfeValidationMode ,
10241044 ) -> InterpResult < ' tcx > {
1025- self . validate_operand_internal ( op, path, Some ( ref_tracking) , Some ( ctfe_mode) )
1045+ let prov = mplace. ptr ( ) . provenance ;
1046+ let path = self . validate_operand_internal (
1047+ & mplace. into ( ) ,
1048+ path,
1049+ Some ( ref_tracking) ,
1050+ Some ( ctfe_mode) ,
1051+ ) ?;
1052+
1053+ // There was no error, so let's check the rest of the relocations in the pointed-to allocation for
1054+ // dangling pointers.
1055+ if let Some ( prov) = prov
1056+ && let Some ( GlobalAlloc :: Memory ( alloc) ) = self . tcx . try_get_global_alloc ( prov. alloc_id ( ) )
1057+ {
1058+ for ( _, prov) in alloc. 0 . provenance ( ) . ptrs ( ) . iter ( ) {
1059+ if self . tcx . try_get_global_alloc ( prov. alloc_id ( ) ) . is_none ( ) {
1060+ throw_validation_failure ! (
1061+ path,
1062+ DanglingPtrUseAfterFree { ptr_kind: PointerKind :: Ref ( Mutability :: Not ) }
1063+ )
1064+ }
1065+ }
1066+ }
1067+ Ok ( ( ) )
10261068 }
1069+ }
10271070
1071+ impl < ' mir , ' tcx : ' mir , M : Machine < ' mir , ' tcx > > InterpCx < ' mir , ' tcx , M > {
10281072 /// This function checks the data at `op` to be runtime-valid.
10291073 /// `op` is assumed to cover valid memory if it is an indirect operand.
10301074 /// It will error if the bits at the destination do not match the ones described by the layout.
@@ -1034,6 +1078,6 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
10341078 // still correct to not use `ctfe_mode`: that mode is for validation of the final constant
10351079 // value, it rules out things like `UnsafeCell` in awkward places. It also can make checking
10361080 // recurse through references which, for now, we don't want here, either.
1037- self . validate_operand_internal ( op, vec ! [ ] , None , None )
1081+ self . validate_operand_internal ( op, vec ! [ ] , None , None ) . map ( drop )
10381082 }
10391083}
0 commit comments