Skip to content

Commit 8ea1a53

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

File tree

6 files changed

+58
-19
lines changed

6 files changed

+58
-19
lines changed

Diff for: compiler/rustc_lint/messages.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,9 @@ lint_extern_without_abi = extern declarations without an explicit ABI are deprec
276276
.label = ABI should be specified here
277277
.help = the default ABI is {$default_abi}
278278
279+
lint_ffi_safety_recursion_limit_reached =
280+
reached recursion limit while checking type `{$ty}` for FFI safety
281+
279282
lint_for_loops_over_fallibles =
280283
for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement
281284
.suggestion = consider using `if let` to clear intent

Diff for: compiler/rustc_lint/src/errors.rs

+9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use rustc_errors::codes::*;
22
use rustc_errors::{Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic};
33
use rustc_macros::{Diagnostic, Subdiagnostic};
4+
use rustc_middle::ty::Ty;
45
use rustc_session::lint::Level;
56
use rustc_span::{Span, Symbol};
67

@@ -110,3 +111,11 @@ pub(crate) struct CheckNameUnknownTool<'a> {
110111
#[subdiagnostic]
111112
pub sub: RequestedLevel<'a>,
112113
}
114+
115+
#[derive(Diagnostic)]
116+
#[diag(lint_ffi_safety_recursion_limit_reached)]
117+
pub(crate) struct RecursionLimitReached<'tcx> {
118+
pub ty: Ty<'tcx>,
119+
#[primary_span]
120+
pub span: Span,
121+
}

Diff for: compiler/rustc_lint/src/types.rs

+19-4
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ use rustc_target::spec::abi::Abi as SpecAbi;
1818
use tracing::debug;
1919
use {rustc_ast as ast, rustc_attr as attr, rustc_hir as hir};
2020

21+
use crate::errors::RecursionLimitReached;
2122
use crate::lints::{
2223
AmbiguousWidePointerComparisons, AmbiguousWidePointerComparisonsAddrMetadataSuggestion,
2324
AmbiguousWidePointerComparisonsAddrSuggestion, AtomicOrderingFence, AtomicOrderingLoad,
@@ -991,12 +992,15 @@ struct CTypesVisitorState<'tcx> {
991992
/// The original type being checked, before we recursed
992993
/// to any other types it contains.
993994
base_ty: Ty<'tcx>,
995+
/// Number of times we recursed while checking the type
996+
recursion_depth: usize,
994997
}
995998

996999
enum FfiResult<'tcx> {
9971000
FfiSafe,
9981001
FfiPhantom(Ty<'tcx>),
9991002
FfiUnsafe { ty: Ty<'tcx>, reason: DiagMessage, help: Option<DiagMessage> },
1003+
RecursionLimitReached,
10001004
}
10011005

10021006
pub(crate) fn nonnull_optimization_guaranteed<'tcx>(
@@ -1270,7 +1274,7 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12701274
// `()` fields are FFI-safe!
12711275
FfiUnsafe { ty, .. } if ty.is_unit() => false,
12721276
FfiPhantom(..) => true,
1273-
r @ FfiUnsafe { .. } => return r,
1277+
r => return r,
12741278
}
12751279
}
12761280

@@ -1296,12 +1300,19 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12961300

12971301
// Protect against infinite recursion, for example
12981302
// `struct S(*mut S);`.
1299-
// FIXME: A recursion limit is necessary as well, for irregular
1300-
// recursive types.
13011303
if !acc.cache.insert(ty) {
13021304
return FfiSafe;
13031305
}
13041306

1307+
// Additional recursion check for more complex types like
1308+
// `struct A<T> { v: *const A<A<T>>, ... }` for which the
1309+
// cache check above won't be enough (fixes #130310)
1310+
if !tcx.recursion_limit().value_within_limit(acc.recursion_depth) {
1311+
return RecursionLimitReached;
1312+
}
1313+
1314+
acc.recursion_depth += 1;
1315+
13051316
match *ty.kind() {
13061317
ty::Adt(def, args) => {
13071318
if let Some(boxed) = ty.boxed_ty()
@@ -1644,7 +1655,8 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
16441655
return;
16451656
}
16461657

1647-
let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty };
1658+
let mut acc =
1659+
CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty, recursion_depth: 0 };
16481660
match self.check_type_for_ffi(&mut acc, ty) {
16491661
FfiResult::FfiSafe => {}
16501662
FfiResult::FfiPhantom(ty) => {
@@ -1658,6 +1670,9 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
16581670
FfiResult::FfiUnsafe { ty, reason, help } => {
16591671
self.emit_ffi_unsafe_type_lint(ty, sp, reason, help);
16601672
}
1673+
FfiResult::RecursionLimitReached => {
1674+
self.cx.tcx.dcx().emit_err(RecursionLimitReached { ty, span: sp });
1675+
}
16611676
}
16621677
}
16631678

Diff for: tests/crashes/130310.rs

-15
This file was deleted.
+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
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+
use std::marker::PhantomData;
7+
8+
#[repr(C)]
9+
struct A<T> {
10+
a: *const A<A<T>>, // Recursive because of this field
11+
p: PhantomData<T>,
12+
}
13+
14+
extern "C" {
15+
fn f(a: *const A<()>);
16+
//~^ ERROR reached recursion limit while checking type `*const A<()>` for FFI safety
17+
}
18+
19+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: reached recursion limit while checking type `*const A<()>` for FFI safety
2+
--> $DIR/improper-types-stack-overflow-130310.rs:15:13
3+
|
4+
LL | fn f(a: *const A<()>);
5+
| ^^^^^^^^^^^^
6+
7+
error: aborting due to 1 previous error
8+

0 commit comments

Comments
 (0)