Skip to content

Commit a37e0c5

Browse files
committed
lint ImproperCTypes: add recursion limit
1 parent efe5695 commit a37e0c5

File tree

3 files changed

+81
-44
lines changed

3 files changed

+81
-44
lines changed

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 63 additions & 29 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

@@ -85,11 +86,6 @@ enum CItemKind {
8586
Definition,
8687
}
8788

88-
struct ImproperCTypesVisitor<'a, 'tcx> {
89-
cx: &'a LateContext<'tcx>,
90-
cache: FxHashSet<Ty<'tcx>>,
91-
}
92-
9389
#[derive(Clone, Debug)]
9490
struct FfiUnsafeReason<'tcx> {
9591
ty: Ty<'tcx>,
@@ -388,9 +384,52 @@ impl CTypesVisitorState {
388384
}
389385
}
390386

387+
/// visitor structure responsible for checking the actual FFI-safety
388+
/// of a given type
389+
struct ImproperCTypesVisitor<'a, 'tcx> {
390+
cx: &'a LateContext<'tcx>,
391+
/// to prevent problems with recursive types, add a types-in-check cache
392+
/// and a depth counter
393+
recursion_limiter: RefCell<(FxHashSet<Ty<'tcx>>, usize)>,
394+
}
395+
396+
/// structure similar to a mutex guard, allocated for each type in-check
397+
/// to let the ImproperCTypesVisitor know the current depth of the checking process
398+
struct ImproperCTypesVisitorDepthGuard<'a, 'tcx, 'v>(&'v ImproperCTypesVisitor<'a, 'tcx>);
399+
400+
impl<'a, 'tcx, 'v> Drop for ImproperCTypesVisitorDepthGuard<'a, 'tcx, 'v> {
401+
fn drop(&mut self) {
402+
let mut limiter_guard = self.0.recursion_limiter.borrow_mut();
403+
let (_, ref mut depth) = *limiter_guard;
404+
*depth -= 1;
405+
}
406+
}
407+
391408
impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
409+
fn new(cx: &'a LateContext<'tcx>) -> Self {
410+
Self { cx, recursion_limiter: RefCell::new((FxHashSet::default(), 0)) }
411+
}
412+
413+
/// Protect against infinite recursion, for example
414+
/// `struct S(*mut S);`, or issue #130310.
415+
fn can_enter_type<'v>(
416+
&'v self,
417+
ty: Ty<'tcx>,
418+
) -> Result<ImproperCTypesVisitorDepthGuard<'a, 'tcx, 'v>, FfiResult<'tcx>> {
419+
// panic unlikely: this non-recursive function is the only place that
420+
// borrows the refcell, outside of ImproperCTypesVisitorDepthGuard::drop()
421+
let mut limiter_guard = self.recursion_limiter.borrow_mut();
422+
let (ref mut cache, ref mut depth) = *limiter_guard;
423+
if (!cache.insert(ty)) || *depth >= 1024 {
424+
Err(FfiResult::FfiSafe)
425+
} else {
426+
*depth += 1;
427+
Ok(ImproperCTypesVisitorDepthGuard(self))
428+
}
429+
}
430+
392431
/// Checks whether an `extern "ABI" fn` function pointer is indeed FFI-safe to call
393-
fn visit_fnptr(&mut self, mode: CItemKind, ty: Ty<'tcx>, sig: Sig<'tcx>) -> FfiResult<'tcx> {
432+
fn visit_fnptr(&self, mode: CItemKind, ty: Ty<'tcx>, sig: Sig<'tcx>) -> FfiResult<'tcx> {
394433
use FfiResult::*;
395434
debug_assert!(!sig.abi().is_rustic_abi());
396435
let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
@@ -428,7 +467,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
428467

