Skip to content

Commit c74c323

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

File tree

4 files changed

+76
-23
lines changed

4 files changed

+76
-23
lines changed

compiler/rustc_lint/src/types/improper_ctypes.rs

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,8 @@ struct VisitorState {
329329
persistent_flags: PersistentStateFlags,
330330
/// Flags describing both the immediate and overall context in which the current mir::Ty is
331331
ephemeral_flags: EphemeralStateFlags,
332+
/// Type recursion depth, to prevent infinite recursion
333+
depth: usize,
332334
}
333335

334336
impl PersistentStateFlags {
@@ -383,6 +385,7 @@ impl VisitorState {
383385
Self {
384386
persistent_flags: self.persistent_flags,
385387
ephemeral_flags: EphemeralStateFlags::from_outer_ty(current_ty),
388+
depth: self.depth + 1,
386389
}
387390
}
388391
fn get_next_in_fnptr<'tcx>(&self, current_ty: Ty<'tcx>, is_ret: bool) -> Self {
@@ -394,12 +397,13 @@ impl VisitorState {
394397
PersistentStateFlags::ARGUMENT_TY_IN_FNPTR
395398
},
396399
ephemeral_flags: EphemeralStateFlags::from_outer_ty(current_ty),
400+
depth: self.depth + 1,
397401
}
398402
}
399403

400404
/// Generate the state for an "outermost" type that needs to be checked
401405
fn entry_point(persistent_flags: PersistentStateFlags) -> Self {
402-
Self { persistent_flags, ephemeral_flags: EphemeralStateFlags::NO_OUTER_TY }
406+
Self { persistent_flags, ephemeral_flags: EphemeralStateFlags::NO_OUTER_TY, depth: 0 }
403407
}
404408

405409
/// Get the proper visitor state for a given function's arguments.
@@ -733,9 +737,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
733737

734738
// Protect against infinite recursion, for example
735739
// `struct S(*mut S);`.
736-
// FIXME: A recursion limit is necessary as well, for irregular
737-
// recursive types.
738-
if !self.cache.insert(ty) {
740+
if !(self.cache.insert(ty) && self.cx.tcx.recursion_limit().value_within_limit(state.depth))
741+
{
739742
return FfiSafe;
740743
}
741744

@@ -956,20 +959,25 @@ impl<'tcx> ImproperCTypesLint {
956959
fn_mode: CItemKind,
957960
) {
958961
struct FnPtrFinder<'tcx> {
962+
current_depth: usize,
963+
depths: Vec<usize>,
959964
spans: Vec<Span>,
960965
tys: Vec<Ty<'tcx>>,
961966
}
962967

963968
impl<'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'tcx> {
964969
fn visit_ty(&mut self, ty: &'_ hir::Ty<'_, AmbigArg>) {
965970
debug!(?ty);
971+
self.current_depth += 1;
966972
if let hir::TyKind::FnPtr(hir::FnPtrTy { abi, .. }) = ty.kind
967973
&& !abi.is_rustic_abi()
968974
{
975+
self.depths.push(self.current_depth);
969976
self.spans.push(ty.span);
970977
}
971978

972979
hir::intravisit::walk_ty(self, ty);
980+
self.current_depth -= 1;
973981
}
974982
}
975983

@@ -987,15 +995,24 @@ impl<'tcx> ImproperCTypesLint {
987995
}
988996
}
989997

990-
let mut visitor = FnPtrFinder { spans: Vec::new(), tys: Vec::new() };
998+
let mut visitor = FnPtrFinder {
999+
spans: Vec::new(),
1000+
tys: Vec::new(),
1001+
depths: Vec::new(),
1002+
current_depth: 0,
1003+
};
9911004
ty.visit_with(&mut visitor);
9921005
visitor.visit_ty_unambig(hir_ty);
9931006

994-
let all_types = iter::zip(visitor.tys.drain(..), visitor.spans.drain(..));
995-
for (fn_ptr_ty, span) in all_types {
1007+
let all_types = iter::zip(
1008+
visitor.depths.drain(..),
1009+
iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)),
1010+
);
1011+
for (depth, (fn_ptr_ty, span)) in all_types {
9961012
let mut visitor = ImproperCTypesVisitor::new(cx, fn_ptr_ty, fn_mode);
1013+
let bridge_state = VisitorState { depth, ..state };
9971014
// FIXME(ctypes): make a check_for_fnptr
998-
let ffi_res = visitor.check_type(state, fn_ptr_ty);
1015+
let ffi_res = visitor.check_type(bridge_state, fn_ptr_ty);
9991016

10001017
self.process_ffi_result(cx, span, ffi_res, fn_mode);
10011018
}

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() {}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
//@ check-pass
2+
3+
#![recursion_limit = "5"]
4+
#![allow(unused)]
5+
#![deny(improper_ctypes)]
6+
7+
#[repr(C)]
8+
struct F1(*const ());
9+
#[repr(C)]
10+
struct F2(*const ());
11+
#[repr(C)]
12+
struct F3(*const ());
13+
#[repr(C)]
14+
struct F4(*const ());
15+
#[repr(C)]
16+
struct F5(*const ());
17+
#[repr(C)]
18+
struct F6(*const ());
19+
20+
#[repr(C)]
21+
struct B {
22+
f1: F1,
23+
f2: F2,
24+
f3: F3,
25+
f4: F4,
26+
f5: F5,
27+
f6: F6,
28+
}
29+
30+
extern "C" fn foo(_: B) {}
31+
32+
fn main() {}

0 commit comments

Comments
 (0)