Skip to content

Commit 1470565

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

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

@@ -357,17 +358,53 @@ impl VisitorState {
357358
struct ImproperCTypesVisitor<'a, 'tcx> {
358359
cx: &'a LateContext<'tcx>,
359360
/// To prevent problems with recursive types,
360-
/// add a types-in-check cache.
361-
cache: FxHashSet<Ty<'tcx>>,
361+
/// add a types-in-check cache and a depth counter.
362+
recursion_limiter: RefCell<(FxHashSet<Ty<'tcx>>, usize)>,
363+
362364
/// The original type being checked, before we recursed
363365
/// to any other types it contains.
364366
base_ty: Ty<'tcx>,
365367
base_fn_mode: CItemKind,
366368
}
367369

370+
/// Structure similar to a mutex guard, allocated for each type in-check
371+
/// to let the ImproperCTypesVisitor know the current depth of the checking process.
372+
struct ImproperCTypesVisitorDepthGuard<'a, 'tcx, 'v>(&'v ImproperCTypesVisitor<'a, 'tcx>);
373+
374+
impl<'a, 'tcx, 'v> Drop for ImproperCTypesVisitorDepthGuard<'a, 'tcx, 'v> {
375+
fn drop(&mut self) {
376+
let mut limiter_guard = self.0.recursion_limiter.borrow_mut();
377+
let (_, ref mut depth) = *limiter_guard;
378+
*depth -= 1;
379+
}
380+
}
381+
368382
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
369383
fn new(cx: &'a LateContext<'tcx>, base_ty: Ty<'tcx>, base_fn_mode: CItemKind) -> Self {
370-
Self { cx, base_ty, base_fn_mode, cache: FxHashSet::default() }
384+
Self {
385+
cx,
386+
base_ty,
387+
base_fn_mode,
388+
recursion_limiter: RefCell::new((FxHashSet::default(), 0)),
389+
}
390+
}
391+
392+
/// Protect against infinite recursion, for example
393+
/// `struct S(*mut S);`, or issue #130310.
394+
fn can_enter_type<'v>(
395+
&'v self,
396+
ty: Ty<'tcx>,
397+
) -> Result<ImproperCTypesVisitorDepthGuard<'a, 'tcx, 'v>, FfiResult<'tcx>> {
398+
// panic unlikely: this non-recursive function is the only place that
399+
// borrows the refcell, outside of ImproperCTypesVisitorDepthGuard::drop()
400+
let mut limiter_guard = self.recursion_limiter.borrow_mut();
401+
let (ref mut cache, ref mut depth) = *limiter_guard;
402+
if (!cache.insert(ty)) || *depth >= 1024 {
403+
Err(FfiResult::FfiSafe)
404+
} else {
405+
*depth += 1;
406+
Ok(ImproperCTypesVisitorDepthGuard(self))
407+
}
371408
}
372409

373410
/// Checks if a simple numeric (int, float) type has an actual portable definition
@@ -390,7 +427,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
390427

