Skip to content

Commit f794b6d

Browse files
committed
lint ImproperCTypes: add recursion limit
1 parent 4aa6a20 commit f794b6d

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

@@ -86,11 +87,6 @@ enum CItemKind {
8687
Definition,
8788
}
8889

89-
struct ImproperCTypesVisitor<'a, 'tcx> {
90-
cx: &'a LateContext<'tcx>,
91-
cache: FxHashSet<Ty<'tcx>>,
92-
}
93-
9490
#[derive(Clone, Debug)]
9591
struct FfiUnsafeReason<'tcx> {
9692
ty: Ty<'tcx>,
@@ -386,9 +382,52 @@ impl CTypesVisitorState {
386382
}
387383
}
388384

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

427466
/// Checks if a simple numeric (int, float) type has an actual portable definition
428467
/// for the compile target
429-
fn visit_numeric(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> {
468+
fn visit_numeric(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> {
430469
// FIXME: for now, this is very incomplete, and seems to assume a x86_64 target
431470
match ty.kind() {
432471
ty::Int(ty::IntTy::I128) | ty::Uint(ty::UintTy::U128) => {
@@ -438,7 +477,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
438477
}
439478

440479
/// Return the right help for Cstring and Cstr-linked unsafety
441-
fn visit_cstr(&mut self, outer_ty: Option<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
480+
fn visit_cstr(&self, outer_ty: Option<Ty<'tcx>>, ty: Ty<'tcx>) -> FfiResult<'tcx> {
442481
debug_assert!(matches!(ty.kind(), ty::Adt(def, _)
443482
if matches!(
444483
self.cx.tcx.get_diagnostic_name(def.did()),
@@ -469,7 +508,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
469508

470509
/// Checks if the given indirection (box,ref,pointer) is "ffi-safe"
471510
fn visit_indirection(
472-
&mut self,
511+
&self,
473512
state: CTypesVisitorState,
474513
outer_ty: Option<Ty<'tcx>>,
475514
ty: Ty<'tcx>,
@@ -609,7 +648,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
609648

610649
/// Checks if the given `VariantDef`'s field types are "ffi-safe".
611650
fn visit_variant_fields(
612-
&mut self,
651+
&self,
613652
state: CTypesVisitorState,
614653
ty: Ty<'tcx>,
615654
def: ty::AdtDef<'tcx>,
@@ -668,7 +707,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
668707
}
669708

670709
fn visit_struct_union(
671-
&mut self,
710+
&self,
672711
state: CTypesVisitorState,
673712
ty: Ty<'tcx>,
674713
def: ty::AdtDef<'tcx>,
@@ -724,7 +763,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
724763
}
725764

726765
fn visit_enum(
727-
&mut self,
766+
&self,
728767
state: CTypesVisitorState,
729768
ty: Ty<'tcx>,
730769
def: ty::AdtDef<'tcx>,
@@ -792,23 +831,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
792831
/// Checks if the given type is "ffi-safe" (has a stable, well-defined
793832
/// representation which can be exported to C code).
794833
fn visit_type(
795-
&mut self,
834+
&self,
796835
state: CTypesVisitorState,
797836
outer_ty: Option<Ty<'tcx>>,
798837
ty: Ty<'tcx>,
799838
) -> FfiResult<'tcx> {
800839
use FfiResult::*;
801840

841+
let _depth_guard = match self.can_enter_type(ty) {
842+
Ok(guard) => guard,
843+
Err(ffi_res) => return ffi_res,
844+
};
802845
let tcx = self.cx.tcx;
803846

804-
// Protect against infinite recursion, for example
805-
// `struct S(*mut S);`.
806-
// FIXME: A recursion limit is necessary as well, for irregular
807-
// recursive types.
808-
if !self.cache.insert(ty) {
809-
return FfiSafe;
810-
}
811-
812847
match *ty.kind() {
813848
ty::Adt(def, args) => {
814849
if let Some(inner_ty) = ty.boxed_ty() {
@@ -1007,7 +1042,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10071042
}
10081043
}
10091044

1010-
fn check_for_opaque_ty(&mut self, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1045+
fn check_for_opaque_ty(&self, ty: Ty<'tcx>) -> FfiResult<'tcx> {
10111046
struct ProhibitOpaqueTypes;
10121047
impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for ProhibitOpaqueTypes {
10131048
type Result = ControlFlow<Ty<'tcx>>;
@@ -1032,7 +1067,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10321067
}
10331068
}
10341069

1035-
fn check_for_type(&mut self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1070+
fn check_for_type(&self, state: CTypesVisitorState, ty: Ty<'tcx>) -> FfiResult<'tcx> {
10361071
let ty = normalize_if_possible(self.cx, ty);
10371072

10381073
match self.check_for_opaque_ty(ty) {
@@ -1042,7 +1077,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
10421077
self.visit_type(state, None, ty)
10431078
}
10441079

1045-
fn check_for_fnptr(&mut self, mode: CItemKind, ty: Ty<'tcx>) -> FfiResult<'tcx> {
1080+
fn check_for_fnptr(&self, mode: CItemKind, ty: Ty<'tcx>) -> FfiResult<'tcx> {
10461081
let ty = normalize_if_possible(self.cx, ty);
10471082

10481083
match self.check_for_opaque_ty(ty) {
@@ -1184,8 +1219,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
11841219
all_types
11851220
.map(|(fn_ptr_ty, span)| {
11861221
// FIXME this will probably lead to error deduplication: fix this
1187-
let mut visitor =
1188-
ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() };
1222+
let visitor = ImproperCTypesVisitor::new(self.cx);
11891223
let ffi_res = visitor.check_for_fnptr(fn_mode, fn_ptr_ty);
11901224
(span, ffi_res)
11911225
})
@@ -1218,7 +1252,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
12181252
/// Check that an extern "ABI" static variable is of a ffi-safe type
12191253
fn check_foreign_static(&self, id: hir::OwnerId, span: Span) {
12201254
let ty = self.cx.tcx.type_of(id).instantiate_identity();
1221-
let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() };
1255+
let visitor = ImproperCTypesVisitor::new(self.cx);
12221256
let ffi_res = visitor.check_for_type(CTypesVisitorState::StaticTy, ty);
12231257
self.process_ffi_result(span, ffi_res, CItemKind::Declaration);
12241258
}
@@ -1234,7 +1268,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
12341268
let sig = self.cx.tcx.instantiate_bound_regions_with_erased(sig);
12351269

12361270
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1237-
let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() };
1271+
let visitor = ImproperCTypesVisitor::new(self.cx);
12381272
let visit_state = match fn_mode {
12391273
CItemKind::Definition => CTypesVisitorState::ArgumentTyInDefinition,
12401274
CItemKind::Declaration => CTypesVisitorState::ArgumentTyInDeclaration,
@@ -1244,7 +1278,7 @@ impl<'c, 'tcx> ImproperCTypesLint<'c, 'tcx> {
12441278
}
12451279

12461280
if let hir::FnRetTy::Return(ret_hir) = decl.output {
1247-
let mut visitor = ImproperCTypesVisitor { cx: self.cx, cache: FxHashSet::default() };
1281+
let visitor = ImproperCTypesVisitor::new(self.cx);
12481282
let visit_state = match fn_mode {
12491283
CItemKind::Definition => CTypesVisitorState::ReturnTyInDefinition,
12501284
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)