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};
7
8use crate::errors;
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 match abi {
21 ExternAbi::CmseNonSecureCall => {
22 let hir_node = tcx.hir_node(hir_id);
23 let hir::Node::Ty(hir::Ty {
24 span: fn_ptr_span,
25 kind: hir::TyKind::FnPtr(fn_ptr_ty),
26 ..
27 }) = hir_node
28 else {
29 let span = match tcx.parent_hir_node(hir_id) {
30 hir::Node::Item(hir::Item {
31 kind: hir::ItemKind::ForeignMod { .. },
32 span,
33 ..
34 }) => *span,
35 _ => tcx.hir_span(hir_id),
36 };
37 struct_span_code_err!(
38 dcx,
39 span,
40 E0781,
41 "the `\"cmse-nonsecure-call\"` ABI is only allowed on function pointers"
42 )
43 .emit();
44 return;
45 };
46
47 match is_valid_cmse_inputs(tcx, fn_sig) {
48 Ok(Ok(())) => {}
49 Ok(Err(index)) => {
50 let span = if let Some(ident) = fn_ptr_ty.param_idents[index] {
53 ident.span.to(fn_ptr_ty.decl.inputs[index].span)
54 } else {
55 fn_ptr_ty.decl.inputs[index].span
56 }
57 .to(fn_ptr_ty.decl.inputs.last().unwrap().span);
58 let plural = fn_ptr_ty.param_idents.len() - index != 1;
59 dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi });
60 }
61 Err(layout_err) => {
62 if should_emit_generic_error(abi, layout_err) {
63 dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span });
64 }
65 }
66 }
67
68 match is_valid_cmse_output(tcx, fn_sig) {
69 Ok(true) => {}
70 Ok(false) => {
71 let span = fn_ptr_ty.decl.output.span();
72 dcx.emit_err(errors::CmseOutputStackSpill { span, abi });
73 }
74 Err(layout_err) => {
75 if should_emit_generic_error(abi, layout_err) {
76 dcx.emit_err(errors::CmseCallGeneric { span: *fn_ptr_span });
77 }
78 }
79 };
80 }
81 ExternAbi::CmseNonSecureEntry => {
82 let hir_node = tcx.hir_node(hir_id);
83 let Some(hir::FnSig { decl, span: fn_sig_span, .. }) = hir_node.fn_sig() else {
84 return;
86 };
87
88 if decl.c_variadic {
91 return;
92 }
93
94 match is_valid_cmse_inputs(tcx, fn_sig) {
95 Ok(Ok(())) => {}
96 Ok(Err(index)) => {
97 let span = decl.inputs[index].span.to(decl.inputs.last().unwrap().span);
100 let plural = decl.inputs.len() - index != 1;
101 dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi });
102 }
103 Err(layout_err) => {
104 if should_emit_generic_error(abi, layout_err) {
105 dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
106 }
107 }
108 }
109
110 match is_valid_cmse_output(tcx, fn_sig) {
111 Ok(true) => {}
112 Ok(false) => {
113 let span = decl.output.span();
114 dcx.emit_err(errors::CmseOutputStackSpill { span, abi });
115 }
116 Err(layout_err) => {
117 if should_emit_generic_error(abi, layout_err) {
118 dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
119 }
120 }
121 };
122 }
123 _ => (),
124 }
125}
126
127fn is_valid_cmse_inputs<'tcx>(
129 tcx: TyCtxt<'tcx>,
130 fn_sig: ty::PolyFnSig<'tcx>,
131) -> Result<Result<(), usize>, &'tcx LayoutError<'tcx>> {
132 let mut span = None;
133 let mut accum = 0u64;
134
135 let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
137 let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);
138
139 for (index, ty) in fn_sig.inputs().iter().enumerate() {
140 let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?;
141
142 let align = layout.layout.align().bytes();
143 let size = layout.layout.size().bytes();
144
145 accum += size;
146 accum = accum.next_multiple_of(Ord::max(4, align));
147
148 if accum > 16 {
150 span = span.or(Some(index));
151 }
152 }
153
154 match span {
155 None => Ok(Ok(())),
156 Some(span) => Ok(Err(span)),
157 }
158}
159
160fn is_valid_cmse_output<'tcx>(
162 tcx: TyCtxt<'tcx>,
163 fn_sig: ty::PolyFnSig<'tcx>,
164) -> Result<bool, &'tcx LayoutError<'tcx>> {
165 let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
167 let fn_sig = tcx.erase_and_anonymize_regions(fn_sig);
168 let return_type = fn_sig.output();
169
170 if return_type.has_opaque_types() {
180 return Err(tcx.arena.alloc(LayoutError::TooGeneric(return_type)));
181 }
182
183 let typing_env = ty::TypingEnv::fully_monomorphized();
184 let layout = tcx.layout_of(typing_env.as_query_input(return_type))?;
185
186 Ok(is_valid_cmse_output_layout(layout))
187}
188
189fn is_valid_cmse_output_layout<'tcx>(layout: TyAndLayout<'tcx>) -> bool {
191 let size = layout.layout.size().bytes();
192
193 if size <= 4 {
194 return true;
195 } else if size > 8 {
196 return false;
197 }
198
199 let BackendRepr::Scalar(scalar) = layout.layout.backend_repr else {
201 return false;
202 };
203
204 let Scalar::Initialized { value, .. } = scalar else {
205 return false;
206 };
207
208 matches!(value, Primitive::Int(Integer::I64, _) | Primitive::Float(Float::F64))
209}
210
211fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
212 use LayoutError::*;
213
214 match layout_err {
215 TooGeneric(ty) => {
216 match abi {
217 ExternAbi::CmseNonSecureCall => {
218 !ty.is_impl_trait()
220 }
221 ExternAbi::CmseNonSecureEntry => true,
222 _ => bug!("invalid ABI: {abi}"),
223 }
224 }
225 Unknown(..)
226 | SizeOverflow(..)
227 | InvalidSimd { .. }
228 | NormalizationFailure(..)
229 | ReferencesError(..)
230 | Cycle(..) => {
231 false }
233 }
234}