Skip to content

Commit 1795530

Browse files
authored
Rollup merge of rust-lang#108611 - davidtwco:issue-94223-external-abi-fn-ptr-in-internal-abi-fn, r=jackh726
lint/ctypes: ext. abi fn-ptr in internal abi fn Fixes rust-lang#94223. - In the improper ctypes lint, instead of skipping functions with internal ABIs, check that the signature doesn't contain any fn-ptr types with external ABIs that aren't FFI-safe. - When computing the ABI for fn-ptr types, remove an `unwrap` that assumed FFI-safe types in foreign fn-ptr types. - I'm not certain that this is the correct approach.
2 parents dbba594 + 3e7ca3e commit 1795530

File tree

7 files changed

+333
-22
lines changed

7 files changed

+333
-22
lines changed

compiler/rustc_lint/src/types.rs

+149-17
Original file line numberDiff line numberDiff line change
@@ -1250,17 +1250,45 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12501250
}
12511251
}
12521252

1253-
fn check_foreign_fn(&mut self, def_id: LocalDefId, decl: &hir::FnDecl<'_>) {
1253+
/// Check if a function's argument types and result type are "ffi-safe".
1254+
///
1255+
/// Argument types and the result type are checked for functions with external ABIs.
1256+
/// For functions with internal ABIs, argument types and the result type are walked to find
1257+
/// fn-ptr types that have external ABIs, as these still need checked.
1258+
fn check_maybe_foreign_fn(
1259+
&mut self,
1260+
abi: SpecAbi,
1261+
def_id: LocalDefId,
1262+
decl: &'tcx hir::FnDecl<'_>,
1263+
) {
12541264
let sig = self.cx.tcx.fn_sig(def_id).subst_identity();
12551265
let sig = self.cx.tcx.erase_late_bound_regions(sig);
12561266

1267+
let is_internal_abi = self.is_internal_abi(abi);
1268+
let check_ty = |this: &mut ImproperCTypesVisitor<'a, 'tcx>,
1269+
hir_ty: &'tcx hir::Ty<'_>,
1270+
ty: Ty<'tcx>,
1271+
is_return_type: bool| {
1272+
// If this function has an external ABI, then its arguments and return type should be
1273+
// checked..
1274+
if !is_internal_abi {
1275+
this.check_type_for_ffi_and_report_errors(hir_ty.span, ty, false, is_return_type);
1276+
return;
1277+
}
1278+
1279+
// ..but if this function has an internal ABI, then search the argument or return type
1280+
// for any fn-ptr types with external ABI, which should be checked..
1281+
for (fn_ptr_ty, span) in this.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
1282+
this.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, false, is_return_type);
1283+
}
1284+
};
1285+
12571286
for (input_ty, input_hir) in iter::zip(sig.inputs(), decl.inputs) {
1258-
self.check_type_for_ffi_and_report_errors(input_hir.span, *input_ty, false, false);
1287+
check_ty(self, input_hir, *input_ty, false);
12591288
}
12601289

12611290
if let hir::FnRetTy::Return(ref ret_hir) = decl.output {
1262-
let ret_ty = sig.output();
1263-
self.check_type_for_ffi_and_report_errors(ret_hir.span, ret_ty, false, true);
1291+
check_ty(self, ret_hir, sig.output(), true);
12641292
}
12651293
}
12661294

@@ -1275,28 +1303,134 @@ impl<'a, 'tcx> ImproperCTypesVisitor<'a, 'tcx> {
12751303
SpecAbi::Rust | SpecAbi::RustCall | SpecAbi::RustIntrinsic | SpecAbi::PlatformIntrinsic
12761304
)
12771305
}
1306+
1307+
/// Find any fn-ptr types with external ABIs in `ty`.
1308+
///
1309+
/// For example, `Option<extern "C" fn()>` returns `extern "C" fn()`
1310+
fn find_fn_ptr_ty_with_external_abi(
1311+
&self,
1312+
hir_ty: &hir::Ty<'tcx>,
1313+
ty: Ty<'tcx>,
1314+
) -> Vec<(Ty<'tcx>, Span)> {
1315+
struct FnPtrFinder<'parent, 'a, 'tcx> {
1316+
visitor: &'parent ImproperCTypesVisitor<'a, 'tcx>,
1317+
spans: Vec<Span>,
1318+
tys: Vec<Ty<'tcx>>,
1319+
}
1320+
1321+
impl<'parent, 'a, 'tcx> hir::intravisit::Visitor<'_> for FnPtrFinder<'parent, 'a, 'tcx> {
1322+
fn visit_ty(&mut self, ty: &'_ hir::Ty<'_>) {
1323+
debug!(?ty);
1324+
if let hir::TyKind::BareFn(hir::BareFnTy { abi, .. }) = ty.kind
1325+
&& !self.visitor.is_internal_abi(*abi)
1326+
{
1327+
self.spans.push(ty.span);
1328+
}
1329+
1330+
hir::intravisit::walk_ty(self, ty)
1331+
}
1332+
}
1333+
1334+
impl<'vis, 'a, 'tcx> ty::visit::TypeVisitor<TyCtxt<'tcx>> for FnPtrFinder<'vis, 'a, 'tcx> {
1335+
type BreakTy = Ty<'tcx>;
1336+
1337+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
1338+
if let ty::FnPtr(sig) = ty.kind() && !self.visitor.is_internal_abi(sig.abi()) {
1339+
self.tys.push(ty);
1340+
}
1341+
1342+
ty.super_visit_with(self)
1343+
}
1344+
}
1345+
1346+
let mut visitor = FnPtrFinder { visitor: &*self, spans: Vec::new(), tys: Vec::new() };
1347+
self.cx
1348+
.tcx
1349+
.try_normalize_erasing_regions(self.cx.param_env, ty)
1350+
.unwrap_or(ty)
1351+
.visit_with(&mut visitor);
1352+
hir::intravisit::Visitor::visit_ty(&mut visitor, hir_ty);
1353+
1354+
iter::zip(visitor.tys.drain(..), visitor.spans.drain(..)).collect()
1355+
}
12781356
}
12791357

