rustc_hir_analysis/hir_ty_lowering/
cmse.rs
1use 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::CCmseNonSecureCall => {
22 let hir_node = tcx.hir_node(hir_id);
23 let hir::Node::Ty(hir::Ty {
24 span: bare_fn_span,
25 kind: hir::TyKind::BareFn(bare_fn_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 tcx.dcx(),
39 span,
40 E0781,
41 "the `\"C-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) = bare_fn_ty.param_names[index] {
53 ident.span.to(bare_fn_ty.decl.inputs[index].span)
54 } else {
55 bare_fn_ty.decl.inputs[index].span
56 }
57 .to(bare_fn_ty.decl.inputs.last().unwrap().span);
58 let plural = bare_fn_ty.param_names.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: *bare_fn_span });
64 }
65 }
66 }
67
68 match is_valid_cmse_output(tcx, fn_sig) {
69 Ok(true) => {}
70 Ok(false) => {
71 let span = bare_fn_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: *bare_fn_span });
77 }
78 }
79 };
80 }
81 ExternAbi::CCmseNonSecureEntry => {
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 match is_valid_cmse_inputs(tcx, fn_sig) {
89 Ok(Ok(())) => {}
90 Ok(Err(index)) => {
91 let span = decl.inputs[index].span.to(decl.inputs.last().unwrap().span);
94 let plural = decl.inputs.len() - index != 1;
95 dcx.emit_err(errors::CmseInputsStackSpill { span, plural, abi });
96 }
97 Err(layout_err) => {
98 if should_emit_generic_error(abi, layout_err) {
99 dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
100 }
101 }
102 }
103
104 match is_valid_cmse_output(tcx, fn_sig) {
105 Ok(true) => {}
106 Ok(false) => {
107 let span = decl.output.span();
108 dcx.emit_err(errors::CmseOutputStackSpill { span, abi });
109 }
110 Err(layout_err) => {
111 if should_emit_generic_error(abi, layout_err) {
112 dcx.emit_err(errors::CmseEntryGeneric { span: *fn_sig_span });
113 }
114 }
115 };
116 }
117 _ => (),
118 }
119}
120
121fn is_valid_cmse_inputs<'tcx>(
123 tcx: TyCtxt<'tcx>,
124 fn_sig: ty::PolyFnSig<'tcx>,
125) -> Result<Result<(), usize>, &'tcx LayoutError<'tcx>> {
126 let mut span = None;
127 let mut accum = 0u64;
128
129 let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
131
132 for (index, ty) in fn_sig.inputs().iter().enumerate() {
133 let layout = tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(*ty))?;
134
135 let align = layout.layout.align().abi.bytes();
136 let size = layout.layout.size().bytes();
137
138 accum += size;
139 accum = accum.next_multiple_of(Ord::max(4, align));
140
141 if accum > 16 {
143 span = span.or(Some(index));
144 }
145 }
146
147 match span {
148 None => Ok(Ok(())),
149 Some(span) => Ok(Err(span)),
150 }
151}
152
153fn is_valid_cmse_output<'tcx>(
155 tcx: TyCtxt<'tcx>,
156 fn_sig: ty::PolyFnSig<'tcx>,
157) -> Result<bool, &'tcx LayoutError<'tcx>> {
158 let fn_sig = tcx.instantiate_bound_regions_with_erased(fn_sig);
160
161 let typing_env = ty::TypingEnv::fully_monomorphized();
162
163 let mut ret_ty = fn_sig.output();
164 let layout = tcx.layout_of(typing_env.as_query_input(ret_ty))?;
165 let size = layout.layout.size().bytes();
166
167 if size <= 4 {
168 return Ok(true);
169 } else if size > 8 {
170 return Ok(false);
171 }
172
173 'outer: loop {
175 let ty::Adt(adt_def, args) = ret_ty.kind() else {
176 break;
177 };
178
179 if !adt_def.repr().transparent() {
180 break;
181 }
182
183 for variant_def in adt_def.variants() {
185 for field_def in variant_def.fields.iter() {
186 let ty = field_def.ty(tcx, args);
187 let layout = tcx.layout_of(typing_env.as_query_input(ty))?;
188
189 if !layout.layout.is_1zst() {
190 ret_ty = ty;
191 continue 'outer;
192 }
193 }
194 }
195 }
196
197 Ok(ret_ty == tcx.types.i64 || ret_ty == tcx.types.u64 || ret_ty == tcx.types.f64)
198}
199
200fn should_emit_generic_error<'tcx>(abi: ExternAbi, layout_err: &'tcx LayoutError<'tcx>) -> bool {
201 use LayoutError::*;
202
203 match layout_err {
204 TooGeneric(ty) => {
205 match abi {
206 ExternAbi::CCmseNonSecureCall => {
207 !ty.is_impl_trait()
209 }
210 ExternAbi::CCmseNonSecureEntry => true,
211 _ => bug!("invalid ABI: {abi}"),
212 }
213 }
214 Unknown(..)
215 | SizeOverflow(..)
216 | NormalizationFailure(..)
217 | ReferencesError(..)
218 | Cycle(..) => {
219 false }
221 }
222}