Skip to main content

miri/shims/
sig.rs

1//! Everything related to checking the signature of shim invocations.
2
3use rustc_abi::{CanonAbi, ExternAbi};
4use rustc_middle::ty::{Binder, FnSig, FnSigKind, Ty};
5use rustc_span::Symbol;
6use rustc_target::callconv::FnAbi;
7
8use crate::*;
9
10/// Describes the expected signature of a shim.
11pub struct ShimSig<'tcx, const ARGS: usize> {
12    pub abi: ExternAbi,
13    pub args: [Ty<'tcx>; ARGS],
14    pub ret: Ty<'tcx>,
15    pub c_variadic: bool,
16}
17
18/// Construct a `ShimSig` with convenient syntax:
19/// ```rust,ignore
20/// shim_sig!(extern "C" fn (*const T, i32) -> usize)
21/// ```
22///
23/// The following types are supported:
24/// - primitive integer types
25/// - `()`
26/// - (thin) raw pointers, written `*_` since the mutability and pointee type are irrelevant
27/// - `$crate::$mod::...::$ty` for a type from the given crate (most commonly that is `libc`)
28/// - `winapi::$ty` for a type from `std::sys::pal::windows::c`
29#[macro_export]
30macro_rules! shim_sig {
31    (extern $abi:literal fn($($args:tt)*) -> $($ret:tt)*) => {
32        |this| {
33            let (args, c_variadic) = shim_sig_args_sep!(this, [$($args)*]);
34            $crate::shims::sig::ShimSig {
35                abi: std::str::FromStr::from_str($abi).expect("incorrect abi specified"),
36                args,
37                ret: shim_sig_arg!(this, $($ret)*),
38                c_variadic,
39            }
40        }
41    };
42}
43
44/// Computes a list of types for varargs, using the same syntax as `shim_sig!`.
45#[macro_export]
46macro_rules! shim_varargs {
47    ($($args:tt)*) => {
48        |this| {
49            let (args, c_variadic) = shim_sig_args_sep!(this, [$($args)*]);
50            assert!(!c_variadic); // don't accept `...` here
51            args
52        }
53    };
54}
55
56/// Helper for `shim_sig!`.
57///
58/// Groups tokens into comma-separated chunks and calls the provided macro on them.
59/// Returns a list of types and a boolean indicating whether there was a trailing `...`.
60///
61/// # Examples
62///
63/// ```ignore
64/// shim_sig_args_sep!(this, [*_, i32, libc::off64_t]);
65/// // expands to:
66/// [shim_sig_arg!(*_), shim_sig_arg!(i32), shim_sig_arg!(libc::off64_t)];
67/// ```
68#[macro_export]
69macro_rules! shim_sig_args_sep {
70    ($this:ident, [$($tt:tt)*]) => {
71        shim_sig_args_sep!(@ $this [] [] $($tt)*)
72    };
73
74    // All below matchers form a fairly simple iterator over the input.
75    // - Non-comma token - append to collector
76    // - Comma token - call the provided macro on the collector and reset the collector
77    // - End of input - empty collector one last time. emit output as an array
78
79    // Handles `,` token - take collected type and call shim_sig_arg on it.
80    // Append the result to the final output.
81    (@ $this:ident [$($final:tt)*] [$($collected:tt)*] , $($tt:tt)*) => {
82        shim_sig_args_sep!(@ $this [$($final)* shim_sig_arg!($this, $($collected)*), ] [] $($tt)*)
83    };
84    // Handle non-comma token - append to collected type.
85    (@ $this:ident [$($final:tt)*] [$($collected:tt)*] $first:tt $($tt:tt)*) => {
86        shim_sig_args_sep!(@ $this [$($final)*] [$($collected)* $first] $($tt)*)
87    };
88    // No more tokens, trailing `...` - emit final output, indicate this is variadic.
89    (@ $this:ident [$($final:tt)*] [...] ) => {
90        ([$($final)*], true)
91    };
92    // No more tokens - emit final output, including final non-comma type.
93    (@ $this:ident [$($final:tt)*] [$($collected:tt)+] ) => {
94        ([$($final)* shim_sig_arg!($this, $($collected)*)], false)
95    };
96    // No more tokens, empty collector - emit final output.
97    (@ $this:ident [$($final:tt)*] [] ) => {
98        ([$($final)*], false)
99    };
100}
101
102/// Helper for `shim_sig!`.
103///
104/// Converts a type
105#[macro_export]
106macro_rules! shim_sig_arg {
107    ($this:ident, i8) => {
108        $this.tcx.types.i8
109    };
110    ($this:ident, i16) => {
111        $this.tcx.types.i16
112    };
113    ($this:ident, i32) => {
114        $this.tcx.types.i32
115    };
116    ($this:ident, i64) => {
117        $this.tcx.types.i64
118    };
119    ($this:ident, i128) => {
120        $this.tcx.types.i128
121    };
122    ($this:ident, isize) => {
123        $this.tcx.types.isize
124    };
125    ($this:ident, u8) => {
126        $this.tcx.types.u8
127    };
128    ($this:ident, u16) => {
129        $this.tcx.types.u16
130    };
131    ($this:ident, u32) => {
132        $this.tcx.types.u32
133    };
134    ($this:ident, u64) => {
135        $this.tcx.types.u64
136    };
137    ($this:ident, u128) => {
138        $this.tcx.types.u128
139    };
140    ($this:ident, usize) => {
141        $this.tcx.types.usize
142    };
143    ($this:ident, ()) => {
144        $this.tcx.types.unit
145    };
146    ($this:ident, !) => {
147        $this.tcx.types.never
148    };
149    ($this:ident, bool) => {
150        $this.tcx.types.bool
151    };
152    ($this:ident, *_) => {
153        // Mutability does not matter for ABI.
154        $this.machine.layouts.void_ptr_mut.ty
155    };
156    ($this:ident, *$($ty:tt)*) => {
157        // Pointee types matter for varargs so we support explicitly giving them.
158        // Mutability does not matter for ABI.
159        rustc_middle::ty::Ty::new_ptr(
160            *$this.tcx,
161            shim_sig_arg!($this, $($ty)*),
162            rustc_middle::mir::Mutability::Mut,
163        )
164    };
165    ($this:ident, fn(..) -> _) => {
166        // All function pointer types are the same on the ABI level so we don't care about arg/ret.
167        $this.machine.layouts.fn_ptr.ty
168    };
169    ($this:ident, &[$($ty:tt)*]) => {
170        rustc_middle::ty::Ty::new_ref(
171            *$this.tcx,
172            $this.tcx.lifetimes.re_erased,
173            rustc_middle::ty::Ty::new_slice(*$this.tcx, shim_sig_arg!($this, $($ty)*)),
174            rustc_middle::mir::Mutability::Not,
175        )
176    };
177    ($this:ident, winapi::$ty:ident) => {
178        $this.windows_ty_layout(stringify!($ty)).ty
179    };
180    ($this:ident, $krate:ident :: $($path:ident)::+) => {
181        helpers::path_ty_layout($this, &[stringify!($krate), $(stringify!($path)),*]).ty
182    };
183    ($this:ident, $($other:tt)*) => {
184        compile_error!(concat!("unsupported signature type: ", stringify!($($other)*)))
185    }
186}
187
188impl<'tcx, const ARGS: usize> ShimSig<'tcx, ARGS> {
189    fn as_abi(&self, ecx: &MiriInterpCx<'tcx>) -> &FnAbi<'tcx, Ty<'tcx>> {
190        let mut inputs_and_output = Vec::with_capacity(ARGS.strict_add(1));
191        inputs_and_output.extend(&self.args);
192        inputs_and_output.push(self.ret);
193        let fn_sig_binder = Binder::dummy(FnSig {
194            inputs_and_output: ecx.machine.tcx.mk_type_list(&inputs_and_output),
195            fn_sig_kind: FnSigKind::default().set_c_variadic(self.c_variadic).set_abi(self.abi),
196        });
197        ecx.fn_abi_of_fn_ptr(fn_sig_binder, Default::default()).unwrap()
198    }
199}
200
201/// Helper function to compare two ABIs.
202fn check_shim_abi<'tcx>(
203    this: &MiriInterpCx<'tcx>,
204    link_name: Symbol,
205    callee_abi: &FnAbi<'tcx, Ty<'tcx>>,
206    caller_abi: &FnAbi<'tcx, Ty<'tcx>>,
207) -> InterpResult<'tcx> {
208    if callee_abi.conv != caller_abi.conv {
209        throw_ub_format!(
210            r#"ABI mismatch: `{link_name}` has calling convention "{callee}", but the caller is using calling convention "{caller}""#,
211            callee = callee_abi.conv,
212            caller = caller_abi.conv,
213        );
214    }
215    // No need to check unwinding: if the caller signature forbids unwinding, that's already
216    // reflected in the unwind destination so if an unwind occurs it will be reported as UB.
217
218    if caller_abi.c_variadic && !callee_abi.c_variadic {
219        throw_ub_format!(
220            "ABI mismatch: `{link_name}` is a non-variadic function, but the caller is using a c-variadic signature"
221        );
222    }
223    if !caller_abi.c_variadic && callee_abi.c_variadic {
224        throw_ub_format!(
225            "ABI mismatch: `{link_name}` is a c-variadic function, but the caller is using a non-variadic signature"
226        );
227    }
228
229    if callee_abi.fixed_count != caller_abi.fixed_count {
230        throw_ub_format!(
231            "ABI mismatch: calling `{link_name}` which takes {} {}argument{}, but {} argument{} given",
232            callee_abi.fixed_count,
233            if callee_abi.c_variadic { "fixed (non-variadic) " } else { "" },
234            if callee_abi.fixed_count == 1 { "" } else { "s" },
235            caller_abi.fixed_count,
236            if caller_abi.fixed_count == 1 { " was" } else { "s were" },
237        );
238    }
239
240    if !this.check_argument_compat(&caller_abi.ret, &callee_abi.ret)? {
241        throw_ub!(AbiMismatchReturn {
242            caller_ty: caller_abi.ret.layout.ty,
243            callee_ty: callee_abi.ret.layout.ty
244        });
245    }
246
247    for (idx, (caller_arg, callee_arg)) in
248        caller_abi.args.iter().zip(callee_abi.args.iter()).enumerate()
249    {
250        if !this.check_argument_compat(caller_arg, callee_arg)? {
251            throw_ub!(AbiMismatchArgument {
252                arg_idx: idx,
253                caller_ty: caller_abi.args[idx].layout.ty,
254                callee_ty: callee_abi.args[idx].layout.ty
255            });
256        }
257    }
258
259    interp_ok(())
260}
261
262/// Represents a tail of variadic arguments that have not yet been checked.
263// Deliberately not `Copy` so that we don't consume the same vararg multiple times accidentally.
264pub struct Varargs<'tcx, 'a> {
265    args: &'a [OpTy<'tcx>],
266    /// Number of arguments (variadic and fixed) that have already been taken, for error messages.
267    already_gone: usize,
268}
269
270impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
271pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
272    /// Ensure the given symbol is not exported by the program.
273    fn check_shim_symbol_clash(&self, link_name: Symbol) -> InterpResult<'tcx, ()> {
274        let this = self.eval_context_ref();
275        if let Some(instance) = this.lookup_exported_symbol(link_name)? {
276            // If compiler-builtins is providing the symbol, then don't treat it as a clash.
277            // We'll use our built-in implementation in `emulate_foreign_item_inner` for increased
278            // performance. Note that this means we won't catch any undefined behavior in
279            // compiler-builtins when running other crates, but Miri can still be run on
280            // compiler-builtins itself (or any crate that uses it as a normal dependency)
281            if this.tcx.is_compiler_builtins(instance.def_id().krate) {
282                return interp_ok(());
283            }
284
285            throw_machine_stop!(TerminationInfo::SymbolShimClashing {
286                link_name,
287                span: this.tcx.def_span(instance.def_id()).data(),
288            })
289        }
290        interp_ok(())
291    }
292
293    /// 'Lenient' signature check. Deprecated; use `check_shim_sig` instead.
294    fn check_shim_sig_deprecated<'a, const N: usize>(
295        &mut self,
296        abi: &FnAbi<'tcx, Ty<'tcx>>,
297        exp_abi: CanonAbi,
298        link_name: Symbol,
299        args: &'a [OpTy<'tcx>],
300    ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
301        self.check_shim_symbol_clash(link_name)?;
302
303        if abi.conv != exp_abi {
304            throw_ub_format!(
305                r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
306                abi.conv
307            );
308        }
309        if abi.c_variadic {
310            throw_ub_format!(
311                "calling a non-variadic function with a c-variadic caller-side signature"
312            );
313        }
314
315        if let Ok(ops) = args.try_into() {
316            return interp_ok(ops);
317        }
318        throw_ub_format!(
319            "incorrect number of arguments for `{link_name}`: got {}, expected {}",
320            args.len(),
321            N
322        )
323    }
324
325    /// Check that the given `caller_fn_abi` matches the expected ABI described by `shim_sig`, and
326    /// then returns the list of arguments.
327    fn check_shim_sig<'a, const N: usize>(
328        &self,
329        shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
330        // We take these as a tuple so that this takes less space on the caller side.
331        (link_name, caller_fn_abi, caller_args): (Symbol, &FnAbi<'tcx, Ty<'tcx>>, &'a [OpTy<'tcx>]),
332    ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
333        let this = self.eval_context_ref();
334
335        // Compute callee ABI.
336        let shim_sig = shim_sig(this);
337        assert!(!shim_sig.c_variadic);
338        let callee_fn_abi = shim_sig.as_abi(this);
339
340        // Check everything.
341        check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?;
342        this.check_shim_symbol_clash(link_name)?;
343
344        // Return arguments.
345        if let Ok(ops) = caller_args.try_into() {
346            return interp_ok(ops);
347        }
348        unreachable!()
349    }
350
351    /// Check that the given `caller_fn_abi` matches the expected ABI described by `shim_sig`, and
352    /// then returns the list of fixed and variadic arguments in separate lists.
353    fn check_shim_sig_variadic<'a, const N: usize>(
354        &self,
355        shim_sig: fn(&MiriInterpCx<'tcx>) -> ShimSig<'tcx, N>,
356        // We take these as a tuple so that this takes less space on the caller side.
357        (link_name, caller_fn_abi, caller_args): (Symbol, &FnAbi<'tcx, Ty<'tcx>>, &'a [OpTy<'tcx>]),
358    ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
359        let this = self.eval_context_ref();
360
361        // Compute callee ABI.
362        let shim_sig = shim_sig(this);
363        assert!(shim_sig.c_variadic);
364        let callee_fn_abi = shim_sig.as_abi(this);
365
366        // Check everything.
367        check_shim_abi(this, link_name, callee_fn_abi, caller_fn_abi)?;
368        this.check_shim_symbol_clash(link_name)?;
369
370        // Return arguments.
371        if let Some((fixed, var)) = caller_args.split_first_chunk() {
372            return interp_ok((fixed, Varargs { args: var, already_gone: N }));
373        }
374        unreachable!()
375    }
376
377    /// Fetches `N` arguments from `varargs`, checking their types.
378    /// Also returns the remaining varargs.
379    fn check_varargs<'a, const N: usize>(
380        &self,
381        tys: fn(&MiriInterpCx<'tcx>) -> [Ty<'tcx>; N],
382        varargs: Varargs<'tcx, 'a>,
383        fn_name: &str,
384    ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], Varargs<'tcx, 'a>)> {
385        let this = self.eval_context_ref();
386        let tys = tys(this);
387
388        let Some((now, tail)) = varargs.args.split_first_chunk::<N>() else {
389            throw_ub_format!(
390                "not enough arguments for `{fn_name}`: got {}, expected at least {}",
391                varargs.already_gone.strict_add(varargs.args.len()),
392                varargs.already_gone.strict_add(N),
393            )
394        };
395
396        for (n, (caller_gave, callee_expected)) in now.iter().zip(tys).enumerate() {
397            // Check ABI compatibility.
398            let compatible =
399                this.validate_c_variadic_compatible_ty(caller_gave.layout.ty, callee_expected)?;
400            match compatible {
401                VarArgCompatible::Compatible => {}
402                VarArgCompatible::Incompatible => {
403                    throw_ub_format!(
404                        "incorrect c-variadic argument type for `{fn_name}`: \
405                        expected argument #{n} to have type `{callee_expected}` but got incompatible type `{caller_ty}`",
406                        n = varargs.already_gone.strict_add(n).strict_add(1),
407                        caller_ty = caller_gave.layout.ty,
408                    );
409                }
410                VarArgCompatible::CastIntTo { source_is_signed } => {
411                    // Check that the value can be represented in the target type.
412                    let size = caller_gave.layout.size;
413                    let scalar = this.read_scalar(caller_gave)?;
414                    if scalar.to_int(size)? < 0 {
415                        throw_ub_format!(
416                            "incorrect c-variadic argument type for `{fn_name}`: \
417                            argument #{n} has value `{value}_{caller_ty}` which cannot be represented in expected type `{callee_expected}`",
418                            n = varargs.already_gone.strict_add(n).strict_add(1),
419                            caller_ty = caller_gave.layout.ty,
420                            value = if source_is_signed {
421                                scalar.to_int(size)?.to_string()
422                            } else {
423                                scalar.to_uint(size)?.to_string()
424                            }
425                        )
426                    }
427                }
428            }
429        }
430
431        interp_ok((now, Varargs { args: tail, already_gone: varargs.already_gone.strict_add(N) }))
432    }
433
434    /// Check that the given function has the expected amount of arguments, and then
435    /// return the list of arguments.
436    ///
437    /// This may only be used for `extern "llvm-intrinsic"` LLVM intrinsics.
438    fn check_shim_sig_llvm_intrinsic<'a, const N: usize>(
439        &mut self,
440        link_name: Symbol,
441        args: &'a [OpTy<'tcx>],
442    ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
443        assert!(link_name.as_str().starts_with("llvm."));
444
445        self.check_shim_symbol_clash(link_name)?;
446
447        if let Ok(ops) = args.try_into() {
448            return interp_ok(ops);
449        }
450        throw_ub_format!(
451            "incorrect number of arguments for `{link_name}`: got {}, expected {}",
452            args.len(),
453            N
454        )
455    }
456}