12801358
impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDeclarations {
1281-
fn check_foreign_item(&mut self, cx: &LateContext<'_>, it: &hir::ForeignItem<'_>) {
1359+
fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, it: &hir::ForeignItem<'tcx>) {
12821360
let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Declaration };
12831361
let abi = cx.tcx.hir().get_foreign_abi(it.hir_id());
12841362

1285-
if !vis.is_internal_abi(abi) {
1286-
match it.kind {
1287-
hir::ForeignItemKind::Fn(ref decl, _, _) => {
1288-
vis.check_foreign_fn(it.owner_id.def_id, decl);
1289-
}
1290-
hir::ForeignItemKind::Static(ref ty, _) => {
1291-
vis.check_foreign_static(it.owner_id, ty.span);
1292-
}
1293-
hir::ForeignItemKind::Type => (),
1363+
match it.kind {
1364+
hir::ForeignItemKind::Fn(ref decl, _, _) => {
1365+
vis.check_maybe_foreign_fn(abi, it.owner_id.def_id, decl);
12941366
}
1367+
hir::ForeignItemKind::Static(ref ty, _) if !vis.is_internal_abi(abi) => {
1368+
vis.check_foreign_static(it.owner_id, ty.span);
1369+
}
1370+
hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => (),
12951371
}
12961372
}
12971373
}
12981374

1375+
impl ImproperCTypesDefinitions {
1376+
fn check_ty_maybe_containing_foreign_fnptr<'tcx>(
1377+
&mut self,
1378+
cx: &LateContext<'tcx>,
1379+
hir_ty: &'tcx hir::Ty<'_>,
1380+
ty: Ty<'tcx>,
1381+
) {
1382+
let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1383+
for (fn_ptr_ty, span) in vis.find_fn_ptr_ty_with_external_abi(hir_ty, ty) {
1384+
vis.check_type_for_ffi_and_report_errors(span, fn_ptr_ty, true, false);
1385+
}
1386+
}
1387+
}
1388+
1389+
/// `ImproperCTypesDefinitions` checks items outside of foreign items (e.g. stuff that isn't in
1390+
/// `extern "C" { }` blocks):
1391+
///
1392+
/// - `extern "<abi>" fn` definitions are checked in the same way as the
1393+
/// `ImproperCtypesDeclarations` visitor checks functions if `<abi>` is external (e.g. "C").
1394+
/// - All other items which contain types (e.g. other functions, struct definitions, etc) are
1395+
/// checked for extern fn-ptrs with external ABIs.
12991396
impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
1397+
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'tcx>) {
1398+
match item.kind {
1399+
hir::ItemKind::Static(ty, ..)
1400+
| hir::ItemKind::Const(ty, ..)
1401+
| hir::ItemKind::TyAlias(ty, ..) => {
1402+
self.check_ty_maybe_containing_foreign_fnptr(
1403+
cx,
1404+
ty,
1405+
cx.tcx.type_of(item.owner_id).subst_identity(),
1406+
);
1407+
}
1408+
// See `check_fn`..
1409+
hir::ItemKind::Fn(..) => {}
1410+
// See `check_field_def`..
1411+
hir::ItemKind::Union(..) | hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) => {}
1412+
// Doesn't define something that can contain a external type to be checked.
1413+
hir::ItemKind::Impl(..)
1414+
| hir::ItemKind::TraitAlias(..)
1415+
| hir::ItemKind::Trait(..)
1416+
| hir::ItemKind::OpaqueTy(..)
1417+
| hir::ItemKind::GlobalAsm(..)
1418+
| hir::ItemKind::ForeignMod { .. }
1419+
| hir::ItemKind::Mod(..)
1420+
| hir::ItemKind::Macro(..)
1421+
| hir::ItemKind::Use(..)
1422+
| hir::ItemKind::ExternCrate(..) => {}
1423+
}
1424+
}
1425+
1426+
fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx hir::FieldDef<'tcx>) {
1427+
self.check_ty_maybe_containing_foreign_fnptr(
1428+
cx,
1429+
field.ty,
1430+
cx.tcx.type_of(field.def_id).subst_identity(),
1431+
);
1432+
}
1433+
13001434
fn check_fn(
13011435
&mut self,
13021436
cx: &LateContext<'tcx>,
@@ -1315,9 +1449,7 @@ impl<'tcx> LateLintPass<'tcx> for ImproperCTypesDefinitions {
13151449
};
13161450

13171451
let mut vis = ImproperCTypesVisitor { cx, mode: CItemKind::Definition };
1318-
if !vis.is_internal_abi(abi) {
1319-
vis.check_foreign_fn(id, decl);
1320-
}
1452+
vis.check_maybe_foreign_fn(abi, id, decl);
13211453
}
13221454
}
13231455

