rustc_hir_analysis/hir_ty_lowering/
cmse.rs1use rustc_abi::ExternAbi;
2use rustc_errors::{DiagCtxtHandle, E0781, struct_span_code_err};
3use rustc_hir::{self as hir, HirId};
4use rustc_middle::ty::layout::{LayoutCx, LayoutError, TyAndLayout};
5use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
6use rustc_span::{Span, bug};
7
8use crate::diagnostics;
9
10pub(crate) fn validate_cmse_abi<'tcx>(
14 tcx: TyCtxt<'tcx>,
15 dcx: DiagCtxtHandle<'_>,
16 hir_id: HirId,
17 abi: ExternAbi,
18 fn_sig: ty::PolyFnSig<'tcx>,
19) {
20 let fn_decl = match abi {
21 ExternAbi::CmseNonSecureCall => match tcx.hir_node(hir_id) {
22 hir::Node::Ty(hir::Ty { kind: hir::TyKind::FnPtr(fn_ptr_ty), .. }) => fn_ptr_ty.decl,
23 _ => {
24 let span = match tcx.parent_hir_node(hir_id) {
25 hir::Node::Item(hir::Item {
26 kind: hir::ItemKind::ForeignMod { .. },
27 span,
28 ..
29 }) => *span,
30 _ => tcx.hir_span(hir_id),
31 };
32 {
dcx.struct_span_err(span,
::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the `\"cmse-nonsecure-call\"` ABI is only allowed on function pointers"))
})).with_code(E0781)
}struct_span_code_err!(
33 dcx,
34 span,
35 E0781,
36 "the `\"cmse-nonsecure-call\"` ABI is only allowed on function pointers"
37 )
38 .emit();
39 return;
40 }
41 },
42 ExternAbi::CmseNonSecureEntry => {
43 let Some(hir::FnSig { decl, .. }) = tcx.hir_node(hir_id).fn_sig() else {
44 return;
46 };
47
48 if decl.c_variadic() {
51 return;
52 }
53
54 decl
55 }
56 _ => return,
57 };
58
59 if let Err((span, layout_err)) = is_valid_cmse_inputs(tcx, dcx, fn_sig, fn_decl, abi) {
60 if should_emit_layout_error(abi, layout_err) {
61 dcx.emit_err(diagnostics::CmseGeneric { span, abi });
62 }
63 }
64
65 if let Err(layout_err) = is_valid_cmse_output(tcx, dcx, fn_sig, fn_decl, abi) {
66 if should_emit_layout_error(abi, layout_err) {
67 dcx.emit_err(diagnostics::CmseGeneric { span: fn_decl.output.span(), abi });
68 }
69 }
70}
71
72fn is_valid_cmse_inputs<'tcx>(
74 tcx: TyCtxt<'tcx>,
75 dcx: DiagCtxtHandle<'_>,
76 fn_sig: ty::PolyFnSig<'tcx>,
77 fn_decl: &hir::FnDecl<'tcx>,
78 abi: ExternAbi,
79) -> Result<(), (Span, &'tcx LayoutError<'tcx>)> {
80 let mut accum = 0u64;
81 let mut excess_argument_spans = Vec::new();
82
83 let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
85 let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);
86
87 for (ty, hir_ty) in fn_sig.inputs().iter().zip(fn_decl.inputs) {
88 if ty.has_infer_types() {
89 let err = LayoutError::Unknown(*ty);
90 return Err((hir_ty.span, tcx.arena.alloc(err)));
91 }
92
93 let layout = tcx
94 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))
95 .map_err(|e| (hir_ty.span, e))?;
96
97 let align = layout.layout.align().bytes();
98 let size = layout.layout.size().bytes();
99
100 accum += size;
101 accum = accum.next_multiple_of(Ord::max(4, align));
102
103 if accum > 16 {
105 excess_argument_spans.push(hir_ty.span);
106 }
107 }
108
109 if !excess_argument_spans.is_empty() {
110 dcx.emit_err(diagnostics::CmseInputsStackSpill { spans: excess_argument_spans, abi });
113 }
114
115 Ok(())
116}
117
118fn is_valid_cmse_output<'tcx>(
120 tcx: TyCtxt<'tcx>,
121 dcx: DiagCtxtHandle<'_>,
122 fn_sig: ty::PolyFnSig<'tcx>,
123 fn_decl: &hir::FnDecl<'tcx>,
124 abi: ExternAbi,
125) -> Result<(), &'tcx LayoutError<'tcx>> {
126 let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
128 let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);
129 let return_type = fn_sig.output();
130
131 if abi == ExternAbi::CmseNonSecureEntry && return_type.has_opaque_types() {
141 dcx.emit_err(diagnostics::CmseImplTrait { span: fn_decl.output.span(), abi });
142 return Ok(());
143 }
144
145 if return_type.has_infer_types() {
146 let err = LayoutError::Unknown(return_type);
147 return Err(tcx.arena.alloc(err));
148 }
149
150 let typing_env = ty::TypingEnv::fully_monomorphized();
151 let layout = tcx.layout_of(typing_env.as_query_input(return_type))?;
152 let layout_cx = LayoutCx::new(tcx, typing_env);
153
154 if !is_valid_cmse_output_layout(layout_cx, layout) {
155 dcx.emit_err(diagnostics::CmseOutputStackSpill { span: fn_decl.output.span(), abi });
156 }
157
158 Ok(())
159}
160
161fn is_valid_cmse_output_layout<'tcx>(cx: LayoutCx<'tcx>, layout: TyAndLayout<'tcx>) -> bool {
163 let size = layout.layout.size().bytes();
164
165 if size <= 4 {
166 return true;
167 } else if size != 8 {
168 return false;
169 }
170
171 #[allow(non_exhaustive_omitted_patterns)] match layout.peel_transparent_wrappers(&cx).ty.kind()
{
ty::Int(ty::IntTy::I64) | ty::Uint(ty::UintTy::U64) |
ty::Float(ty::FloatTy::F64) => true,
_ => false,
}matches!(
173 layout.peel_transparent_wrappers(&cx).ty.kind(),
174 ty::Int(ty::IntTy::I64) | ty::Uint(ty::UintTy::U64) | ty::Float(ty::FloatTy::F64)
175 )
176}
177
178fn should_emit_layout_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
179 use LayoutError::*;
180
181 match layout_err {
182 TooGeneric(ty) => {
183 match abi {
184 ExternAbi::CmseNonSecureCall => {
185 !ty.has_opaque_types()
187 }
188 ExternAbi::CmseNonSecureEntry => true,
189 _ => bug_impl(None, format_args!("invalid ABI: {0}", abi), Location::caller())bug!("invalid ABI: {abi}"),
190 }
191 }
192 Unknown(..)
193 | SizeOverflow(..)
194 | InvalidSimd { .. }
195 | NormalizationFailure(..)
196 | ReferencesError(..) => {
197 false }
199 }
200}