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