1+ use std:: cell:: RefCell ;
12use std:: iter;
23use std:: ops:: ControlFlow ;
34
@@ -483,17 +484,53 @@ impl VisitorState {
483484struct ImproperCTypesVisitor < ' a , ' tcx > {
484485 cx : & ' a LateContext < ' tcx > ,
485486 /// To prevent problems with recursive types,
486- /// add a types-in-check cache.
487- cache : FxHashSet < Ty < ' tcx > > ,
487+ /// add a types-in-check cache and a depth counter.
488+ recursion_limiter : RefCell < ( FxHashSet < Ty < ' tcx > > , usize ) > ,
489+
488490 /// The original type being checked, before we recursed
489491 /// to any other types it contains.
490492 base_ty : Ty < ' tcx > ,
491493 base_fn_mode : CItemKind ,
492494}
493495
496+ /// Structure similar to a mutex guard, allocated for each type in-check
497+ /// to let the ImproperCTypesVisitor know the current depth of the checking process.
498+ struct ImproperCTypesVisitorDepthGuard < ' a , ' tcx , ' v > ( & ' v ImproperCTypesVisitor < ' a , ' tcx > ) ;
499+
500+ impl < ' a , ' tcx , ' v > Drop for ImproperCTypesVisitorDepthGuard < ' a , ' tcx , ' v > {
501+ fn drop ( & mut self ) {
502+ let mut limiter_guard = self . 0 . recursion_limiter . borrow_mut ( ) ;
503+ let ( _, ref mut depth) = * limiter_guard;
504+ * depth -= 1 ;
505+ }
506+ }
507+
494508impl < ' a , ' tcx > ImproperCTypesVisitor < ' a , ' tcx > {
495509 fn new ( cx : & ' a LateContext < ' tcx > , base_ty : Ty < ' tcx > , base_fn_mode : CItemKind ) -> Self {
496- Self { cx, base_ty, base_fn_mode, cache : FxHashSet :: default ( ) }
510+ Self {
511+ cx,
512+ base_ty,
513+ base_fn_mode,
514+ recursion_limiter : RefCell :: new ( ( FxHashSet :: default ( ) , 0 ) ) ,
515+ }
516+ }
517+
518+ /// Protect against infinite recursion, for example
519+ /// `struct S(*mut S);`, or issue #130310.
520+ fn can_enter_type < ' v > (
521+ & ' v self ,
522+ ty : Ty < ' tcx > ,
523+ ) -> Result < ImproperCTypesVisitorDepthGuard < ' a , ' tcx , ' v > , FfiResult < ' tcx > > {
524+ // panic unlikely: this non-recursive function is the only place that
525+ // borrows the refcell, outside of ImproperCTypesVisitorDepthGuard::drop()
526+ let mut limiter_guard = self . recursion_limiter . borrow_mut ( ) ;
527+ let ( ref mut cache, ref mut depth) = * limiter_guard;
528+ if ( !cache. insert ( ty) ) || * depth >= 1024 {
529+ Err ( FfiResult :: FfiSafe )
530+ } else {
531+ * depth += 1 ;
532+ Ok ( ImproperCTypesVisitorDepthGuard ( self ) )
533+ }
497534 }
498535
499536 /// Checks if a simple numeric (int, float) type has an actual portable definition
@@ -516,7 +553,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
516553
517554 /// Checks if the given indirection (box,ref,pointer) is "ffi-safe".
518555 fn visit_indirection (
519- & mut self ,
556+ & self ,
520557 state : VisitorState ,
521558 ty : Ty < ' tcx > ,
522559 inner_ty : Ty < ' tcx > ,
@@ -564,7 +601,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
564601
565602 /// Checks if the given `VariantDef`'s field types are "ffi-safe".
566603 fn visit_variant_fields (
567- & mut self ,
604+ & self ,
568605 state : VisitorState ,
569606 ty : Ty < ' tcx > ,
570607 def : AdtDef < ' tcx > ,
@@ -609,7 +646,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
609646 }
610647
611648 fn visit_struct_or_union (
612- & mut self ,
649+ & self ,
613650 state : VisitorState ,
614651 ty : Ty < ' tcx > ,
615652 def : AdtDef < ' tcx > ,
@@ -666,7 +703,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
666703 }
667704
668705 fn visit_enum (
669- & mut self ,
706+ & self ,
670707 state : VisitorState ,
671708 ty : Ty < ' tcx > ,
672709 def : AdtDef < ' tcx > ,
@@ -718,23 +755,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
718755 /// Checks if the given type is "ffi-safe" (has a stable, well-defined
719756 /// representation which can be exported to C code).
720757 fn visit_type (
721- & mut self ,
758+ & self ,
722759 state : VisitorState ,
723760 outer_ty : Option < Ty < ' tcx > > ,
724761 ty : Ty < ' tcx > ,
725762 ) -> FfiResult < ' tcx > {
726763 use FfiResult :: * ;
727764
765+ let _depth_guard = match self . can_enter_type ( ty) {
766+ Ok ( guard) => guard,
767+ Err ( ffi_res) => return ffi_res,
768+ } ;
728769 let tcx = self . cx . tcx ;
729770
730- // Protect against infinite recursion, for example
731- // `struct S(*mut S);`.
732- // FIXME: A recursion limit is necessary as well, for irregular
733- // recursive types.
734- if !self . cache . insert ( ty) {
735- return FfiSafe ;
736- }
737-
738771 match * ty. kind ( ) {
739772 ty:: Adt ( def, args) => {
740773 if let Some ( inner_ty) = ty. boxed_ty ( ) {
@@ -910,7 +943,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
910943 }
911944 }
912945
913- fn visit_for_opaque_ty ( & mut self , ty : Ty < ' tcx > ) -> PartialFfiResult < ' tcx > {
946+ fn visit_for_opaque_ty ( & self , ty : Ty < ' tcx > ) -> PartialFfiResult < ' tcx > {
914947 struct ProhibitOpaqueTypes ;
915948 impl < ' tcx > ty:: TypeVisitor < TyCtxt < ' tcx > > for ProhibitOpaqueTypes {
916949 type Result = ControlFlow < Ty < ' tcx > > ;
@@ -933,7 +966,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
933966 . map ( |ty| FfiResult :: new_with_reason ( ty, fluent:: lint_improper_ctypes_opaque, None ) )
934967 }
935968
936- fn check_type ( & mut self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
969+ fn check_type ( & self , state : VisitorState , ty : Ty < ' tcx > ) -> FfiResult < ' tcx > {
937970 let ty = self . cx . tcx . try_normalize_erasing_regions ( self . cx . typing_env ( ) , ty) . unwrap_or ( ty) ;
938971 match self . visit_for_opaque_ty ( ty) {
939972 None => { }
@@ -1065,7 +1098,7 @@ impl<'tcx> ImproperCTypesLint {
10651098
10661099 let all_types = iter:: zip ( visitor. tys . drain ( ..) , visitor. spans . drain ( ..) ) ;
10671100 for ( fn_ptr_ty, span) in all_types {
1068- let mut visitor = ImproperCTypesVisitor :: new ( cx, fn_ptr_ty, fn_mode) ;
1101+ let visitor = ImproperCTypesVisitor :: new ( cx, fn_ptr_ty, fn_mode) ;
10691102 // FIXME(ctypes): make a check_for_fnptr
10701103 let ffi_res = visitor. check_type ( state, fn_ptr_ty) ;
10711104
@@ -1113,9 +1146,9 @@ impl<'tcx> ImproperCTypesLint {
11131146 ImproperCTypesVisitor :: check_struct_for_power_alignment ( cx, item, adt_def) ;
11141147 }
11151148
1116- fn check_foreign_static ( & mut self , cx : & LateContext < ' tcx > , id : hir:: OwnerId , span : Span ) {
1149+ fn check_foreign_static ( & self , cx : & LateContext < ' tcx > , id : hir:: OwnerId , span : Span ) {
11171150 let ty = cx. tcx . type_of ( id) . instantiate_identity ( ) ;
1118- let mut visitor = ImproperCTypesVisitor :: new ( cx, ty, CItemKind :: Declaration ) ;
1151+ let visitor = ImproperCTypesVisitor :: new ( cx, ty, CItemKind :: Declaration ) ;
11191152 let ffi_res = visitor. check_type ( VisitorState :: STATIC_TY , ty) ;
11201153 self . process_ffi_result ( cx, span, ffi_res, CItemKind :: Declaration ) ;
11211154 }
@@ -1133,14 +1166,14 @@ impl<'tcx> ImproperCTypesLint {
11331166
11341167 for ( input_ty, input_hir) in iter:: zip ( sig. inputs ( ) , decl. inputs ) {
11351168 let state = VisitorState :: argument_from_fnmode ( fn_mode) ;
1136- let mut visitor = ImproperCTypesVisitor :: new ( cx, * input_ty, fn_mode) ;
1169+ let visitor = ImproperCTypesVisitor :: new ( cx, * input_ty, fn_mode) ;
11371170 let ffi_res = visitor. check_type ( state, * input_ty) ;
11381171 self . process_ffi_result ( cx, input_hir. span , ffi_res, fn_mode) ;
11391172 }
11401173
11411174 if let hir:: FnRetTy :: Return ( ret_hir) = decl. output {
11421175 let state = VisitorState :: return_from_fnmode ( fn_mode) ;
1143- let mut visitor = ImproperCTypesVisitor :: new ( cx, sig. output ( ) , fn_mode) ;
1176+ let visitor = ImproperCTypesVisitor :: new ( cx, sig. output ( ) , fn_mode) ;
11441177 let ffi_res = visitor. check_type ( state, sig. output ( ) ) ;
11451178 self . process_ffi_result ( cx, ret_hir. span , ffi_res, fn_mode) ;
11461179 }
0 commit comments