Skip to content

Commit 971f0ab

Browse files
committed
ImproperCTypes: add recursion limit
Simple change to stop irregular recursive types from causing infinitely-deep recursion in type checking.
1 parent b2c7050 commit 971f0ab

File tree

4 files changed

+75
-38
lines changed

4 files changed

+75
-38
lines changed

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 56 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::cell::RefCell;
12
use std::iter;
23
use std::ops::ControlFlow;
34

@@ -483,17 +484,53 @@ impl VisitorState {
483484
struct 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+
494508
impl<'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
}

tests/crashes/130310.rs

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//@ check-pass
2+
3+
//! this test checks that irregular recursive types do not cause stack overflow in ImproperCTypes
4+
//! Issue: https://github.com/rust-lang/rust/issues/94223
5+
6+
use std::marker::PhantomData;
7+
8+
#[repr(C)]
9+
struct A<T> {
10+
a: *const A<A<T>>, // without a recursion limit, checking this ends up creating checks for
11+
// infinitely deep types the likes of `A<A<A<A<A<A<...>>>>>>`
12+
p: PhantomData<T>,
13+
}
14+
15+
extern "C" {
16+
fn f(a: *const A<()>);
17+
}
18+
19+
fn main() {}
File renamed without changes.

0 commit comments

Comments
 (0)