Skip to content

Commit

Permalink
Check ABI target compatibility for function pointers
Browse files Browse the repository at this point in the history
This check was previously only performed on functions not function pointers.

Co-authored-by: Folkert <folkert@folkertdev.nl>
  • Loading branch information
tdittr and folkertdev committed Sep 23, 2024
1 parent 66b0b29 commit 47293c1
Show file tree
Hide file tree
Showing 20 changed files with 991 additions and 82 deletions.
24 changes: 14 additions & 10 deletions compiler/rustc_hir_analysis/src/check/check.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ use rustc_hir::Node;
use rustc_hir::def::{CtorKind, DefKind};
use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt};
use rustc_infer::traits::Obligation;
use rustc_lint_defs::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
use rustc_lint_defs::builtin::{
REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS, UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
};
use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
use rustc_middle::middle::stability::EvalResult;
use rustc_middle::span_bug;
Expand Down Expand Up @@ -52,16 +54,18 @@ pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
});
}
}
}

// This ABI is only allowed on function pointers
if abi == Abi::CCmseNonSecureCall {
struct_span_code_err!(
tcx.dcx(),
span,
E0781,
"the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
)
.emit();
pub fn check_abi_fn_ptr(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
match tcx.sess.target.is_abi_supported(abi) {
Some(true) => (),
Some(false) | None => {
tcx.node_span_lint(UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS, hir_id, span, |lint| {
lint.primary_message(
"use of calling convention not supported on this target on function pointer",
);
});
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_hir_analysis/src/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ pub mod wfcheck;

use std::num::NonZero;

pub use check::check_abi;
pub use check::{check_abi, check_abi_fn_ptr};
use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
use rustc_errors::{Diag, ErrorGuaranteed, pluralize, struct_span_code_err};
use rustc_hir::def_id::{DefId, LocalDefId};
Expand Down
19 changes: 15 additions & 4 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/cmse.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use rustc_errors::DiagCtxtHandle;
use rustc_hir as hir;
use rustc_hir::HirId;
use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err};
use rustc_hir::{self as hir, HirId};
use rustc_middle::ty::layout::LayoutError;
use rustc_middle::ty::{self, ParamEnv, TyCtxt};
use rustc_span::Span;
Expand All @@ -26,7 +25,19 @@ pub(crate) fn validate_cmse_abi<'tcx>(
..
}) = hir_node
else {
// might happen when this ABI is used incorrectly. That will be handled elsewhere
let span = match tcx.parent_hir_node(hir_id) {
hir::Node::Item(hir::Item {
kind: hir::ItemKind::ForeignMod { .. }, span, ..
}) => *span,
_ => tcx.hir().span(hir_id),
};
struct_span_code_err!(
tcx.dcx(),
span,
E0781,
"the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
)
.emit();
return;
};

Expand Down
7 changes: 7 additions & 0 deletions compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ use rustc_trait_selection::traits::{self, ObligationCtxt};
use tracing::{debug, debug_span, instrument};

use crate::bounds::Bounds;
use crate::check::check_abi_fn_ptr;
use crate::errors::{AmbiguousLifetimeBound, BadReturnTypeNotation, WildPatTy};
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
Expand Down Expand Up @@ -2324,6 +2325,12 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, safety, abi);
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);

if let hir::Node::Ty(hir::Ty { kind: hir::TyKind::BareFn(bare_fn_ty), span, .. }) =
tcx.hir_node(hir_id)
{
check_abi_fn_ptr(tcx, hir_id, *span, bare_fn_ty.abi);
}

// reject function types that violate cmse ABI requirements
cmse::validate_cmse_abi(self.tcx(), self.dcx(), hir_id, abi, bare_fn_ty);

Expand Down
45 changes: 45 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ declare_lint_pass! {
UNSTABLE_NAME_COLLISIONS,
UNSTABLE_SYNTAX_PRE_EXPANSION,
UNSUPPORTED_CALLING_CONVENTIONS,
UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
UNUSED_ASSIGNMENTS,
UNUSED_ASSOCIATED_TYPE_BOUNDS,
UNUSED_ATTRIBUTES,
Expand Down Expand Up @@ -3874,6 +3875,50 @@ declare_lint! {
};
}

declare_lint! {
/// The `unsupported_fn_ptr_calling_conventions` lint is output whenever there is a use of
/// a target dependent calling convention on a target that does not support this calling
/// convention on a function pointer.
///
/// For example `stdcall` does not make much sense for a x86_64 or, more apparently, powerpc
/// code, because this calling convention was never specified for those targets.
///
/// ### Example
///
/// ```rust,ignore (needs specific targets)
/// fn stdcall_ptr(f: extern "stdcall" fn ()) {
/// f()
/// }
/// ```
///
/// This will produce:
///
/// ```text
/// warning: use of calling convention not supported on this target on function pointer
/// --> $DIR/unsupported.rs:34:15
/// |
/// LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
/// | ^^^^^^^^^^^^^^^^^^^^^^^^
/// |
/// = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
/// = note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
/// = note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default
/// ```
///
/// ### Explanation
///
/// On most of the targets the behaviour of `stdcall` and similar calling conventions is not
/// defined at all, but was previously accepted due to a bug in the implementation of the
/// compiler.
pub UNSUPPORTED_FN_PTR_CALLING_CONVENTIONS,
Warn,
"use of unsupported calling convention for function pointer",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseErrorDontReportInDeps,
reference: "issue #130260 <https://github.com/rust-lang/rust/issues/130260>",
};
}