391428
/// Checks if the given indirection (box,ref,pointer) is "ffi-safe".
392429
fn visit_indirection(
393-
&mut self,
430+
&self,
394431
state: VisitorState,
395432
ty: Ty<'tcx>,
396433
inner_ty: Ty<'tcx>,
@@ -438,7 +475,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
438475

439476
/// Checks if the given `VariantDef`'s field types are "ffi-safe".
440477
fn visit_variant_fields(
441-
&mut self,
478+
&self,
442479
state: VisitorState,
443480
ty: Ty<'tcx>,
444481
def: AdtDef<'tcx>,
@@ -490,7 +527,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
490527
}
491528

492529
fn visit_struct_or_union(
493-
&mut self,
530+
&self,
494531
state: VisitorState,
495532
ty: Ty<'tcx>,
496533
def: AdtDef<'tcx>,
@@ -547,7 +584,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
547584
}
548585

549586
fn visit_enum(
550-
&mut self,
587+
&self,
551588
state: VisitorState,
552589
ty: Ty<'tcx>,
553590
def: AdtDef<'tcx>,
@@ -599,23 +636,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
599636
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
600637
/// representation which can be exported to C code).
601638
fn visit_type(
602-
&mut self,
639+
&self,
603640
state: VisitorState,
604641
outer_ty: Option<Ty<'tcx>>,
605642
ty: Ty<'tcx>,
606643
) -> FfiResult<'tcx> {
607644
use FfiResult::*;
608645

646+
let _depth_guard = match self.can_enter_type(ty) {
647+
Ok(guard) => guard,
648+
Err(ffi_res) => return ffi_res,
649+
};
609650
let tcx = self.cx.tcx;
610651

611-
// Protect against infinite recursion, for example
612-
// `struct S(*mut S);`.
613-
// FIXME: A recursion limit is necessary as well, for irregular
614-
// recursive types.
615-
if !self.cache.insert(ty) {
616-
return FfiSafe;
617-
}
618-
619652
match *ty.kind() {
620653
ty::Adt(def, args) => {
621654
if let Some(inner_ty) = ty.boxed_ty() {
@@ -796,7 +829,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
796829
}
797830
}
798831

799-
fn visit_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> {
832+
fn visit_for_opaque_ty(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> {
800833
struct ProhibitOpaqueTypes;
801834
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
802835
type Result = ControlFlow<Ty<'tcx>>;
@@ -821,7 +854,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
821854
}
822855
}
823856

824-
fn check_for_type(&mut self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
857+
fn check_for_type(&self, state: VisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
825858
let ty = self.cx.tcx.try_normalize_erasing_regions(self.cx.typing_env(), ty).unwrap_or(ty);
826859

827860
match self.visit_for_opaque_ty(ty) {
@@ -944,7 +977,7 @@ impl<'tcx> ImproperCTypesLint {
944977

945978
let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..));
946979
all_types.for_each(|(fn_ptr_ty, span)| {
947-
let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
980+
let visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
948981
// TODO: make a check_for_fnptr
949982
let ffi_res = visitor.check_for_type(state, fn_ptr_ty);
950983

@@ -992,9 +1025,9 @@ impl<'tcx> ImproperCTypesLint {
9921025
ImproperCTypesVisitor::check_struct_for_power_alignment(cx, item, adt_def);
9931026
}
9941027

995-
fn check_foreign_static(&mut self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
1028+
fn check_foreign_static(&self, cx: &LateContext<'tcx>, id: hir::OwnerId, span: Span) {
9961029
let ty = cx.tcx.type_of(id).instantiate_identity();
997-
let mut visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration);
1030+
let visitor = ImproperCTypesVisitor::new(cx, ty, CItemKind::Declaration);
9981031
let ffi_res = visitor.check_for_type(VisitorState::StaticTy, ty);
9991032
self.process_ffi_result(cx, span, ffi_res, CItemKind::Declaration);
10001033
}
@@ -1012,14 +1045,14 @@ impl<'tcx> ImproperCTypesLint {
10121045

10131046
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
10141047
let state = VisitorState::argument_from_fnmode(fn_mode);
1015-
let mut visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode);
1048+
let visitor = ImproperCTypesVisitor::new(cx, *input_ty, fn_mode);
10161049
let ffi_res = visitor.check_for_type(state, *input_ty);
10171050
self.process_ffi_result(cx, input_hir.span, ffi_res, fn_mode);
10181051
}
10191052

10201053
if let hir::FnRetTy::Return(ret_hir) = decl.output {
10211054
let state = VisitorState::return_from_fnmode(fn_mode);
1022-
let mut visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode);
1055+
let visitor = ImproperCTypesVisitor::new(cx, sig.output(), fn_mode);
10231056
let ffi_res = visitor.check_for_type(state, sig.output());
10241057
self.process_ffi_result(cx, ret_hir.span, ffi_res, fn_mode);
10251058
}

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)