Skip to main content

miri/
helpers.rs

1use std::num::NonZero;
2use std::sync::Mutex;
3use std::time::Duration;
4use std::{cmp, iter};
5
6use rand::RngCore;
7use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
8use rustc_data_structures::fx::{FxBuildHasher, FxHashSet};
9use rustc_hir::Safety;
10use rustc_hir::def::{DefKind, Namespace};
11use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE};
12use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
13use rustc_middle::middle::dependency_format::Linkage;
14use rustc_middle::middle::exported_symbols::ExportedSymbol;
15use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout};
16use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy};
17use rustc_session::config::CrateType;
18use rustc_span::{Span, Symbol};
19use rustc_symbol_mangling::mangle_internal_symbol;
20use rustc_target::spec::Os;
21
22use crate::*;
23
24/// Gets an instance for a path.
25///
26/// A `None` namespace indicates we are looking for a module.
27fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>) -> Option<DefId> {
28    let _trace = enter_trace_span!("try_resolve_did", ?path);
29
30    /// Yield all children of the given item, that have the given name.
31    fn find_children<'tcx: 'a, 'a>(
32        tcx: TyCtxt<'tcx>,
33        item: DefId,
34        name: &'a str,
35    ) -> impl Iterator<Item = DefId> + 'a {
36        let name = Symbol::intern(name);
37        tcx.module_children(item)
38            .iter()
39            .filter(move |item| item.ident.name == name)
40            .map(move |item| item.res.def_id())
41    }
42
43    // Take apart the path: leading crate, a sequence of modules, and potentially a final item.
44    let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
45    let (modules, item) = if let Some(namespace) = namespace {
46        let (&item_name, modules) =
47            path.split_last().expect("non-module paths must have at least 2 segments");
48        (modules, Some((item_name, namespace)))
49    } else {
50        (path, None)
51    };
52
53    // There may be more than one crate with this name. We try them all.
54    // (This is particularly relevant when running `std` tests as then there are two `std` crates:
55    // the one in the sysroot and the one locally built by `cargo test`.)
56    // FIXME: can we prefer the one from the sysroot?
57    'crates: for krate in
58        tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
59    {
60        let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
61        // Go over the modules.
62        for &segment in modules {
63            let Some(next_item) = find_children(tcx, cur_item, segment)
64                .find(|&item| tcx.def_kind(item) == DefKind::Mod)
65            else {
66                continue 'crates;
67            };
68            cur_item = next_item;
69        }
70        // Finally, look up the desired item in this module, if any.
71        match item {
72            Some((item_name, namespace)) => {
73                let Some(item) = find_children(tcx, cur_item, item_name)
74                    .find(|&item| tcx.def_kind(item).ns() == Some(namespace))
75                else {
76                    continue 'crates;
77                };
78                return Some(item);
79            }
80            None => {
81                // Just return the module.
82                return Some(cur_item);
83            }
84        }
85    }
86    // Item not found in any of the crates with the right name.
87    None
88}
89
90/// Gets an instance for a path; fails gracefully if the path does not exist.
91pub fn try_resolve_path<'tcx>(
92    tcx: TyCtxt<'tcx>,
93    path: &[&str],
94    namespace: Namespace,
95) -> Option<ty::Instance<'tcx>> {
96    let did = try_resolve_did(tcx, path, Some(namespace))?;
97    Some(ty::Instance::mono(tcx, did))
98}
99
100/// Gets an instance for a path.
101#[track_caller]
102pub fn resolve_path<'tcx>(
103    tcx: TyCtxt<'tcx>,
104    path: &[&str],
105    namespace: Namespace,
106) -> ty::Instance<'tcx> {
107    try_resolve_path(tcx, path, namespace)
108        .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
109}
110
111/// Gets the layout of a type at a path.
112#[track_caller]
113pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
114    let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
115    cx.layout_of(ty).to_result().ok().unwrap()
116}
117
118/// Call `f` for each exported symbol.
119pub fn iter_exported_symbols<'tcx>(
120    tcx: TyCtxt<'tcx>,
121    mut f: impl FnMut(CrateNum, DefId) -> InterpResult<'tcx>,
122) -> InterpResult<'tcx> {
123    // First, the symbols in the local crate. We can't use `exported_symbols` here as that
124    // skips `#[used]` statics (since `reachable_set` skips them in binary crates).
125    // So we walk all HIR items ourselves instead.
126    let crate_items = tcx.hir_crate_items(());
127    for def_id in crate_items.definitions() {
128        let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
129            let codegen_attrs = tcx.codegen_fn_attrs(def_id);
130            codegen_attrs.contains_extern_indicator()
131                || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
132                || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
133        };
134        if exported {
135            f(LOCAL_CRATE, def_id.into())?;
136        }
137    }
138
139    // Next, all our dependencies.
140    // `dependency_formats` includes all the transitive information needed to link a crate,
141    // which is what we need here since we need to dig out `exported_symbols` from all transitive
142    // dependencies.
143    let dependency_formats = tcx.dependency_formats(());
144    // Find the dependencies of the executable we are running.
145    let dependency_format = dependency_formats
146        .get(&CrateType::Executable)
147        .expect("interpreting a non-executable crate");
148    for cnum in dependency_format
149        .iter_enumerated()
150        .filter_map(|(num, &linkage)| (linkage != Linkage::NotLinked).then_some(num))
151    {
152        if cnum == LOCAL_CRATE {
153            continue; // Already handled above
154        }
155
156        // We can ignore `_export_info` here: we are a Rust crate, and everything is exported
157        // from a Rust crate.
158        for &(symbol, _export_info) in tcx.exported_non_generic_symbols(cnum) {
159            if let ExportedSymbol::NonGeneric(def_id) = symbol {
160                f(cnum, def_id)?;
161            }
162        }
163    }
164    interp_ok(())
165}
166
167impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
168pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
169    /// Checks if the given crate/module exists.
170    fn have_module(&self, path: &[&str]) -> bool {
171        try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
172    }
173
174    /// Evaluates the scalar at the specified path.
175    fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> {
176        let this = self.eval_context_ref();
177        let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
178        // We don't give a span -- this isn't actually used directly by the program anyway.
179        this.eval_global(instance).unwrap_or_else(|err| {
180            panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
181        })
182    }
183    fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
184        let this = self.eval_context_ref();
185        let val = this.eval_path(path);
186        this.read_scalar(&val)
187            .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
188    }
189
190    /// Helper function to get a `libc` constant as a `Scalar`.
191    fn eval_libc(&self, name: &str) -> Scalar {
192        if self.eval_context_ref().tcx.sess.target.os == Os::Windows {
193            panic!(
194                "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
195            );
196        }
197        self.eval_path_scalar(&["libc", name])
198    }
199
200    /// Helper function to get a `libc` constant as an `i32`.
201    fn eval_libc_i32(&self, name: &str) -> i32 {
202        // TODO: Cache the result.
203        self.eval_libc(name).to_i32().unwrap_or_else(|_err| {
204            panic!("required libc item has unexpected type (not `i32`): {name}")
205        })
206    }
207
208    /// Helper function to get a `libc` constant as an `u32`.
209    fn eval_libc_u32(&self, name: &str) -> u32 {
210        // TODO: Cache the result.
211        self.eval_libc(name).to_u32().unwrap_or_else(|_err| {
212            panic!("required libc item has unexpected type (not `u32`): {name}")
213        })
214    }
215
216    /// Helper function to get a `libc` constant as an `u64`.
217    fn eval_libc_u64(&self, name: &str) -> u64 {
218        // TODO: Cache the result.
219        self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
220            panic!("required libc item has unexpected type (not `u64`): {name}")
221        })
222    }
223
224    /// Helper function to get a `windows` constant as a `Scalar`.
225    fn eval_windows(&self, module: &str, name: &str) -> Scalar {
226        self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
227    }
228
229    /// Helper function to get a `windows` constant as a `u32`.
230    fn eval_windows_u32(&self, module: &str, name: &str) -> u32 {
231        // TODO: Cache the result.
232        self.eval_windows(module, name).to_u32().unwrap_or_else(|_err| {
233            panic!("required Windows item has unexpected type (not `u32`): {module}::{name}")
234        })
235    }
236
237    /// Helper function to get a `windows` constant as a `u64`.
238    fn eval_windows_u64(&self, module: &str, name: &str) -> u64 {
239        // TODO: Cache the result.
240        self.eval_windows(module, name).to_u64().unwrap_or_else(|_err| {
241            panic!("required Windows item has unexpected type (not `u64`): {module}::{name}")
242        })
243    }
244
245    /// Helper function to get the `TyAndLayout` of a `libc` type
246    fn libc_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
247        let this = self.eval_context_ref();
248        if this.tcx.sess.target.os == Os::Windows {
249            panic!(
250                "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
251            );
252        }
253        path_ty_layout(this, &["libc", name])
254    }
255
256    /// Helper function to get the `TyAndLayout` of a `windows` type
257    fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
258        let this = self.eval_context_ref();
259        path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
260    }
261
262    /// Helper function to get `TyAndLayout` of an array that consists of `libc` type.
263    fn libc_array_ty_layout(&self, name: &str, size: u64) -> TyAndLayout<'tcx> {
264        let this = self.eval_context_ref();
265        let elem_ty_layout = this.libc_ty_layout(name);
266        let array_ty = Ty::new_array(*this.tcx, elem_ty_layout.ty, size);
267        this.layout_of(array_ty).unwrap()
268    }
269
270    /// Project to the given *named* field (which must be a struct or union type).
271    fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
272        &self,
273        base: &P,
274        name: &str,
275    ) -> InterpResult<'tcx, Option<P>> {
276        let this = self.eval_context_ref();
277        let adt = base.layout().ty.ty_adt_def().unwrap();
278        for (idx, field) in adt.non_enum_variant().fields.iter_enumerated() {
279            if field.name.as_str() == name {
280                return interp_ok(Some(this.project_field(base, idx)?));
281            }
282        }
283        interp_ok(None)
284    }
285
286    /// Project to the given *named* field (which must be a struct or union type).
287    fn project_field_named<P: Projectable<'tcx, Provenance>>(
288        &self,
289        base: &P,
290        name: &str,
291    ) -> InterpResult<'tcx, P> {
292        interp_ok(
293            self.try_project_field_named(base, name)?
294                .unwrap_or_else(|| bug!("no field named {} in type {}", name, base.layout().ty)),
295        )
296    }
297
298    /// Write an int of the appropriate size to `dest`. The target type may be signed or unsigned,
299    /// we try to do the right thing anyway. `i128` can fit all integer types except for `u128` so
300    /// this method is fine for almost all integer types.
301    fn write_int(
302        &mut self,
303        i: impl Into<i128>,
304        dest: &impl Writeable<'tcx, Provenance>,
305    ) -> InterpResult<'tcx> {
306        assert!(
307            dest.layout().backend_repr.is_scalar(),
308            "write_int on non-scalar type {}",
309            dest.layout().ty
310        );
311        let val = if dest.layout().backend_repr.is_signed() {
312            Scalar::from_int(i, dest.layout().size)
313        } else {
314            // `unwrap` can only fail here if `i` is negative
315            Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
316        };
317        self.eval_context_mut().write_scalar(val, dest)
318    }
319
320    /// Write the first N fields of the given place.
321    fn write_int_fields(
322        &mut self,
323        values: &[i128],
324        dest: &impl Writeable<'tcx, Provenance>,
325    ) -> InterpResult<'tcx> {
326        let this = self.eval_context_mut();
327        for (idx, &val) in values.iter().enumerate() {
328            let idx = FieldIdx::from_usize(idx);
329            let field = this.project_field(dest, idx)?;
330            this.write_int(val, &field)?;
331        }
332        interp_ok(())
333    }
334
335    /// Write the given fields of the given place.
336    fn write_int_fields_named(
337        &mut self,
338        values: &[(&str, i128)],
339        dest: &impl Writeable<'tcx, Provenance>,
340    ) -> InterpResult<'tcx> {
341        let this = self.eval_context_mut();
342        for &(name, val) in values.iter() {
343            let field = this.project_field_named(dest, name)?;
344            this.write_int(val, &field)?;
345        }
346        interp_ok(())
347    }
348
349    /// Write a 0 of the appropriate size to `dest`.
350    fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> {
351        self.write_int(0, dest)
352    }
353
354    /// Test if this pointer equals 0.
355    fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
356        interp_ok(ptr.addr().bytes() == 0)
357    }
358
359    /// Generate some random bytes, and write them to `dest`.
360    fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
361        // Some programs pass in a null pointer and a length of 0
362        // to their platform's random-generation function (e.g. getrandom())
363        // on Linux. For compatibility with these programs, we don't perform
364        // any additional checks - it's okay if the pointer is invalid,
365        // since we wouldn't actually be writing to it.
366        if len == 0 {
367            return interp_ok(());
368        }
369        let this = self.eval_context_mut();
370
371        let mut data = vec![0; usize::try_from(len).unwrap()];
372
373        if this.machine.communicate() {
374            // Fill the buffer using the host's rng.
375            getrandom::fill(&mut data)
376                .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
377        } else {
378            let rng = this.machine.rng.get_mut();
379            rng.fill_bytes(&mut data);
380        }
381
382        this.write_bytes_ptr(ptr, data.iter().copied())
383    }
384
385    /// Call a function: Push the stack frame and pass the arguments.
386    /// For now, arguments must be scalars (so that the caller does not have to know the layout).
387    ///
388    /// If you do not provide a return place, a dangling zero-sized place will be created
389    /// for your convenience. This is only valid if the return type is `()`.
390    fn call_function(
391        &mut self,
392        f: ty::Instance<'tcx>,
393        caller_abi: ExternAbi,
394        args: &[ImmTy<'tcx>],
395        dest: Option<&MPlaceTy<'tcx>>,
396        cont: ReturnContinuation,
397    ) -> InterpResult<'tcx> {
398        let this = self.eval_context_mut();
399
400        // Get MIR.
401        let mir = this.load_mir(f.def, None)?;
402        let dest = match dest {
403            Some(dest) => dest.clone(),
404            None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit),
405        };
406
407        // Construct a function pointer type representing the caller perspective.
408        let sig = this.tcx.mk_fn_sig(
409            args.iter().map(|a| a.layout.ty),
410            dest.layout.ty,
411            /*c_variadic*/ false,
412            Safety::Safe,
413            caller_abi,
414        );
415        let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
416
417        // This will also show proper errors if there is any ABI mismatch.
418        this.init_stack_frame(
419            f,
420            mir,
421            caller_fn_abi,
422            &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
423            /*with_caller_location*/ false,
424            &dest.into(),
425            cont,
426        )
427    }
428
429    /// Call a function in an "empty" thread.
430    fn call_thread_root_function(
431        &mut self,
432        f: ty::Instance<'tcx>,
433        caller_abi: ExternAbi,
434        args: &[ImmTy<'tcx>],
435        dest: Option<&MPlaceTy<'tcx>>,
436        span: Span,
437    ) -> InterpResult<'tcx> {
438        let this = self.eval_context_mut();
439        assert!(this.active_thread_stack().is_empty());
440        assert!(this.active_thread_ref().origin_span.is_dummy());
441        this.active_thread_mut().origin_span = span;
442        this.call_function(f, caller_abi, args, dest, ReturnContinuation::Stop { cleanup: true })
443    }
444
445    /// Visits the memory covered by `place`, sensitive to freezing: the 2nd parameter
446    /// of `action` will be true if this is frozen, false if this is in an `UnsafeCell`.
447    /// The range is relative to `place`.
448    fn visit_freeze_sensitive(
449        &self,
450        place: &MPlaceTy<'tcx>,
451        size: Size,
452        mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
453    ) -> InterpResult<'tcx> {
454        let this = self.eval_context_ref();
455        trace!("visit_frozen(place={:?}, size={:?})", *place, size);
456        debug_assert_eq!(
457            size,
458            this.size_and_align_of_val(place)?
459                .map(|(size, _)| size)
460                .unwrap_or_else(|| place.layout.size)
461        );
462        // Store how far we proceeded into the place so far. Everything to the left of
463        // this offset has already been handled, in the sense that the frozen parts
464        // have had `action` called on them.
465        let start_addr = place.ptr().addr();
466        let mut cur_addr = start_addr;
467        // Called when we detected an `UnsafeCell` at the given offset and size.
468        // Calls `action` and advances `cur_ptr`.
469        let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
470            // We assume that we are given the fields in increasing offset order,
471            // and nothing else changes.
472            let unsafe_cell_addr = unsafe_cell_ptr.addr();
473            assert!(unsafe_cell_addr >= cur_addr);
474            let frozen_size = unsafe_cell_addr - cur_addr;
475            // Everything between the cur_ptr and this `UnsafeCell` is frozen.
476            if frozen_size != Size::ZERO {
477                action(alloc_range(cur_addr - start_addr, frozen_size), /*frozen*/ true)?;
478            }
479            cur_addr += frozen_size;
480            // This `UnsafeCell` is NOT frozen.
481            if unsafe_cell_size != Size::ZERO {
482                action(
483                    alloc_range(cur_addr - start_addr, unsafe_cell_size),
484                    /*frozen*/ false,
485                )?;
486            }
487            cur_addr += unsafe_cell_size;
488            // Done
489            interp_ok(())
490        };
491        // Run a visitor
492        {
493            let mut visitor = UnsafeCellVisitor {
494                ecx: this,
495                unsafe_cell_action: |place| {
496                    trace!("unsafe_cell_action on {:?}", place.ptr());
497                    // We need a size to go on.
498                    let unsafe_cell_size = this
499                        .size_and_align_of_val(place)?
500                        .map(|(size, _)| size)
501                        // for extern types, just cover what we can
502                        .unwrap_or_else(|| place.layout.size);
503                    // Now handle this `UnsafeCell`, unless it is empty.
504                    if unsafe_cell_size != Size::ZERO {
505                        unsafe_cell_action(&place.ptr(), unsafe_cell_size)
506                    } else {
507                        interp_ok(())
508                    }
509                },
510            };
511            visitor.visit_value(place)?;
512        }
513        // The part between the end_ptr and the end of the place is also frozen.
514        // So pretend there is a 0-sized `UnsafeCell` at the end.
515        unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?;
516        // Done!
517        return interp_ok(());
518
519        /// Visiting the memory covered by a `MemPlace`, being aware of
520        /// whether we are inside an `UnsafeCell` or not.
521        struct UnsafeCellVisitor<'ecx, 'tcx, F>
522        where
523            F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
524        {
525            ecx: &'ecx MiriInterpCx<'tcx>,
526            unsafe_cell_action: F,
527        }
528
529        impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
530        where
531            F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
532        {
533            type V = MPlaceTy<'tcx>;
534
535            #[inline(always)]
536            fn ecx(&self) -> &MiriInterpCx<'tcx> {
537                self.ecx
538            }
539
540            // Hook to detect `UnsafeCell`.
541            fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
542                trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
543                let is_unsafe_cell = match v.layout.ty.kind() {
544                    ty::Adt(adt, _) =>
545                        Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
546                    _ => false,
547                };
548                if is_unsafe_cell {
549                    // We do not have to recurse further, this is an `UnsafeCell`.
550                    (self.unsafe_cell_action)(v)
551                } else if self.ecx.type_is_freeze(v.layout.ty) {
552                    // This is `Freeze`, there cannot be an `UnsafeCell`
553                    interp_ok(())
554                } else if matches!(v.layout.fields, FieldsShape::Union(..)) {
555                    // A (non-frozen) union. We fall back to whatever the type says.
556                    (self.unsafe_cell_action)(v)
557                } else {
558                    // We want to not actually read from memory for this visit. So, before
559                    // walking this value, we have to make sure it is not a
560                    // `Variants::Multiple`.
561                    // FIXME: the current logic here is layout-dependent, so enums with
562                    // multiple variants where all but 1 are uninhabited will be recursed into.
563                    // Is that truly what we want?
564                    match v.layout.variants {
565                        Variants::Multiple { .. } => {
566                            // A multi-variant enum, or coroutine, or so.
567                            // Treat this like a union: without reading from memory,
568                            // we cannot determine the variant we are in. Reading from
569                            // memory would be subject to Stacked Borrows rules, leading
570                            // to all sorts of "funny" recursion.
571                            // We only end up here if the type is *not* freeze, so we just call the
572                            // `UnsafeCell` action.
573                            (self.unsafe_cell_action)(v)
574                        }
575                        Variants::Single { .. } | Variants::Empty => {
576                            // Proceed further, try to find where exactly that `UnsafeCell`
577                            // is hiding.
578                            self.walk_value(v)
579                        }
580                    }
581                }
582            }
583
584            fn visit_union(
585                &mut self,
586                _v: &MPlaceTy<'tcx>,
587                _fields: NonZero<usize>,
588            ) -> InterpResult<'tcx> {
589                bug!("we should have already handled unions in `visit_value`")
590            }
591        }
592    }
593
594    /// Helper function used inside the shims of foreign functions to check that isolation is
595    /// disabled. It returns an error using the `name` of the foreign function if this is not the
596    /// case.
597    fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
598        if !self.eval_context_ref().machine.communicate() {
599            self.reject_in_isolation(name, RejectOpWith::Abort)?;
600        }
601        interp_ok(())
602    }
603
604    /// Helper function used inside the shims of foreign functions which reject the op
605    /// when isolation is enabled. It is used to print a warning/backtrace about the rejection.
606    fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> InterpResult<'tcx> {
607        let this = self.eval_context_ref();
608        match reject_with {
609            RejectOpWith::Abort => isolation_abort_error(op_name),
610            RejectOpWith::WarningWithoutBacktrace => {
611                // Deduplicate these warnings *by shim* (not by span)
612                static DEDUP: Mutex<FxHashSet<String>> =
613                    Mutex::new(FxHashSet::with_hasher(FxBuildHasher));
614                let mut emitted_warnings = DEDUP.lock().unwrap();
615                if !emitted_warnings.contains(op_name) {
616                    // First time we are seeing this.
617                    emitted_warnings.insert(op_name.to_owned());
618                    this.tcx
619                        .dcx()
620                        .warn(format!("{op_name} was made to return an error due to isolation"));
621                }
622
623                interp_ok(())
624            }
625            RejectOpWith::Warning => {
626                this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
627                interp_ok(())
628            }
629            RejectOpWith::NoWarning => interp_ok(()), // no warning
630        }
631    }
632
633    /// Helper function used inside the shims of foreign functions to assert that the target OS
634    /// is `target_os`. It panics showing a message with the `name` of the foreign function
635    /// if this is not the case.
636    fn assert_target_os(&self, target_os: Os, name: &str) {
637        assert_eq!(
638            self.eval_context_ref().tcx.sess.target.os,
639            target_os,
640            "`{name}` is only available on the `{target_os}` target OS",
641        )
642    }
643
644    /// Helper function used inside shims of foreign functions to check that the target OS
645    /// is one of `target_oses`. It returns an error containing the `name` of the foreign function
646    /// in a message if this is not the case.
647    fn check_target_os(&self, target_oses: &[Os], name: Symbol) -> InterpResult<'tcx> {
648        let target_os = &self.eval_context_ref().tcx.sess.target.os;
649        if !target_oses.contains(target_os) {
650            throw_unsup_format!("`{name}` is not supported on {target_os}");
651        }
652        interp_ok(())
653    }
654
655    /// Helper function used inside the shims of foreign functions to assert that the target OS
656    /// is part of the UNIX family. It panics showing a message with the `name` of the foreign function
657    /// if this is not the case.
658    fn assert_target_os_is_unix(&self, name: &str) {
659        assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",);
660    }
661
662    fn target_os_is_unix(&self) -> bool {
663        self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
664    }
665
666    /// Dereference a pointer operand to a place using `layout` instead of the pointer's declared type
667    fn deref_pointer_as(
668        &self,
669        op: &impl Projectable<'tcx, Provenance>,
670        layout: TyAndLayout<'tcx>,
671    ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
672        let this = self.eval_context_ref();
673        let ptr = this.read_pointer(op)?;
674        interp_ok(this.ptr_to_mplace(ptr, layout))
675    }
676
677    /// Calculates the MPlaceTy given the offset and layout of an access on an operand
678    fn deref_pointer_and_offset(
679        &self,
680        op: &impl Projectable<'tcx, Provenance>,
681        offset: u64,
682        base_layout: TyAndLayout<'tcx>,
683        value_layout: TyAndLayout<'tcx>,
684    ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
685        let this = self.eval_context_ref();
686        let op_place = this.deref_pointer_as(op, base_layout)?;
687        let offset = Size::from_bytes(offset);
688
689        // Ensure that the access is within bounds.
690        assert!(base_layout.size >= offset + value_layout.size);
691        let value_place = op_place.offset(offset, value_layout, this)?;
692        interp_ok(value_place)
693    }
694
695    fn deref_pointer_and_read(
696        &self,
697        op: &impl Projectable<'tcx, Provenance>,
698        offset: u64,
699        base_layout: TyAndLayout<'tcx>,
700        value_layout: TyAndLayout<'tcx>,
701    ) -> InterpResult<'tcx, Scalar> {
702        let this = self.eval_context_ref();
703        let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
704        this.read_scalar(&value_place)
705    }
706
707    fn deref_pointer_and_write(
708        &mut self,
709        op: &impl Projectable<'tcx, Provenance>,
710        offset: u64,
711        value: impl Into<Scalar>,
712        base_layout: TyAndLayout<'tcx>,
713        value_layout: TyAndLayout<'tcx>,
714    ) -> InterpResult<'tcx, ()> {
715        let this = self.eval_context_mut();
716        let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
717        this.write_scalar(value, &value_place)
718    }
719
720    /// Parse a `timespec` struct and return it as a `std::time::Duration`. It returns `None`
721    /// if the value in the `timespec` struct is invalid. Some libc functions will return
722    /// `EINVAL` in this case.
723    fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> {
724        let this = self.eval_context_mut();
725        let seconds_place = this.project_field(tp, FieldIdx::ZERO)?;
726        let seconds_scalar = this.read_scalar(&seconds_place)?;
727        let seconds = seconds_scalar.to_target_isize(this)?;
728        let nanoseconds_place = this.project_field(tp, FieldIdx::ONE)?;
729        let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?;
730        let nanoseconds = nanoseconds_scalar.to_target_isize(this)?;
731
732        interp_ok(try {
733            // tv_sec must be non-negative.
734            let seconds: u64 = seconds.try_into().ok()?;
735            // tv_nsec must be non-negative.
736            let nanoseconds: u32 = nanoseconds.try_into().ok()?;
737            if nanoseconds >= 1_000_000_000 {
738                // tv_nsec must not be greater than 999,999,999.
739                None?
740            }
741            Duration::new(seconds, nanoseconds)
742        })
743    }
744
745    /// Read bytes from a byte slice.
746    fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
747    where
748        'tcx: 'a,
749    {
750        let this = self.eval_context_ref();
751        let (ptr, len) = slice.to_scalar_pair();
752        let ptr = ptr.to_pointer(this)?;
753        let len = len.to_target_usize(this)?;
754        let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
755        interp_ok(bytes)
756    }
757
758    /// Read a sequence of bytes until the first null terminator.
759    fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
760    where
761        'tcx: 'a,
762    {
763        let this = self.eval_context_ref();
764        let size1 = Size::from_bytes(1);
765
766        // Step 1: determine the length.
767        let mut len = Size::ZERO;
768        loop {
769            // FIXME: We are re-getting the allocation each time around the loop.
770            // Would be nice if we could somehow "extend" an existing AllocRange.
771            let alloc = this.get_ptr_alloc(ptr.wrapping_offset(len, this), size1)?.unwrap(); // not a ZST, so we will get a result
772            let byte = alloc.read_integer(alloc_range(Size::ZERO, size1))?.to_u8()?;
773            if byte == 0 {
774                break;
775            } else {
776                len += size1;
777            }
778        }
779
780        // Step 2: get the bytes.
781        this.read_bytes_ptr_strip_provenance(ptr, len)
782    }
783
784    /// Helper function to write a sequence of bytes with an added null-terminator, which is what
785    /// the Unix APIs usually handle. This function returns `Ok((false, length))` without trying
786    /// to write if `size` is not large enough to fit the contents of `c_str` plus a null
787    /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
788    /// string length returned does include the null terminator.
789    fn write_c_str(
790        &mut self,
791        c_str: &[u8],
792        ptr: Pointer,
793        size: u64,
794    ) -> InterpResult<'tcx, (bool, u64)> {
795        // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required null
796        // terminator to memory using the `ptr` pointer would cause an out-of-bounds access.
797        let string_length = u64::try_from(c_str.len()).unwrap();
798        let string_length = string_length.strict_add(1);
799        if size < string_length {
800            return interp_ok((false, string_length));
801        }
802        self.eval_context_mut()
803            .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
804        interp_ok((true, string_length))
805    }
806
807    /// Helper function to read a sequence of unsigned integers of the given size and alignment
808    /// until the first null terminator.
809    fn read_c_str_with_char_size<T>(
810        &self,
811        mut ptr: Pointer,
812        size: Size,
813        align: Align,
814    ) -> InterpResult<'tcx, Vec<T>>
815    where
816        T: TryFrom<u128>,
817        <T as TryFrom<u128>>::Error: std::fmt::Debug,
818    {
819        assert_ne!(size, Size::ZERO);
820
821        let this = self.eval_context_ref();
822
823        this.check_ptr_align(ptr, align)?;
824
825        let mut wchars = Vec::new();
826        loop {
827            // FIXME: We are re-getting the allocation each time around the loop.
828            // Would be nice if we could somehow "extend" an existing AllocRange.
829            let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); // not a ZST, so we will get a result
830            let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
831            if wchar_int == 0 {
832                break;
833            } else {
834                wchars.push(wchar_int.try_into().unwrap());
835                ptr = ptr.wrapping_offset(size, this);
836            }
837        }
838
839        interp_ok(wchars)
840    }
841
842    /// Read a sequence of u16 until the first null terminator.
843    fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
844        self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
845    }
846
847    /// Helper function to write a sequence of u16 with an added 0x0000-terminator, which is what
848    /// the Windows APIs usually handle. This function returns `Ok((false, length))` without trying
849    /// to write if `size` is not large enough to fit the contents of `os_string` plus a null
850    /// terminator. It returns `Ok((true, length))` if the writing process was successful. The
851    /// string length returned does include the null terminator. Length is measured in units of
852    /// `u16.`
853    fn write_wide_str(
854        &mut self,
855        wide_str: &[u16],
856        ptr: Pointer,
857        size: u64,
858    ) -> InterpResult<'tcx, (bool, u64)> {
859        // If `size` is smaller or equal than `bytes.len()`, writing `bytes` plus the required
860        // 0x0000 terminator to memory would cause an out-of-bounds access.
861        let string_length = u64::try_from(wide_str.len()).unwrap();
862        let string_length = string_length.strict_add(1);
863        if size < string_length {
864            return interp_ok((false, string_length));
865        }
866
867        // Store the UTF-16 string.
868        let size2 = Size::from_bytes(2);
869        let this = self.eval_context_mut();
870        this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
871        let mut alloc = this.get_ptr_alloc_mut(ptr, size2 * string_length)?.unwrap(); // not a ZST, so we will get a result
872        for (offset, wchar) in wide_str.iter().copied().chain(iter::once(0x0000)).enumerate() {
873            let offset = u64::try_from(offset).unwrap();
874            alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
875        }
876        interp_ok((true, string_length))
877    }
878
879    /// Read a sequence of wchar_t until the first null terminator.
880    /// Always returns a `Vec<u32>` no matter the size of `wchar_t`.
881    fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
882        let this = self.eval_context_ref();
883        let wchar_t = if this.tcx.sess.target.os == Os::Windows {
884            // We don't have libc on Windows so we have to hard-code the type ourselves.
885            this.machine.layouts.u16
886        } else {
887            this.libc_ty_layout("wchar_t")
888        };
889        self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
890    }
891
892    fn frame_in_std(&self) -> bool {
893        let this = self.eval_context_ref();
894        let frame = this.frame();
895        // Make an attempt to get at the instance of the function this is inlined from.
896        let instance: Option<_> = try {
897            let scope = frame.current_source_info()?.scope;
898            let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
899            let source = &frame.body().source_scopes[inlined_parent];
900            source.inlined.expect("inlined_parent_scope points to scope without inline info").0
901        };
902        // Fall back to the instance of the function itself.
903        let instance = instance.unwrap_or(frame.instance());
904        // Now check the crate it is in. We could try to be clever here and e.g. check if this is
905        // the same crate as `start_fn`, but that would not work for running std tests in Miri, so
906        // we'd need some more hacks anyway. So we just check the name of the crate. If someone
907        // calls their crate `std` then we'll just let them keep the pieces.
908        let frame_crate = this.tcx.def_path(instance.def_id()).krate;
909        let crate_name = this.tcx.crate_name(frame_crate);
910        let crate_name = crate_name.as_str();
911        // On miri-test-libstd, the name of the crate is different.
912        crate_name == "std" || crate_name == "std_miri_test"
913    }
914
915    /// Mark a machine allocation that was just created as immutable.
916    fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
917        let this = self.eval_context_mut();
918        // This got just allocated, so there definitely is a pointer here.
919        let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
920        this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap();
921    }
922
923    /// Returns an integer type that is twice wide as `ty`
924    fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
925        let this = self.eval_context_ref();
926        match ty.kind() {
927            // Unsigned
928            ty::Uint(UintTy::U8) => this.tcx.types.u16,
929            ty::Uint(UintTy::U16) => this.tcx.types.u32,
930            ty::Uint(UintTy::U32) => this.tcx.types.u64,
931            ty::Uint(UintTy::U64) => this.tcx.types.u128,
932            // Signed
933            ty::Int(IntTy::I8) => this.tcx.types.i16,
934            ty::Int(IntTy::I16) => this.tcx.types.i32,
935            ty::Int(IntTy::I32) => this.tcx.types.i64,
936            ty::Int(IntTy::I64) => this.tcx.types.i128,
937            _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
938        }
939    }
940
941    /// Checks that target feature `target_feature` is enabled.
942    ///
943    /// If not enabled, emits an UB error that states that the feature is
944    /// required by `intrinsic`.
945    fn expect_target_feature_for_intrinsic(
946        &self,
947        intrinsic: Symbol,
948        target_feature: &str,
949    ) -> InterpResult<'tcx, ()> {
950        let this = self.eval_context_ref();
951        if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) {
952            throw_ub_format!(
953                "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
954            );
955        }
956        interp_ok(())
957    }
958
959    /// Lookup an array of immediates from any linker sections matching the provided predicate,
960    /// with the spans of where they were found.
961    fn lookup_link_section(
962        &mut self,
963        include_name: impl Fn(&str) -> bool,
964    ) -> InterpResult<'tcx, Vec<(ImmTy<'tcx>, Span)>> {
965        let this = self.eval_context_mut();
966        let tcx = this.tcx.tcx;
967
968        let mut array = vec![];
969
970        iter_exported_symbols(tcx, |_cnum, def_id| {
971            let attrs = tcx.codegen_fn_attrs(def_id);
972            let Some(link_section) = attrs.link_section else {
973                return interp_ok(());
974            };
975            if include_name(link_section.as_str()) {
976                let instance = ty::Instance::mono(tcx, def_id);
977                let span = tcx.def_span(def_id);
978                let const_val = this.eval_global(instance).unwrap_or_else(|err| {
979                    panic!(
980                        "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
981                    )
982                });
983                match const_val.layout.ty.kind() {
984                    ty::FnPtr(..) => {
985                        array.push((this.read_immediate(&const_val)?, span));
986                    }
987                    ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
988                        let mut elems = this.project_array_fields(&const_val)?;
989                        while let Some((_idx, elem)) = elems.next(this)? {
990                            array.push((this.read_immediate(&elem)?, span));
991                        }
992                    }
993                    _ =>
994                        throw_unsup_format!(
995                            "only function pointers and arrays of function pointers are supported in well-known linker sections"
996                        ),
997                }
998            }
999            interp_ok(())
1000        })?;
1001
1002        interp_ok(array)
1003    }
1004
1005    fn mangle_internal_symbol<'a>(&'a mut self, name: &'static str) -> &'a str
1006    where
1007        'tcx: 'a,
1008    {
1009        let this = self.eval_context_mut();
1010        let tcx = *this.tcx;
1011        this.machine
1012            .mangle_internal_symbol_cache
1013            .entry(name)
1014            .or_insert_with(|| mangle_internal_symbol(tcx, name))
1015    }
1016}
1017
1018impl<'tcx> MiriMachine<'tcx> {
1019    /// Get the current span in the topmost function which is workspace-local and not
1020    /// `#[track_caller]`.
1021    /// This function is backed by a cache, and can be assumed to be very fast.
1022    /// It will work even when the stack is empty.
1023    pub fn current_user_relevant_span(&self) -> Span {
1024        self.threads.active_thread_ref().current_user_relevant_span()
1025    }
1026
1027    /// Returns the span of the *caller* of the current operation, again
1028    /// walking down the stack to find the closest frame in a local crate, if the caller of the
1029    /// current operation is not in a local crate.
1030    /// This is useful when we are processing something which occurs on function-entry and we want
1031    /// to point at the call to the function, not the function definition generally.
1032    pub fn caller_span(&self) -> Span {
1033        // We need to go down at least to the caller (len - 2), or however
1034        // far we have to go to find a frame in a local crate which is also not #[track_caller].
1035        let frame_idx = self.top_user_relevant_frame().unwrap();
1036        let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
1037        self.stack()[frame_idx].current_span()
1038    }
1039
1040    fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] {
1041        self.threads.active_thread_stack()
1042    }
1043
1044    fn top_user_relevant_frame(&self) -> Option<usize> {
1045        self.threads.active_thread_ref().top_user_relevant_frame()
1046    }
1047
1048    /// This is the source of truth for the `user_relevance` flag in our `FrameExtra`.
1049    pub fn user_relevance(&self, frame: &Frame<'tcx, Provenance>) -> u8 {
1050        if frame.instance().def.requires_caller_location(self.tcx) {
1051            return 0;
1052        }
1053        if self.is_local(frame.instance()) {
1054            u8::MAX
1055        } else {
1056            // A non-relevant frame, but at least it doesn't require a caller location, so
1057            // better than nothing.
1058            1
1059        }
1060    }
1061}
1062
1063pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
1064    throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
1065        "{name} not available when isolation is enabled",
1066    )))
1067}
1068
1069pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
1070    // SIMD uses all-1 as pattern for "true". In two's complement,
1071    // -1 has all its bits set to one and `from_int` will truncate or
1072    // sign-extend it to `size` as required.
1073    let val = if b { -1 } else { 0 };
1074    Scalar::from_int(val, size)
1075}
1076
1077/// Check whether an operation that writes to a target buffer was successful.
1078/// Accordingly select return value.
1079/// Local helper function to be used in Windows shims.
1080pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
1081    if success {
1082        // If the function succeeds, the return value is the number of characters stored in the target buffer,
1083        // not including the terminating null character.
1084        u32::try_from(len.strict_sub(1)).unwrap()
1085    } else {
1086        // If the target buffer was not large enough to hold the data, the return value is the buffer size, in characters,
1087        // required to hold the string and its terminating null character.
1088        u32::try_from(len).unwrap()
1089    }
1090}
1091
1092/// We don't support 16-bit systems, so let's have ergonomic conversion from `u32` to `usize`.
1093pub trait ToUsize {
1094    fn to_usize(self) -> usize;
1095}
1096
1097impl ToUsize for u32 {
1098    fn to_usize(self) -> usize {
1099        self.try_into().unwrap()
1100    }
1101}
1102
1103/// Similarly, a maximum address size of `u64` is assumed widely here, so let's have ergonomic
1104/// conversion from `usize` to `u64`.
1105pub trait ToU64 {
1106    fn to_u64(self) -> u64;
1107}
1108
1109impl ToU64 for usize {
1110    fn to_u64(self) -> u64 {
1111        self.try_into().unwrap()
1112    }
1113}
1114
1115/// Enters a [tracing::info_span] only if the "tracing" feature is enabled, otherwise does nothing.
1116/// This calls [rustc_const_eval::enter_trace_span] with [MiriMachine] as the first argument, which
1117/// will in turn call [MiriMachine::enter_trace_span], which takes care of determining at compile
1118/// time whether to trace or not (and supposedly the call is compiled out if tracing is disabled).
1119/// Look at [rustc_const_eval::enter_trace_span] for complete documentation, examples and tips.
1120#[macro_export]
1121macro_rules! enter_trace_span {
1122    ($($tt:tt)*) => {
1123        rustc_const_eval::enter_trace_span!($crate::MiriMachine<'static>, $($tt)*)
1124    };
1125}