declare_lint! {
/// The `break_with_label_and_loop` lint detects labeled `break` expressions with
/// an unlabeled loop as their value expression.
Expand Down
4 changes: 2 additions & 2 deletions tests/debuginfo/type-names.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
// gdb-check:type = type_names::GenericStruct<type_names::mod1::Struct2, type_names::mod1::mod2::Struct3>

// gdb-command:whatis generic_struct2
// gdb-check:type = type_names::GenericStruct<type_names::Struct1, extern "fastcall" fn(isize) -> usize>
// gdb-check:type = type_names::GenericStruct<type_names::Struct1, extern "system" fn(isize) -> usize>

// gdb-command:whatis mod_struct
// gdb-check:type = type_names::mod1::Struct2
Expand Down Expand Up @@ -372,7 +372,7 @@ fn main() {
let simple_struct = Struct1;
let generic_struct1: GenericStruct<mod1::Struct2, mod1::mod2::Struct3> =
GenericStruct(PhantomData);
let generic_struct2: GenericStruct<Struct1, extern "fastcall" fn(isize) -> usize> =
let generic_struct2: GenericStruct<Struct1, extern "system" fn(isize) -> usize> =
GenericStruct(PhantomData);
let mod_struct = mod1::Struct2;

Expand Down
153 changes: 143 additions & 10 deletions tests/ui/abi/unsupported.aarch64.stderr
Original file line number Diff line number Diff line change
@@ -1,55 +1,188 @@
warning: use of calling convention not supported on this target on function pointer
--> $DIR/unsupported.rs:34:15
|
LL | fn ptx_ptr(f: extern "ptx-kernel" fn()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>
= note: `#[warn(unsupported_fn_ptr_calling_conventions)]` on by default

error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:28:1
--> $DIR/unsupported.rs:39:1
|
LL | extern "ptx-kernel" {}
| ^^^^^^^^^^^^^^^^^^^^^^

warning: use of calling convention not supported on this target on function pointer
--> $DIR/unsupported.rs:48:17
|
LL | fn aapcs_ptr(f: extern "aapcs" fn()) {
| ^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>

error[E0570]: `"aapcs"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:61:1
|
LL | extern "aapcs" {}
| ^^^^^^^^^^^^^^^^^

warning: use of calling convention not supported on this target on function pointer
--> $DIR/unsupported.rs:70:18
|
LL | fn msp430_ptr(f: extern "msp430-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>

error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:75:1
|
LL | extern "msp430-interrupt" {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: use of calling convention not supported on this target on function pointer
--> $DIR/unsupported.rs:80:15
|
LL | fn avr_ptr(f: extern "avr-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>

error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:85:1
|
LL | extern "avr-interrupt" {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^

warning: use of calling convention not supported on this target on function pointer
--> $DIR/unsupported.rs:93:17
|
LL | fn riscv_ptr(f: extern "riscv-interrupt-m" fn()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>

error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:104:1
|
LL | extern "riscv-interrupt-m" {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: use of calling convention not supported on this target on function pointer
--> $DIR/unsupported.rs:115:15
|
LL | fn x86_ptr(f: extern "x86-interrupt" fn()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>

error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:126:1
|
LL | extern "x86-interrupt" {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^

warning: use of calling convention not supported on this target on function pointer
--> $DIR/unsupported.rs:138:20
|
LL | fn thiscall_ptr(f: extern "thiscall" fn()) {
| ^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>

error[E0570]: `"thiscall"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:151:1
|
LL | extern "thiscall" {}
| ^^^^^^^^^^^^^^^^^^^^

warning: use of calling convention not supported on this target on function pointer
--> $DIR/unsupported.rs:169:19
|
LL | fn stdcall_ptr(f: extern "stdcall" fn()) {
| ^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>

warning: use of calling convention not supported on this target
--> $DIR/unsupported.rs:182:1
|
LL | extern "stdcall" {}
| ^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678>
= note: `#[warn(unsupported_calling_conventions)]` on by default

warning: use of calling convention not supported on this target on function pointer
--> $DIR/unsupported.rs:194:16
|
LL | fn cmse_ptr(f: extern "C-cmse-nonsecure-call" fn()) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #130260 <https://github.com/rust-lang/rust/issues/130260>

error[E0570]: `"ptx-kernel"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:32:1
|
LL | extern "ptx-kernel" fn ptx() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0570]: `"aapcs"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:30:1
--> $DIR/unsupported.rs:42:1
|
LL | extern "aapcs" fn aapcs() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0570]: `"msp430-interrupt"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:36:1
--> $DIR/unsupported.rs:68:1
|
LL | extern "msp430-interrupt" fn msp430() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0570]: `"avr-interrupt"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:38:1
--> $DIR/unsupported.rs:78:1
|
LL | extern "avr-interrupt" fn avr() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0570]: `"riscv-interrupt-m"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:40:1
--> $DIR/unsupported.rs:88:1
|
LL | extern "riscv-interrupt-m" fn riscv() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0570]: `"x86-interrupt"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:45:1
--> $DIR/unsupported.rs:110:1
|
LL | extern "x86-interrupt" fn x86() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

error[E0570]: `"thiscall"` is not a supported ABI for the current target
--> $DIR/unsupported.rs:50:1
--> $DIR/unsupported.rs:132:1
|
LL | extern "thiscall" fn thiscall() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

warning: use of calling convention not supported on this target
--> $DIR/unsupported.rs:56:1
--> $DIR/unsupported.rs:158:1
|
LL | extern "stdcall" fn stdcall() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
= note: for more information, see issue #87678 <https://github.com/rust-lang/rust/issues/87678>
= note: `#[warn(unsupported_calling_conventions)]` on by default

error: aborting due to 7 previous errors; 1 warning emitted
error: aborting due to 14 previous errors; 11 warnings emitted

For more information about this error, try `rustc --explain E0570`.
Loading

0 comments on commit 47293c1

Please sign in to comment.