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