Add recursion limit to FFI safety lint

Fixes stack overflow in the case of recursive types
This commit is contained in:
Gurinder Singh 2024-09-20 18:57:59 +05:30
parent e2dc1a1c0f
commit 716044751b
5 changed files with 50 additions and 18 deletions

View File

@ -395,6 +395,8 @@ lint_improper_ctypes_opaque = opaque types have no C equivalent
lint_improper_ctypes_pat_help = consider using the base type instead
lint_improper_ctypes_pat_reason = pattern types have no C equivalent
lint_improper_ctypes_recursion_limit_reached = type is infinitely recursive
lint_improper_ctypes_slice_help = consider using a raw pointer instead
lint_improper_ctypes_slice_reason = slices have no C equivalent

View File

@ -991,6 +991,8 @@ struct CTypesVisitorState<'tcx> {
/// The original type being checked, before we recursed
/// to any other types it contains.
base_ty: Ty<'tcx>,
/// Number of times we recursed while checking the type
recursion_depth: usize,
}
enum FfiResult<'tcx> {
@ -1296,12 +1298,23 @@ fn check_type_for_ffi(
// Protect against infinite recursion, for example
// `struct S(*mut S);`.
// FIXME: A recursion limit is necessary as well, for irregular
// recursive types.
if !acc.cache.insert(ty) {
return FfiSafe;
}
// Additional recursion check for more complex types like
// `struct A<T> { v: *const A<A<T>>, ... }` for which the
// cache check above won't be enough (fixes #130310)
if !tcx.recursion_limit().value_within_limit(acc.recursion_depth) {
return FfiUnsafe {
ty: acc.base_ty,
reason: fluent::lint_improper_ctypes_recursion_limit_reached,
help: None,
};
}
acc.recursion_depth += 1;
match *ty.kind() {
ty::Adt(def, args) => {
if let Some(boxed) = ty.boxed_ty()
@ -1644,7 +1657,8 @@ fn check_type_for_ffi_and_report_errors(
return;
}
let mut acc = CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty };
let mut acc =
CTypesVisitorState { cache: FxHashSet::default(), base_ty: ty, recursion_depth: 0 };
match self.check_type_for_ffi(&mut acc, ty) {
FfiResult::FfiSafe => {}
FfiResult::FfiPhantom(ty) => {

View File

@ -1,15 +0,0 @@
//@ known-bug: rust-lang/rust#130310
use std::marker::PhantomData;
#[repr(C)]
struct A<T> {
a: *const A<A<T>>,
p: PhantomData<T>,
}
extern "C" {
fn f(a: *const A<()>);
}
fn main() {}

View File

@ -0,0 +1,20 @@
// Regression test for #130310
// Tests that we do not fall into infinite
// recursion while checking FFI safety of
// recursive types like `A<T>` below
//@ build-pass
use std::marker::PhantomData;
#[repr(C)]
struct A<T> {
a: *const A<A<T>>, // Recursive because of this field
p: PhantomData<T>,
}
extern "C" {
fn f(a: *const A<()>);
//~^ WARN `extern` block uses type `*const A<()>`, which is not FFI-safe
}
fn main() {}

View File

@ -0,0 +1,11 @@
warning: `extern` block uses type `*const A<()>`, which is not FFI-safe
--> $DIR/improper-types-stack-overflow-130310.rs:16:13
|
LL | fn f(a: *const A<()>);
| ^^^^^^^^^^^^ not FFI-safe
|
= note: type is infinitely recursive
= note: `#[warn(improper_ctypes)]` on by default
warning: 1 warning emitted