compiler/rustc_target/src/abi/call/x86_64.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ fn reg_component(cls: &[Option<Class>], i: &mut usize, size: Size) -> Option<Reg
153153
}
154154
}
155155

156-
fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
156+
fn cast_target(cls: &[Option<Class>], size: Size) -> Option<CastTarget> {
157157
let mut i = 0;
158-
let lo = reg_component(cls, &mut i, size).unwrap();
158+
let lo = reg_component(cls, &mut i, size)?;
159159
let offset = Size::from_bytes(8) * (i as u64);
160160
let mut target = CastTarget::from(lo);
161161
if size > offset {
@@ -164,7 +164,7 @@ fn cast_target(cls: &[Option<Class>], size: Size) -> CastTarget {
164164
}
165165
}
166166
assert_eq!(reg_component(cls, &mut i, Size::ZERO), None);
167-
target
167+
Some(target)
168168
}
169169

170170
const MAX_INT_REGS: usize = 6; // RDI, RSI, RDX, RCX, R8, R9
@@ -227,7 +227,9 @@ where
227227
// split into sized chunks passed individually
228228
if arg.layout.is_aggregate() {
229229
let size = arg.layout.size;
230-
arg.cast_to(cast_target(cls, size))
230+
if let Some(cast_target) = cast_target(cls, size) {
231+
arg.cast_to(cast_target);
232+
}
231233
} else {
232234
arg.extend_integer_width_to(32);
233235
}

tests/ui/abi/foreign/foreign-fn-with-byval.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
// run-pass
2-
#![allow(improper_ctypes)]
2+
#![allow(improper_ctypes, improper_ctypes_definitions)]
33

44
// ignore-wasm32-bare no libc to test ffi with
55

tests/ui/abi/issue-94223.rs

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
// check-pass
2+
#![allow(improper_ctypes_definitions)]
3+
#![crate_type = "lib"]
4+
5+
// Check that computing the fn abi for `bad`, with a external ABI fn ptr that is not FFI-safe, does
6+
// not ICE.
7+
8+
pub fn bad(f: extern "C" fn([u8])) {}

tests/ui/hashmap/hashmap-memory.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-pass
22

3+
#![allow(improper_ctypes_definitions)]
34
#![allow(non_camel_case_types)]
45
#![allow(dead_code)]
56
#![allow(unused_mut)]

tests/ui/lint/lint-ctypes-94223.rs

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#![crate_type = "lib"]
2+
#![deny(improper_ctypes_definitions)]
3+
4+
pub fn bad(f: extern "C" fn([u8])) {}
5+
//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
6+
7+
pub fn bad_twice(f: Result<extern "C" fn([u8]), extern "C" fn([u8])>) {}
8+
//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
9+
//~^^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
10+
11+
struct BadStruct(extern "C" fn([u8]));
12+
//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
13+
14+
enum BadEnum {
15+
A(extern "C" fn([u8])),
16+
//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
17+
}
18+
19+
enum BadUnion {
20+
A(extern "C" fn([u8])),
21+
//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
22+
}
23+
24+
type Foo = extern "C" fn([u8]);
25+
//~^ ERROR `extern` fn uses type `[u8]`, which is not FFI-safe
26+
27+
pub struct FfiUnsafe;
28+
29+
#[allow(improper_ctypes_definitions)]
30+
extern "C" fn f(_: FfiUnsafe) {
31+
unimplemented!()
32+
}
33+
34+
pub static BAD: extern "C" fn(FfiUnsafe) = f;
35+
//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe
36+
37+
pub static BAD_TWICE: Result<extern "C" fn(FfiUnsafe), extern "C" fn(FfiUnsafe)> = Ok(f);
38+
//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe
39+
//~^^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe
40+
41+
pub const BAD_CONST: extern "C" fn(FfiUnsafe) = f;
42+
//~^ ERROR `extern` fn uses type `FfiUnsafe`, which is not FFI-safe

0 commit comments

Comments
 (0)