429468
/// Checks if a simple numeric (int, float) type has an actual portable definition
430469
/// for the compile target
431-
fn visit_numeric(&mut self, _ty: Ty<'tcx>) -> FfiResult<'tcx> {
470+
fn visit_numeric(&self, _ty: Ty<'tcx>) -> FfiResult<'tcx> {
432471
// FIXME: for now, this is very incomplete, and seems to assume a x86_64 target
433472
return FfiResult::FfiSafe;
434473
// match ty.kind() {
@@ -439,7 +478,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
439478
}
440479

441480
/// Return the right help for Cstring and Cstr-linked unsafety
442-
fn visit_cstr(&mut self, outer_ty: Option<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
481+
fn visit_cstr(&self, outer_ty: Option<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
443482
debug_assert!(matches!(ty.kind(), ty::Adt(def, _)
444483
if matches!(
445484
self.cx.tcx.get_diagnostic_name(def.did()),
@@ -470,7 +509,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
470509

471510
/// Checks if the given indirection (box,ref,pointer) is "ffi-safe"
472511
fn visit_indirection(
473-
&mut self,
512+
&self,
474513
state: CTypesVisitorState,
475514
outer_ty: Option<Ty<'tcx>>,
476515
ty: Ty<'tcx>,
@@ -610,7 +649,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
610649

611650
/// Checks if the given `VariantDef`'s field types are "ffi-safe".
612651
fn visit_variant_fields(
613-
&mut self,
652+
&self,
614653
state: CTypesVisitorState,
615654
ty: Ty<'tcx>,
616655
def: ty::AdtDef<'tcx>,
@@ -669,7 +708,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
669708
}
670709

671710
fn visit_struct_union(
672-
&mut self,
711+
&self,
673712
state: CTypesVisitorState,
674713
ty: Ty<'tcx>,
675714
def: ty::AdtDef<'tcx>,
@@ -725,7 +764,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
725764
}
726765

727766
fn visit_enum(
728-
&mut self,
767+
&self,
729768
state: CTypesVisitorState,
730769
ty: Ty<'tcx>,
731770
def: ty::AdtDef<'tcx>,
@@ -789,23 +828,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
789828
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
790829
/// representation which can be exported to C code).
791830
fn visit_type(
792-
&mut self,
831+
&self,
793832
state: CTypesVisitorState,
794833
outer_ty: Option<Ty<'tcx>>,
795834
ty: Ty<'tcx>,
796835
) -> FfiResult<'tcx> {
797836
use FfiResult::*;
798837

838+
let _depth_guard = match self.can_enter_type(ty) {
839+
Ok(guard) => guard,
840+
Err(ffi_res) => return ffi_res,
841+
};
799842
let tcx = self.cx.tcx;
800843

801-
// Protect against infinite recursion, for example
802-
// `struct S(*mut S);`.
803-
// FIXME: A recursion limit is necessary as well, for irregular
804-
// recursive types.
805-
if !self.cache.insert(ty) {
806-
return FfiSafe;
807-
}
808-
809844
match *ty.kind() {
810845
ty::Adt(def, args) => {
811846
if let Some(inner_ty) = ty.boxed_ty() {
@@ -1004,7 +1039,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10041039
}
10051040
}
10061041

1007-
fn check_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1042+
fn check_for_opaque_ty(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> {
10081043
struct ProhibitOpaqueTypes;
10091044
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
10101045
type Result = ControlFlow<Ty<'tcx>>;
@@ -1029,7 +1064,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10291064
}
10301065
}
10311066

1032-
fn check_for_type(&mut self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1067+
fn check_for_type(&self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
10331068
let ty = normalize_if_possible(self.cx, ty);
10341069

10351070
match self.check_for_opaque_ty(ty) {
@@ -1039,7 +1074,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10391074
self.visit_type(state, None, ty)
10401075
}
10411076

1042-
fn check_for_fnptr(&mut self, mode: CItemKind, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1077+
fn check_for_fnptr(&self, mode: CItemKind, ty: Ty<'tcx>) -> FfiResult<'tcx> {
10431078
let ty = normalize_if_possible(self.cx, ty);
10441079

10451080
match self.check_for_opaque_ty(ty) {
@@ -1181,8 +1216,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
11811216
all_types
11821217
.map(|(fn_ptr_ty, span)| {
11831218
// FIXME this will probably lead to error deduplication: fix this
1184-
let mut visitor =
1185-
ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() };
1219+
let visitor = ImproperCTypesVisitor::new(self.cx);
11861220
let ffi_res = visitor.check_for_fnptr(fn_mode, fn_ptr_ty);
11871221
(span, ffi_res)
11881222
})
@@ -1215,7 +1249,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
12151249
/// Check that an extern "ABI" static variable is of a ffi-safe type
12161250
fn check_foreign_static(&self, id: hir::OwnerId, span: Span) {
12171251
let ty = self.cx.tcx.type_of(id).instantiate_identity();
1218-
let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() };
1252+
let visitor = ImproperCTypesVisitor::new(self.cx);
12191253
let ffi_res = visitor.check_for_type(CTypesVisitorState::StaticTy, ty);
12201254
self.process_ffi_result(span, ffi_res, CItemKind::Declaration);
12211255
}
@@ -1231,7 +1265,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
12311265
let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
12321266

12331267
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1234-
let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() };
1268+
let visitor = ImproperCTypesVisitor::new(self.cx);
12351269
let visit_state = match fn_mode {
12361270
CItemKind::Definition => CTypesVisitorState::ArgumentTyInDefinition,
12371271
CItemKind::Declaration => CTypesVisitorState::ArgumentTyInDeclaration,
@@ -1241,7 +1275,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
12411275
}
12421276

12431277
if let hir::FnRetTy::Return(ret_hir) = decl.output {
1244-
let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() };
1278+
let visitor = ImproperCTypesVisitor::new(self.cx);
12451279
let visit_state = match fn_mode {
12461280
CItemKind::Definition => CTypesVisitorState::ReturnTyInDefinition,
12471281
CItemKind::Declaration => CTypesVisitorState::ReturnTyInDeclaration,

tests/crashes/130310.rs

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

0 commit comments

Comments
 (0)