rustc_hir_analysis/hir_ty_lowering/
cmse.rs1use rustc_abi::{BackendRepr, ExternAbi, Float, Integer, Primitive, Scalar};
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::{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 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 let layout = tcx
90 .layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))
91 .map_err(|e| (hir_ty.span, e))?;
92
93 let align = layout.layout.align().bytes();
94 let size = layout.layout.size().bytes();
95
96 accum += size;
97 accum = accum.next_multiple_of(Ord::max(4, align));
98
99 if accum > 16 {
101 excess_argument_spans.push(hir_ty.span);
102 }
103 }
104
105 if !excess_argument_spans.is_empty() {
106 dcx.emit_err(errors::CmseInputsStackSpill { spans: excess_argument_spans, abi });
109 }
110
111 Ok(())
112}
113
114fn is_valid_cmse_output<'tcx>(
116 tcx: TyCtxt<'tcx>,
117 dcx: DiagCtxtHandle<'_>,
118 fn_sig: ty::PolyFnSig<'tcx>,
119 fn_decl: &hir::FnDecl<'tcx>,
120 abi: ExternAbi,
121) -> Result<(), &'tcx LayoutError<'tcx>> {
122 let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
124 let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);
125 let return_type = fn_sig.output();
126
127 if abi == ExternAbi::CmseNonSecureEntry && return_type.has_opaque_types() {
137 dcx.emit_err(errors::CmseImplTrait { span: fn_decl.output.span(), abi });
138 return Ok(());
139 }
140
141 let typing_env = ty::TypingEnv::fully_monomorphized();
142 let layout = tcx.layout_of(typing_env.as_query_input(return_type))?;
143
144 if !is_valid_cmse_output_layout(layout) {
145 dcx.emit_err(errors::CmseOutputStackSpill { span: fn_decl.output.span(), abi });
146 }
147
148 Ok(())
149}
150
151fn is_valid_cmse_output_layout<'tcx>(layout: TyAndLayout<'tcx>) -> bool {
153 let size = layout.layout.size().bytes();
154
155 if size <= 4 {
156 return true;
157 } else if size > 8 {
158 return false;
159 }
160
161 let BackendRepr::Scalar(scalar) = layout.layout.backend_repr else {
163 return false;
164 };
165
166 let Scalar::Initialized { value, .. } = scalar else {
167 return false;
168 };
169
170 matches!(value, Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64))
171}
172
173fn should_emit_layout_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
174 use LayoutError::*;
175
176 match layout_err {
177 TooGeneric(ty) => {
178 match abi {
179 ExternAbi::CmseNonSecureCall => {
180 !ty.has_opaque_types()
182 }
183 ExternAbi::CmseNonSecureEntry => true,
184 _ => bug!("invalid ABI: {abi}"),
185 }
186 }
187 Unknown(..)
188 | SizeOverflow(..)
189 | InvalidSimd { .. }
190 | NormalizationFailure(..)
191 | ReferencesError(..)
192 | Cycle(..) => {
193 false }
195 }
196}