Skip to content

Commit 7160447

Browse files
committed
Add recursion limit to FFI safety lint
Fixes stack overflow in the case of recursive types
1 parent e2dc1a1 commit 7160447

File tree

5 files changed

+50
-18
lines changed

5 files changed

+50
-18
lines changed

compiler/rustc_lint/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -395,6 +395,8 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent
395395
lint_improper_ctypes_pat_help = consider using the base type instead
396396
397397
lint_improper_ctypes_pat_reason = pattern types have no C equivalent
398+
399+
lint_improper_ctypes_recursion_limit_reached = type is infinitely recursive
398400
lint_improper_ctypes_slice_help = consider using a raw pointer instead
399401
400402
lint_improper_ctypes_slice_reason = slices have no C equivalent

compiler/rustc_lint/src/types.rs

+17-3
Original file line numberDiff line numberDiff line change
@@ -991,6 +991,8 @@ struct CTypesVisitorState<'tcx> {
991991
/// The original type being checked, before we recursed
992992
/// to any other types it contains.
993993
base_ty: Ty<'tcx>,
994+
/// Number of times we recursed while checking the type
995+
recursion_depth: usize,
994996
}
995997

996998
enum FfiResult<'tcx> {
@@ -1296,12 +1298,23 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12961298

12971299
// Protect against infinite recursion, for example
12981300
// `struct S(*mut S);`.
1299-
// FIXME: A recursion limit is necessary as well, for irregular
1300-
// recursive types.
13011301
if !acc.cache.insert(ty) {
13021302
return FfiSafe;
13031303
}
13041304

1305+
// Additional recursion check for more complex types like
1306+
// `struct A<T> { v: *const A<A<T>>, ... }` for which the
1307+
// cache check above won't be enough (fixes #130310)
1308+
if !tcx.recursion_limit().value_within_limit(acc.recursion_depth) {
1309+
return FfiUnsafe {
1310+
ty: acc.base_ty,
1311+
reason: fluent::lint_improper_ctypes_recursion_limit_reached,
1312+
help: None,
1313+
};
1314+
}
1315+
1316+
acc.recursion_depth += 1;
1317+
13051318
match *ty.kind() {
13061319
ty::Adt(def, args) => {
13071320
if let Some(boxed) = ty.boxed_ty()
@@ -1644,7 +1657,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
16441657
return;
16451658
}
16461659

1647-
let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty };
1660+
let mut acc =
1661+
CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty, recursion_depth: 0 };
16481662
match self.check_type_for_ffi(&mut acc, ty) {
16491663
FfiResult::FfiSafe => {}
16501664
FfiResult::FfiPhantom(ty) => {

tests/crashes/130310.rs

-15
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// Regression test for #130310
2+
// Tests that we do not fall into infinite
3+
// recursion while checking FFI safety of
4+
// recursive types like `A<T>` below
5+
6+
//@ build-pass
7+
use std::marker::PhantomData;
8+
9+
#[repr(C)]
10+
struct A<T> {
11+
a: *const A<A<T>>, // Recursive because of this field
12+
p: PhantomData<T>,
13+
}
14+
15+
extern "C" {
16+
fn f(a: *const A<()>);
17+
//~^ WARN `extern` block uses type `*const A<()>`, which is not FFI-safe
18+
}
19+
20+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
warning: `extern` block uses type `*const A<()>`, which is not FFI-safe
2+
--> $DIR/improper-types-stack-overflow-130310.rs:16:13
3+
|
4+
LL | fn f(a: *const A<()>);
5+
| ^^^^^^^^^^^^ not FFI-safe
6+
|
7+
= note: type is infinitely recursive
8+
= note: `#[warn(improper_ctypes)]` on by default
9+
10+
warning: 1 warning emitted
11+

0 commit comments

Comments
 (0)