miri/shims/
sig.rs

1//! Everything related to checking the signature of shim invocations.
2
3use rustc_abi::{CanonAbi, ExternAbi};
4use rustc_hir::Safety;
5use rustc_middle::ty::{Binder, FnSig, Ty};
6use rustc_span::Symbol;
7use rustc_target::callconv::FnAbi;
8
9use crate::*;
10
11/// Describes the expected signature of a shim.
12pub struct ShimSig<'tcx, const ARGS: usize> {
13    pub abi: ExternAbi,
14    pub args: [Ty<'tcx>; ARGS],
15    pub ret: Ty<'tcx>,
16}
17
18/// Construct a `ShimSig` with convenient syntax:
19/// ```rust,ignore
20/// shim_sig!(this, extern "C" fn (*const T, i32) -> usize)
21/// ```
22#[macro_export]
23macro_rules! shim_sig {
24    (extern $abi:literal fn($($arg:ty),*) -> $ret:ty) => {
25        |this| $crate::shims::sig::ShimSig {
26            abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"),
27            args: [$(shim_sig_arg!(this, $arg)),*],
28            ret: shim_sig_arg!(this, $ret),
29        }
30    };
31}
32
33/// Helper for `shim_sig!`.
34#[macro_export]
35macro_rules! shim_sig_arg {
36    // Unfortuantely we cannot take apart a `ty`-typed token at compile time,
37    // so we have to stringify it and match at runtime.
38    ($this:ident, $x:ty) => {{
39        match stringify!($x) {
40            "i8" => $this.tcx.types.i8,
41            "i16" => $this.tcx.types.i16,
42            "i32" => $this.tcx.types.i32,
43            "i64" => $this.tcx.types.i64,
44            "i128" => $this.tcx.types.i128,
45            "isize" => $this.tcx.types.isize,
46            "u8" => $this.tcx.types.u8,
47            "u16" => $this.tcx.types.u16,
48            "u32" => $this.tcx.types.u32,
49            "u64" => $this.tcx.types.u64,
50            "u128" => $this.tcx.types.u128,
51            "usize" => $this.tcx.types.usize,
52            "()" => $this.tcx.types.unit,
53            "*const _" => $this.machine.layouts.const_raw_ptr.ty,
54            "*mut _" => $this.machine.layouts.mut_raw_ptr.ty,
55            ty if let Some(libc_ty) = ty.strip_prefix("libc::") => $this.libc_ty_layout(libc_ty).ty,
56            ty => panic!("unsupported signature type {ty:?}"),
57        }
58    }};
59}
60
61/// Helper function to compare two ABIs.
62fn check_shim_abi<'tcx>(
63    this: &MiriInterpCx<'tcx>,
64    callee_abi: &FnAbi<'tcx, Ty<'tcx>>,
65    caller_abi: &FnAbi<'tcx, Ty<'tcx>>,
66) -> InterpResult<'tcx> {
67    if callee_abi.conv != caller_abi.conv {
68        throw_ub_format!(
69            r#"calling a function with calling convention "{callee}" using caller calling convention "{caller}""#,
70            callee = callee_abi.conv,
71            caller = caller_abi.conv,
72        );
73    }
74    if callee_abi.can_unwind && !caller_abi.can_unwind {
75        throw_ub_format!(
76            "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding",
77        );
78    }
79    if caller_abi.c_variadic && !callee_abi.c_variadic {
80        throw_ub_format!(
81            "ABI mismatch: calling a non-variadic function with a variadic caller-side signature"
82        );
83    }
84    if !caller_abi.c_variadic && callee_abi.c_variadic {
85        throw_ub_format!(
86            "ABI mismatch: calling a variadic function with a non-variadic caller-side signature"
87        );
88    }
89
90    if callee_abi.fixed_count != caller_abi.fixed_count {
91        throw_ub_format!(
92            "ABI mismatch: expected {} arguments, found {} arguments ",
93            callee_abi.fixed_count,
94            caller_abi.fixed_count
95        );
96    }
97
98    if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? {
99        throw_ub!(AbiMismatchReturn {
100            caller_ty: caller_abi.ret.layout.ty,
101            callee_ty: callee_abi.ret.layout.ty
102        });
103    }
104
105    for (idx, (caller_arg, callee_arg)) in
106        caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate()
107    {
108        if !this.check_argument_compat(caller_arg, callee_arg)? {
109            throw_ub!(AbiMismatchArgument {
110                arg_idx: idx,
111                caller_ty: caller_abi.args[idx].layout.ty,
112                callee_ty: callee_abi.args[idx].layout.ty
113            });
114        }
115    }
116
117    interp_ok(())
118}
119
120fn check_shim_symbol_clash<'tcx>(
121    this: &mut MiriInterpCx<'tcx>,
122    link_name: Symbol,
123) -> InterpResult<'tcx, ()> {
124    if let Some((body, instance)) = this.lookup_exported_symbol(link_name)? {
125        // If compiler-builtins is providing the symbol, then don't treat it as a clash.
126        // We'll use our built-in implementation in `emulate_foreign_item_inner` for increased
127        // performance. Note that this means we won't catch any undefined behavior in
128        // compiler-builtins when running other crates, but Miri can still be run on
129        // compiler-builtins itself (or any crate that uses it as a normal dependency)
130        if this.tcx.is_compiler_builtins(instance.def_id().krate) {
131            return interp_ok(());
132        }
133
134        throw_machine_stop!(TerminationInfo::SymbolShimClashing {
135            link_name,
136            span: body.span.data(),
137        })
138    }
139    interp_ok(())
140}
141
142impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
143pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
144    fn check_shim_sig_lenient<'a, const N: usize>(
145        &mut self,
146        abi: &FnAbi<'tcx, Ty<'tcx>>,
147        exp_abi: CanonAbi,
148        link_name: Symbol,
149        args: &'a [OpTy<'tcx>],
150    ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
151        let this = self.eval_context_mut();
152        check_shim_symbol_clash(this, link_name)?;
153
154        if abi.conv != exp_abi {
155            throw_ub_format!(
156                r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
157                abi.conv
158            );
159        }
160        if abi.c_variadic {
161            throw_ub_format!(
162                "calling a non-variadic function with a variadic caller-side signature"
163            );
164        }
165
166        if let Ok(ops) = args.try_into() {
167            return interp_ok(ops);
168        }
169        throw_ub_format!(
170            "incorrect number of arguments for `{link_name}`: got {}, expected {}",
171            args.len(),
172            N
173        )
174    }
175
176    /// Check that the given `caller_fn_abi` matches the expected ABI described by `shim_sig`, and
177    /// then returns the list of arguments.
178    fn check_shim_sig<'a, const N: usize>(
179        &mut self,
180        shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
181        link_name: Symbol,
182        caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
183        caller_args: &'a [OpTy<'tcx>],
184    ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
185        let this = self.eval_context_mut();
186        let shim_sig = shim_sig(this);
187
188        // Compute full callee ABI.
189        let mut inputs_and_output = Vec::with_capacity(N.strict_add(1));
190        inputs_and_output.extend(&shim_sig.args);
191        inputs_and_output.push(shim_sig.ret);
192        let fn_sig_binder = Binder::dummy(FnSig {
193            inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output),
194            c_variadic: false,
195            // This does not matter for the ABI.
196            safety: Safety::Safe,
197            abi: shim_sig.abi,
198        });
199        let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?;
200
201        // Check everything.
202        check_shim_abi(this, callee_fn_abi, caller_fn_abi)?;
203        check_shim_symbol_clash(this, link_name)?;
204
205        // Return arguments.
206        if let Ok(ops) = caller_args.try_into() {
207            return interp_ok(ops);
208        }
209        unreachable!()
210    }
211
212    /// Check shim for variadic function.
213    /// Returns a tuple that consisting of an array of fixed args, and a slice of varargs.
214    fn check_shim_sig_variadic_lenient<'a, const N: usize>(
215        &mut self,
216        abi: &FnAbi<'tcx, Ty<'tcx>>,
217        exp_abi: CanonAbi,
218        link_name: Symbol,
219        args: &'a [OpTy<'tcx>],
220    ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])>
221    where
222        &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
223    {
224        let this = self.eval_context_mut();
225        check_shim_symbol_clash(this, link_name)?;
226
227        if abi.conv != exp_abi {
228            throw_ub_format!(
229                r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
230                abi.conv
231            );
232        }
233        if !abi.c_variadic {
234            throw_ub_format!(
235                "calling a variadic function with a non-variadic caller-side signature"
236            );
237        }
238        if abi.fixed_count != u32::try_from(N).unwrap() {
239            throw_ub_format!(
240                "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
241                link_name.as_str(),
242                abi.fixed_count
243            )
244        }
245        if let Some(args) = args.split_first_chunk() {
246            return interp_ok(args);
247        }
248        panic!("mismatch between signature and `args` slice");
249    }
250}
251
252/// Check that the number of varargs is at least the minimum what we expect.
253/// Fixed args should not be included.
254pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
255    name: &'a str,
256    args: &'a [OpTy<'tcx>],
257) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
258    if let Some((ops, _)) = args.split_first_chunk() {
259        return interp_ok(ops);
260    }
261    throw_ub_format!(
262        "not enough variadic arguments for `{name}`: got {}, expected at least {}",
263        args.len(),
264        N
265    )
266}