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::LayoutError;
6use rustc_middle::ty::{self, TyCtxt};
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
138 for (index, ty) in fn_sig.inputs().iter().enumerate() {
139 let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?;
140
141 let align = layout.layout.align().abi.bytes();
142 let size = layout.layout.size().bytes();
143
144 accum += size;
145 accum = accum.next_multiple_of(Ord::max(4, align));
146
147 if accum > 16 {
149 span = span.or(Some(index));
150 }
151 }
152
153 match span {
154 None => Ok(Ok(())),
155 Some(span) => Ok(Err(span)),
156 }
157}
158
159fn is_valid_cmse_output<'tcx>(
161 tcx: TyCtxt<'tcx>,
162 fn_sig: ty::PolyFnSig<'tcx>,
163) -> Result<bool, &'tcx LayoutError<'tcx>> {
164 let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
166
167 let typing_env = ty::TypingEnv::fully_monomorphized();
168
169 let mut ret_ty = fn_sig.output();
170 let layout = tcx.layout_of(typing_env.as_query_input(ret_ty))?;
171 let size = layout.layout.size().bytes();
172
173 if size <= 4 {
174 return Ok(true);
175 } else if size > 8 {
176 return Ok(false);
177 }
178
179 'outer: loop {
181 let ty::Adt(adt_def, args) = ret_ty.kind() else {
182 break;
183 };
184
185 if !adt_def.repr().transparent() {
186 break;
187 }
188
189 for variant_def in adt_def.variants() {
191 for field_def in variant_def.fields.iter() {
192 let ty = field_def.ty(tcx, args);
193 let layout = tcx.layout_of(typing_env.as_query_input(ty))?;
194
195 if !layout.layout.is_1zst() {
196 ret_ty = ty;
197 continue 'outer;
198 }
199 }
200 }
201 }
202
203 Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64)
204}
205
206fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
207 use LayoutError::*;
208
209 match layout_err {
210 TooGeneric(ty) => {
211 match abi {
212 ExternAbi::CmseNonSecureCall => {
213 !ty.is_impl_trait()
215 }
216 ExternAbi::CmseNonSecureEntry => true,
217 _ => bug!("invalid ABI: {abi}"),
218 }
219 }
220 Unknown(..)
221 | SizeOverflow(..)
222 | InvalidSimd { .. }
223 | NormalizationFailure(..)
224 | ReferencesError(..)
225 | Cycle(..) => {
226 false }
228 }
229}