1use std::num::NonZero;
2use std::time::Duration;
3use std::{cmp, iter};
4
5use rand::RngCore;
6use rustc_abi::{Align, CanonAbi, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
7use rustc_apfloat::Float;
8use rustc_apfloat::ieee::{Double, Half, Quad, Single};
9use rustc_hir::Safety;
10use rustc_hir::def::{DefKind, Namespace};
11use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE};
12use rustc_index::IndexVec;
13use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
14use rustc_middle::middle::dependency_format::Linkage;
15use rustc_middle::middle::exported_symbols::ExportedSymbol;
16use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, MaybeResult, TyAndLayout};
17use rustc_middle::ty::{self, Binder, FloatTy, FnSig, IntTy, Ty, TyCtxt, UintTy};
18use rustc_session::config::CrateType;
19use rustc_span::{Span, Symbol};
20use rustc_symbol_mangling::mangle_internal_symbol;
21use rustc_target::callconv::FnAbi;
22
23use crate::*;
24
25#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
27pub enum AccessKind {
28 Read,
29 Write,
30}
31
32fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>) -> Option<DefId> {
36 fn find_children<'tcx: 'a, 'a>(
38 tcx: TyCtxt<'tcx>,
39 item: DefId,
40 name: &'a str,
41 ) -> impl Iterator<Item = DefId> + 'a {
42 let name = Symbol::intern(name);
43 tcx.module_children(item)
44 .iter()
45 .filter(move |item| item.ident.name == name)
46 .map(move |item| item.res.def_id())
47 }
48
49 let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
51 let (modules, item) = if let Some(namespace) = namespace {
52 let (&item_name, modules) =
53 path.split_last().expect("non-module paths must have at least 2 segments");
54 (modules, Some((item_name, namespace)))
55 } else {
56 (path, None)
57 };
58
59 'crates: for krate in
64 tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
65 {
66 let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
67 for &segment in modules {
69 let Some(next_item) = find_children(tcx, cur_item, segment)
70 .find(|item| tcx.def_kind(item) == DefKind::Mod)
71 else {
72 continue 'crates;
73 };
74 cur_item = next_item;
75 }
76 match item {
78 Some((item_name, namespace)) => {
79 let Some(item) = find_children(tcx, cur_item, item_name)
80 .find(|item| tcx.def_kind(item).ns() == Some(namespace))
81 else {
82 continue 'crates;
83 };
84 return Some(item);
85 }
86 None => {
87 return Some(cur_item);
89 }
90 }
91 }
92 None
94}
95
96pub fn try_resolve_path<'tcx>(
98 tcx: TyCtxt<'tcx>,
99 path: &[&str],
100 namespace: Namespace,
101) -> Option<ty::Instance<'tcx>> {
102 let did = try_resolve_did(tcx, path, Some(namespace))?;
103 Some(ty::Instance::mono(tcx, did))
104}
105
106#[track_caller]
108pub fn resolve_path<'tcx>(
109 tcx: TyCtxt<'tcx>,
110 path: &[&str],
111 namespace: Namespace,
112) -> ty::Instance<'tcx> {
113 try_resolve_path(tcx, path, namespace)
114 .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
115}
116
117#[track_caller]
119pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
120 let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
121 cx.layout_of(ty).to_result().ok().unwrap()
122}
123
124pub fn iter_exported_symbols<'tcx>(
126 tcx: TyCtxt<'tcx>,
127 mut f: impl FnMut(CrateNum, DefId) -> InterpResult<'tcx>,
128) -> InterpResult<'tcx> {
129 let crate_items = tcx.hir_crate_items(());
133 for def_id in crate_items.definitions() {
134 let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
135 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
136 codegen_attrs.contains_extern_indicator()
137 || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
138 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
139 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
140 };
141 if exported {
142 f(LOCAL_CRATE, def_id.into())?;
143 }
144 }
145
146 let dependency_formats = tcx.dependency_formats(());
151 let dependency_format = dependency_formats
153 .get(&CrateType::Executable)
154 .expect("interpreting a non-executable crate");
155 for cnum in dependency_format
156 .iter_enumerated()
157 .filter_map(|(num, &linkage)| (linkage != Linkage::NotLinked).then_some(num))
158 {
159 if cnum == LOCAL_CRATE {
160 continue; }
162
163 for &(symbol, _export_info) in tcx.exported_symbols(cnum) {
166 if let ExportedSymbol::NonGeneric(def_id) = symbol {
167 f(cnum, def_id)?;
168 }
169 }
170 }
171 interp_ok(())
172}
173
174pub trait ToHost {
176 type HostFloat;
177 fn to_host(self) -> Self::HostFloat;
178}
179
180pub trait ToSoft {
182 type SoftFloat;
183 fn to_soft(self) -> Self::SoftFloat;
184}
185
186impl ToHost for rustc_apfloat::ieee::Double {
187 type HostFloat = f64;
188
189 fn to_host(self) -> Self::HostFloat {
190 f64::from_bits(self.to_bits().try_into().unwrap())
191 }
192}
193
194impl ToSoft for f64 {
195 type SoftFloat = rustc_apfloat::ieee::Double;
196
197 fn to_soft(self) -> Self::SoftFloat {
198 Float::from_bits(self.to_bits().into())
199 }
200}
201
202impl ToHost for rustc_apfloat::ieee::Single {
203 type HostFloat = f32;
204
205 fn to_host(self) -> Self::HostFloat {
206 f32::from_bits(self.to_bits().try_into().unwrap())
207 }
208}
209
210impl ToSoft for f32 {
211 type SoftFloat = rustc_apfloat::ieee::Single;
212
213 fn to_soft(self) -> Self::SoftFloat {
214 Float::from_bits(self.to_bits().into())
215 }
216}
217
218impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
219pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
220 fn have_module(&self, path: &[&str]) -> bool {
222 try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
223 }
224
225 fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> {
227 let this = self.eval_context_ref();
228 let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
229 this.eval_global(instance).unwrap_or_else(|err| {
231 panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
232 })
233 }
234 fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
235 let this = self.eval_context_ref();
236 let val = this.eval_path(path);
237 this.read_scalar(&val)
238 .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
239 }
240
241 fn eval_libc(&self, name: &str) -> Scalar {
243 if self.eval_context_ref().tcx.sess.target.os == "windows" {
244 panic!(
245 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
246 );
247 }
248 self.eval_path_scalar(&["libc", name])
249 }
250
251 fn eval_libc_i32(&self, name: &str) -> i32 {
253 self.eval_libc(name).to_i32().unwrap_or_else(|_err| {
255 panic!("required libc item has unexpected type (not `i32`): {name}")
256 })
257 }
258
259 fn eval_libc_u32(&self, name: &str) -> u32 {
261 self.eval_libc(name).to_u32().unwrap_or_else(|_err| {
263 panic!("required libc item has unexpected type (not `u32`): {name}")
264 })
265 }
266
267 fn eval_libc_u64(&self, name: &str) -> u64 {
269 self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
271 panic!("required libc item has unexpected type (not `u64`): {name}")
272 })
273 }
274
275 fn eval_windows(&self, module: &str, name: &str) -> Scalar {
277 self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
278 }
279
280 fn eval_windows_u32(&self, module: &str, name: &str) -> u32 {
282 self.eval_windows(module, name).to_u32().unwrap_or_else(|_err| {
284 panic!("required Windows item has unexpected type (not `u32`): {module}::{name}")
285 })
286 }
287
288 fn eval_windows_u64(&self, module: &str, name: &str) -> u64 {
290 self.eval_windows(module, name).to_u64().unwrap_or_else(|_err| {
292 panic!("required Windows item has unexpected type (not `u64`): {module}::{name}")
293 })
294 }
295
296 fn libc_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
298 let this = self.eval_context_ref();
299 if this.tcx.sess.target.os == "windows" {
300 panic!(
301 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
302 );
303 }
304 path_ty_layout(this, &["libc", name])
305 }
306
307 fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
309 let this = self.eval_context_ref();
310 path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
311 }
312
313 fn libc_array_ty_layout(&self, name: &str, size: u64) -> TyAndLayout<'tcx> {
315 let this = self.eval_context_ref();
316 let elem_ty_layout = this.libc_ty_layout(name);
317 let array_ty = Ty::new_array(*this.tcx, elem_ty_layout.ty, size);
318 this.layout_of(array_ty).unwrap()
319 }
320
321 fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
323 &self,
324 base: &P,
325 name: &str,
326 ) -> InterpResult<'tcx, Option<P>> {
327 let this = self.eval_context_ref();
328 let adt = base.layout().ty.ty_adt_def().unwrap();
329 for (idx, field) in adt.non_enum_variant().fields.iter_enumerated() {
330 if field.name.as_str() == name {
331 return interp_ok(Some(this.project_field(base, idx)?));
332 }
333 }
334 interp_ok(None)
335 }
336
337 fn project_field_named<P: Projectable<'tcx, Provenance>>(
339 &self,
340 base: &P,
341 name: &str,
342 ) -> InterpResult<'tcx, P> {
343 interp_ok(
344 self.try_project_field_named(base, name)?
345 .unwrap_or_else(|| bug!("no field named {} in type {}", name, base.layout().ty)),
346 )
347 }
348
349 fn write_int(
353 &mut self,
354 i: impl Into<i128>,
355 dest: &impl Writeable<'tcx, Provenance>,
356 ) -> InterpResult<'tcx> {
357 assert!(
358 dest.layout().backend_repr.is_scalar(),
359 "write_int on non-scalar type {}",
360 dest.layout().ty
361 );
362 let val = if dest.layout().backend_repr.is_signed() {
363 Scalar::from_int(i, dest.layout().size)
364 } else {
365 Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
367 };
368 self.eval_context_mut().write_scalar(val, dest)
369 }
370
371 fn write_int_fields(
373 &mut self,
374 values: &[i128],
375 dest: &impl Writeable<'tcx, Provenance>,
376 ) -> InterpResult<'tcx> {
377 let this = self.eval_context_mut();
378 for (idx, &val) in values.iter().enumerate() {
379 let idx = FieldIdx::from_usize(idx);
380 let field = this.project_field(dest, idx)?;
381 this.write_int(val, &field)?;
382 }
383 interp_ok(())
384 }
385
386 fn write_int_fields_named(
388 &mut self,
389 values: &[(&str, i128)],
390 dest: &impl Writeable<'tcx, Provenance>,
391 ) -> InterpResult<'tcx> {
392 let this = self.eval_context_mut();
393 for &(name, val) in values.iter() {
394 let field = this.project_field_named(dest, name)?;
395 this.write_int(val, &field)?;
396 }
397 interp_ok(())
398 }
399
400 fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> {
402 self.write_int(0, dest)
403 }
404
405 fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
407 interp_ok(ptr.addr().bytes() == 0)
408 }
409
410 fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
412 if len == 0 {
418 return interp_ok(());
419 }
420 let this = self.eval_context_mut();
421
422 let mut data = vec![0; usize::try_from(len).unwrap()];
423
424 if this.machine.communicate() {
425 getrandom::fill(&mut data)
427 .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
428 } else {
429 let rng = this.machine.rng.get_mut();
430 rng.fill_bytes(&mut data);
431 }
432
433 this.write_bytes_ptr(ptr, data.iter().copied())
434 }
435
436 fn call_function(
442 &mut self,
443 f: ty::Instance<'tcx>,
444 caller_abi: ExternAbi,
445 args: &[ImmTy<'tcx>],
446 dest: Option<&MPlaceTy<'tcx>>,
447 stack_pop: StackPopCleanup,
448 ) -> InterpResult<'tcx> {
449 let this = self.eval_context_mut();
450
451 let mir = this.load_mir(f.def, None)?;
453 let dest = match dest {
454 Some(dest) => dest.clone(),
455 None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?),
456 };
457
458 let sig = this.tcx.mk_fn_sig(
460 args.iter().map(|a| a.layout.ty),
461 dest.layout.ty,
462 false,
463 Safety::Safe,
464 caller_abi,
465 );
466 let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
467
468 this.init_stack_frame(
469 f,
470 mir,
471 caller_fn_abi,
472 &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
473 false,
474 &dest.into(),
475 stack_pop,
476 )
477 }
478
479 fn visit_freeze_sensitive(
483 &self,
484 place: &MPlaceTy<'tcx>,
485 size: Size,
486 mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
487 ) -> InterpResult<'tcx> {
488 let this = self.eval_context_ref();
489 trace!("visit_frozen(place={:?}, size={:?})", *place, size);
490 debug_assert_eq!(
491 size,
492 this.size_and_align_of_mplace(place)?
493 .map(|(size, _)| size)
494 .unwrap_or_else(|| place.layout.size)
495 );
496 let start_addr = place.ptr().addr();
500 let mut cur_addr = start_addr;
501 let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
504 let unsafe_cell_addr = unsafe_cell_ptr.addr();
507 assert!(unsafe_cell_addr >= cur_addr);
508 let frozen_size = unsafe_cell_addr - cur_addr;
509 if frozen_size != Size::ZERO {
511 action(alloc_range(cur_addr - start_addr, frozen_size), true)?;
512 }
513 cur_addr += frozen_size;
514 if unsafe_cell_size != Size::ZERO {
516 action(
517 alloc_range(cur_addr - start_addr, unsafe_cell_size),
518 false,
519 )?;
520 }
521 cur_addr += unsafe_cell_size;
522 interp_ok(())
524 };
525 {
527 let mut visitor = UnsafeCellVisitor {
528 ecx: this,
529 unsafe_cell_action: |place| {
530 trace!("unsafe_cell_action on {:?}", place.ptr());
531 let unsafe_cell_size = this
533 .size_and_align_of_mplace(place)?
534 .map(|(size, _)| size)
535 .unwrap_or_else(|| place.layout.size);
537 if unsafe_cell_size != Size::ZERO {
539 unsafe_cell_action(&place.ptr(), unsafe_cell_size)
540 } else {
541 interp_ok(())
542 }
543 },
544 };
545 visitor.visit_value(place)?;
546 }
547 unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?;
550 return interp_ok(());
552
553 struct UnsafeCellVisitor<'ecx, 'tcx, F>
556 where
557 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
558 {
559 ecx: &'ecx MiriInterpCx<'tcx>,
560 unsafe_cell_action: F,
561 }
562
563 impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
564 where
565 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
566 {
567 type V = MPlaceTy<'tcx>;
568
569 #[inline(always)]
570 fn ecx(&self) -> &MiriInterpCx<'tcx> {
571 self.ecx
572 }
573
574 fn aggregate_field_iter(
575 memory_index: &IndexVec<FieldIdx, u32>,
576 ) -> impl Iterator<Item = FieldIdx> + 'static {
577 let inverse_memory_index = memory_index.invert_bijective_mapping();
578 inverse_memory_index.into_iter()
579 }
580
581 fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
583 trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
584 let is_unsafe_cell = match v.layout.ty.kind() {
585 ty::Adt(adt, _) =>
586 Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
587 _ => false,
588 };
589 if is_unsafe_cell {
590 (self.unsafe_cell_action)(v)
592 } else if self.ecx.type_is_freeze(v.layout.ty) {
593 interp_ok(())
595 } else if matches!(v.layout.fields, FieldsShape::Union(..)) {
596 (self.unsafe_cell_action)(v)
598 } else if matches!(v.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)) {
599 (self.unsafe_cell_action)(v)
602 } else {
603 match v.layout.variants {
610 Variants::Multiple { .. } => {
611 (self.unsafe_cell_action)(v)
619 }
620 Variants::Single { .. } | Variants::Empty => {
621 self.walk_value(v)
624 }
625 }
626 }
627 }
628
629 fn visit_union(
630 &mut self,
631 _v: &MPlaceTy<'tcx>,
632 _fields: NonZero<usize>,
633 ) -> InterpResult<'tcx> {
634 bug!("we should have already handled unions in `visit_value`")
635 }
636 }
637 }
638
639 fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
643 if !self.eval_context_ref().machine.communicate() {
644 self.reject_in_isolation(name, RejectOpWith::Abort)?;
645 }
646 interp_ok(())
647 }
648
649 fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> InterpResult<'tcx> {
652 let this = self.eval_context_ref();
653 match reject_with {
654 RejectOpWith::Abort => isolation_abort_error(op_name),
655 RejectOpWith::WarningWithoutBacktrace => {
656 let mut emitted_warnings = this.machine.reject_in_isolation_warned.borrow_mut();
657 if !emitted_warnings.contains(op_name) {
658 emitted_warnings.insert(op_name.to_owned());
660 this.tcx
661 .dcx()
662 .warn(format!("{op_name} was made to return an error due to isolation"));
663 }
664
665 interp_ok(())
666 }
667 RejectOpWith::Warning => {
668 this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
669 interp_ok(())
670 }
671 RejectOpWith::NoWarning => interp_ok(()), }
673 }
674
675 fn assert_target_os(&self, target_os: &str, name: &str) {
679 assert_eq!(
680 self.eval_context_ref().tcx.sess.target.os,
681 target_os,
682 "`{name}` is only available on the `{target_os}` target OS",
683 )
684 }
685
686 fn check_target_os(&self, target_oses: &[&str], name: Symbol) -> InterpResult<'tcx> {
690 let target_os = self.eval_context_ref().tcx.sess.target.os.as_ref();
691 if !target_oses.contains(&target_os) {
692 throw_unsup_format!("`{name}` is not supported on {target_os}");
693 }
694 interp_ok(())
695 }
696
697 fn assert_target_os_is_unix(&self, name: &str) {
701 assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",);
702 }
703
704 fn target_os_is_unix(&self) -> bool {
705 self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
706 }
707
708 fn deref_pointer_as(
710 &self,
711 op: &impl Projectable<'tcx, Provenance>,
712 layout: TyAndLayout<'tcx>,
713 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
714 let this = self.eval_context_ref();
715 let ptr = this.read_pointer(op)?;
716 interp_ok(this.ptr_to_mplace(ptr, layout))
717 }
718
719 fn deref_pointer_and_offset(
721 &self,
722 op: &impl Projectable<'tcx, Provenance>,
723 offset: u64,
724 base_layout: TyAndLayout<'tcx>,
725 value_layout: TyAndLayout<'tcx>,
726 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
727 let this = self.eval_context_ref();
728 let op_place = this.deref_pointer_as(op, base_layout)?;
729 let offset = Size::from_bytes(offset);
730
731 assert!(base_layout.size >= offset + value_layout.size);
733 let value_place = op_place.offset(offset, value_layout, this)?;
734 interp_ok(value_place)
735 }
736
737 fn deref_pointer_and_read(
738 &self,
739 op: &impl Projectable<'tcx, Provenance>,
740 offset: u64,
741 base_layout: TyAndLayout<'tcx>,
742 value_layout: TyAndLayout<'tcx>,
743 ) -> InterpResult<'tcx, Scalar> {
744 let this = self.eval_context_ref();
745 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
746 this.read_scalar(&value_place)
747 }
748
749 fn deref_pointer_and_write(
750 &mut self,
751 op: &impl Projectable<'tcx, Provenance>,
752 offset: u64,
753 value: impl Into<Scalar>,
754 base_layout: TyAndLayout<'tcx>,
755 value_layout: TyAndLayout<'tcx>,
756 ) -> InterpResult<'tcx, ()> {
757 let this = self.eval_context_mut();
758 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
759 this.write_scalar(value, &value_place)
760 }
761
762 fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> {
766 let this = self.eval_context_mut();
767 let seconds_place = this.project_field(tp, FieldIdx::ZERO)?;
768 let seconds_scalar = this.read_scalar(&seconds_place)?;
769 let seconds = seconds_scalar.to_target_isize(this)?;
770 let nanoseconds_place = this.project_field(tp, FieldIdx::ONE)?;
771 let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?;
772 let nanoseconds = nanoseconds_scalar.to_target_isize(this)?;
773
774 interp_ok(
775 try {
776 let seconds: u64 = seconds.try_into().ok()?;
778 let nanoseconds: u32 = nanoseconds.try_into().ok()?;
780 if nanoseconds >= 1_000_000_000 {
781 None?
783 }
784 Duration::new(seconds, nanoseconds)
785 },
786 )
787 }
788
789 fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
791 where
792 'tcx: 'a,
793 {
794 let this = self.eval_context_ref();
795 let (ptr, len) = slice.to_scalar_pair();
796 let ptr = ptr.to_pointer(this)?;
797 let len = len.to_target_usize(this)?;
798 let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
799 interp_ok(bytes)
800 }
801
802 fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
804 where
805 'tcx: 'a,
806 {
807 let this = self.eval_context_ref();
808 let size1 = Size::from_bytes(1);
809
810 let mut len = Size::ZERO;
812 loop {
813 let alloc = this.get_ptr_alloc(ptr.wrapping_offset(len, this), size1)?.unwrap(); let byte = alloc.read_integer(alloc_range(Size::ZERO, size1))?.to_u8()?;
817 if byte == 0 {
818 break;
819 } else {
820 len += size1;
821 }
822 }
823
824 this.read_bytes_ptr_strip_provenance(ptr, len)
826 }
827
828 fn write_c_str(
834 &mut self,
835 c_str: &[u8],
836 ptr: Pointer,
837 size: u64,
838 ) -> InterpResult<'tcx, (bool, u64)> {
839 let string_length = u64::try_from(c_str.len()).unwrap();
842 let string_length = string_length.strict_add(1);
843 if size < string_length {
844 return interp_ok((false, string_length));
845 }
846 self.eval_context_mut()
847 .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
848 interp_ok((true, string_length))
849 }
850
851 fn read_c_str_with_char_size<T>(
854 &self,
855 mut ptr: Pointer,
856 size: Size,
857 align: Align,
858 ) -> InterpResult<'tcx, Vec<T>>
859 where
860 T: TryFrom<u128>,
861 <T as TryFrom<u128>>::Error: std::fmt::Debug,
862 {
863 assert_ne!(size, Size::ZERO);
864
865 let this = self.eval_context_ref();
866
867 this.check_ptr_align(ptr, align)?;
868
869 let mut wchars = Vec::new();
870 loop {
871 let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
875 if wchar_int == 0 {
876 break;
877 } else {
878 wchars.push(wchar_int.try_into().unwrap());
879 ptr = ptr.wrapping_offset(size, this);
880 }
881 }
882
883 interp_ok(wchars)
884 }
885
886 fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
888 self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
889 }
890
891 fn write_wide_str(
898 &mut self,
899 wide_str: &[u16],
900 ptr: Pointer,
901 size: u64,
902 ) -> InterpResult<'tcx, (bool, u64)> {
903 let string_length = u64::try_from(wide_str.len()).unwrap();
906 let string_length = string_length.strict_add(1);
907 if size < string_length {
908 return interp_ok((false, string_length));
909 }
910
911 let size2 = Size::from_bytes(2);
913 let this = self.eval_context_mut();
914 this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
915 let mut alloc = this.get_ptr_alloc_mut(ptr, size2 * string_length)?.unwrap(); for (offset, wchar) in wide_str.iter().copied().chain(iter::once(0x0000)).enumerate() {
917 let offset = u64::try_from(offset).unwrap();
918 alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
919 }
920 interp_ok((true, string_length))
921 }
922
923 fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
926 let this = self.eval_context_ref();
927 let wchar_t = if this.tcx.sess.target.os == "windows" {
928 this.machine.layouts.u16
930 } else {
931 this.libc_ty_layout("wchar_t")
932 };
933 self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
934 }
935
936 fn check_callconv<'a>(
938 &self,
939 fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
940 exp_abi: CanonAbi,
941 ) -> InterpResult<'a, ()> {
942 if fn_abi.conv != exp_abi {
943 throw_ub_format!(
944 r#"calling a function with calling convention "{exp_abi}" using caller calling convention "{}""#,
945 fn_abi.conv
946 );
947 }
948 interp_ok(())
949 }
950
951 fn frame_in_std(&self) -> bool {
952 let this = self.eval_context_ref();
953 let frame = this.frame();
954 let instance: Option<_> = try {
956 let scope = frame.current_source_info()?.scope;
957 let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
958 let source = &frame.body().source_scopes[inlined_parent];
959 source.inlined.expect("inlined_parent_scope points to scope without inline info").0
960 };
961 let instance = instance.unwrap_or(frame.instance());
963 let frame_crate = this.tcx.def_path(instance.def_id()).krate;
968 let crate_name = this.tcx.crate_name(frame_crate);
969 let crate_name = crate_name.as_str();
970 crate_name == "std" || crate_name == "std_miri_test"
972 }
973
974 fn check_abi_and_shim_symbol_clash(
975 &mut self,
976 abi: &FnAbi<'tcx, Ty<'tcx>>,
977 exp_abi: CanonAbi,
978 link_name: Symbol,
979 ) -> InterpResult<'tcx, ()> {
980 self.check_callconv(abi, exp_abi)?;
981 if let Some((body, instance)) = self.eval_context_mut().lookup_exported_symbol(link_name)? {
982 if self.eval_context_ref().tcx.is_compiler_builtins(instance.def_id().krate) {
988 return interp_ok(());
989 }
990
991 throw_machine_stop!(TerminationInfo::SymbolShimClashing {
992 link_name,
993 span: body.span.data(),
994 })
995 }
996 interp_ok(())
997 }
998
999 fn check_shim<'a, const N: usize>(
1000 &mut self,
1001 abi: &FnAbi<'tcx, Ty<'tcx>>,
1002 exp_abi: CanonAbi,
1003 link_name: Symbol,
1004 args: &'a [OpTy<'tcx>],
1005 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
1006 self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
1007
1008 if abi.c_variadic {
1009 throw_ub_format!(
1010 "calling a non-variadic function with a variadic caller-side signature"
1011 );
1012 }
1013 if let Ok(ops) = args.try_into() {
1014 return interp_ok(ops);
1015 }
1016 throw_ub_format!(
1017 "incorrect number of arguments for `{link_name}`: got {}, expected {}",
1018 args.len(),
1019 N
1020 )
1021 }
1022
1023 fn check_shim_abi<'a, const N: usize>(
1027 &mut self,
1028 link_name: Symbol,
1029 caller_fn_abi: &FnAbi<'tcx, Ty<'tcx>>,
1030 callee_abi: ExternAbi,
1031 callee_input_tys: [Ty<'tcx>; N],
1032 callee_output_ty: Ty<'tcx>,
1033 caller_args: &'a [OpTy<'tcx>],
1034 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
1035 let this = self.eval_context_mut();
1036 let mut inputs_and_output = callee_input_tys.to_vec();
1037 inputs_and_output.push(callee_output_ty);
1038 let fn_sig_binder = Binder::dummy(FnSig {
1039 inputs_and_output: this.machine.tcx.mk_type_list(&inputs_and_output),
1040 c_variadic: false,
1041 safety: Safety::Safe,
1043 abi: callee_abi,
1044 });
1045 let callee_fn_abi = this.fn_abi_of_fn_ptr(fn_sig_binder, Default::default())?;
1046
1047 this.check_abi_and_shim_symbol_clash(caller_fn_abi, callee_fn_abi.conv, link_name)?;
1048
1049 if caller_fn_abi.c_variadic {
1050 throw_ub_format!(
1051 "ABI mismatch: calling a non-variadic function with a variadic caller-side signature"
1052 );
1053 }
1054
1055 if callee_fn_abi.fixed_count != caller_fn_abi.fixed_count {
1056 throw_ub_format!(
1057 "ABI mismatch: expected {} arguments, found {} arguments ",
1058 callee_fn_abi.fixed_count,
1059 caller_fn_abi.fixed_count
1060 );
1061 }
1062
1063 if callee_fn_abi.can_unwind && !caller_fn_abi.can_unwind {
1064 throw_ub_format!(
1065 "ABI mismatch: callee may unwind, but caller-side signature prohibits unwinding",
1066 );
1067 }
1068
1069 if !this.check_argument_compat(&caller_fn_abi.ret, &callee_fn_abi.ret)? {
1070 throw_ub!(AbiMismatchReturn {
1071 caller_ty: caller_fn_abi.ret.layout.ty,
1072 callee_ty: callee_fn_abi.ret.layout.ty
1073 });
1074 }
1075
1076 if let Some(index) = caller_fn_abi
1077 .args
1078 .iter()
1079 .zip(callee_fn_abi.args.iter())
1080 .map(|(caller_arg, callee_arg)| this.check_argument_compat(caller_arg, callee_arg))
1081 .collect::<InterpResult<'tcx, Vec<bool>>>()?
1082 .into_iter()
1083 .position(|b| !b)
1084 {
1085 throw_ub!(AbiMismatchArgument {
1086 caller_ty: caller_fn_abi.args[index].layout.ty,
1087 callee_ty: callee_fn_abi.args[index].layout.ty
1088 });
1089 }
1090
1091 if let Ok(ops) = caller_args.try_into() {
1092 return interp_ok(ops);
1093 }
1094 unreachable!()
1095 }
1096
1097 fn check_shim_variadic<'a, const N: usize>(
1100 &mut self,
1101 abi: &FnAbi<'tcx, Ty<'tcx>>,
1102 exp_abi: CanonAbi,
1103 link_name: Symbol,
1104 args: &'a [OpTy<'tcx>],
1105 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])>
1106 where
1107 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
1108 {
1109 self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
1110
1111 if !abi.c_variadic {
1112 throw_ub_format!(
1113 "calling a variadic function with a non-variadic caller-side signature"
1114 );
1115 }
1116 if abi.fixed_count != u32::try_from(N).unwrap() {
1117 throw_ub_format!(
1118 "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
1119 link_name.as_str(),
1120 abi.fixed_count
1121 )
1122 }
1123 if let Some(args) = args.split_first_chunk() {
1124 return interp_ok(args);
1125 }
1126 panic!("mismatch between signature and `args` slice");
1127 }
1128
1129 fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
1131 let this = self.eval_context_mut();
1132 let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
1134 this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap();
1135 }
1136
1137 fn float_to_int_checked(
1141 &self,
1142 src: &ImmTy<'tcx>,
1143 cast_to: TyAndLayout<'tcx>,
1144 round: rustc_apfloat::Round,
1145 ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
1146 let this = self.eval_context_ref();
1147
1148 fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>(
1149 ecx: &MiriInterpCx<'tcx>,
1150 src: F,
1151 cast_to: TyAndLayout<'tcx>,
1152 round: rustc_apfloat::Round,
1153 ) -> (Scalar, rustc_apfloat::Status) {
1154 let int_size = cast_to.layout.size;
1155 match cast_to.ty.kind() {
1156 ty::Uint(_) => {
1158 let res = src.to_u128_r(int_size.bits_usize(), round, &mut false);
1159 (Scalar::from_uint(res.value, int_size), res.status)
1160 }
1161 ty::Int(_) => {
1163 let res = src.to_i128_r(int_size.bits_usize(), round, &mut false);
1164 (Scalar::from_int(res.value, int_size), res.status)
1165 }
1166 _ =>
1168 span_bug!(
1169 ecx.cur_span(),
1170 "attempted float-to-int conversion with non-int output type {}",
1171 cast_to.ty,
1172 ),
1173 }
1174 }
1175
1176 let ty::Float(fty) = src.layout.ty.kind() else {
1177 bug!("float_to_int_checked: non-float input type {}", src.layout.ty)
1178 };
1179
1180 let (val, status) = match fty {
1181 FloatTy::F16 =>
1182 float_to_int_inner::<Half>(this, src.to_scalar().to_f16()?, cast_to, round),
1183 FloatTy::F32 =>
1184 float_to_int_inner::<Single>(this, src.to_scalar().to_f32()?, cast_to, round),
1185 FloatTy::F64 =>
1186 float_to_int_inner::<Double>(this, src.to_scalar().to_f64()?, cast_to, round),
1187 FloatTy::F128 =>
1188 float_to_int_inner::<Quad>(this, src.to_scalar().to_f128()?, cast_to, round),
1189 };
1190
1191 if status.intersects(
1192 rustc_apfloat::Status::INVALID_OP
1193 | rustc_apfloat::Status::OVERFLOW
1194 | rustc_apfloat::Status::UNDERFLOW,
1195 ) {
1196 interp_ok(None)
1199 } else {
1200 interp_ok(Some(ImmTy::from_scalar(val, cast_to)))
1203 }
1204 }
1205
1206 fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
1208 let this = self.eval_context_ref();
1209 match ty.kind() {
1210 ty::Uint(UintTy::U8) => this.tcx.types.u16,
1212 ty::Uint(UintTy::U16) => this.tcx.types.u32,
1213 ty::Uint(UintTy::U32) => this.tcx.types.u64,
1214 ty::Uint(UintTy::U64) => this.tcx.types.u128,
1215 ty::Int(IntTy::I8) => this.tcx.types.i16,
1217 ty::Int(IntTy::I16) => this.tcx.types.i32,
1218 ty::Int(IntTy::I32) => this.tcx.types.i64,
1219 ty::Int(IntTy::I64) => this.tcx.types.i128,
1220 _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
1221 }
1222 }
1223
1224 fn expect_target_feature_for_intrinsic(
1229 &self,
1230 intrinsic: Symbol,
1231 target_feature: &str,
1232 ) -> InterpResult<'tcx, ()> {
1233 let this = self.eval_context_ref();
1234 if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) {
1235 throw_ub_format!(
1236 "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
1237 );
1238 }
1239 interp_ok(())
1240 }
1241
1242 fn lookup_link_section(&mut self, name: &str) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
1244 let this = self.eval_context_mut();
1245 let tcx = this.tcx.tcx;
1246
1247 let mut array = vec![];
1248
1249 iter_exported_symbols(tcx, |_cnum, def_id| {
1250 let attrs = tcx.codegen_fn_attrs(def_id);
1251 let Some(link_section) = attrs.link_section else {
1252 return interp_ok(());
1253 };
1254 if link_section.as_str() == name {
1255 let instance = ty::Instance::mono(tcx, def_id);
1256 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
1257 panic!(
1258 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
1259 )
1260 });
1261 let val = this.read_immediate(&const_val)?;
1262 array.push(val);
1263 }
1264 interp_ok(())
1265 })?;
1266
1267 interp_ok(array)
1268 }
1269
1270 fn mangle_internal_symbol<'a>(&'a mut self, name: &'static str) -> &'a str
1271 where
1272 'tcx: 'a,
1273 {
1274 let this = self.eval_context_mut();
1275 let tcx = *this.tcx;
1276 this.machine
1277 .mangle_internal_symbol_cache
1278 .entry(name)
1279 .or_insert_with(|| mangle_internal_symbol(tcx, name))
1280 }
1281}
1282
1283impl<'tcx> MiriMachine<'tcx> {
1284 pub fn current_span(&self) -> Span {
1289 self.threads.active_thread_ref().current_span()
1290 }
1291
1292 pub fn caller_span(&self) -> Span {
1298 let frame_idx = self.top_user_relevant_frame().unwrap();
1301 let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
1302 self.stack()[frame_idx].current_span()
1303 }
1304
1305 fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] {
1306 self.threads.active_thread_stack()
1307 }
1308
1309 fn top_user_relevant_frame(&self) -> Option<usize> {
1310 self.threads.active_thread_ref().top_user_relevant_frame()
1311 }
1312
1313 pub fn is_user_relevant(&self, frame: &Frame<'tcx, Provenance>) -> bool {
1315 let def_id = frame.instance().def_id();
1316 (def_id.is_local() || self.local_crates.contains(&def_id.krate))
1317 && !frame.instance().def.requires_caller_location(self.tcx)
1318 }
1319}
1320
1321pub fn check_intrinsic_arg_count<'a, 'tcx, const N: usize>(
1323 args: &'a [OpTy<'tcx>],
1324) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
1325where
1326 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
1327{
1328 if let Ok(ops) = args.try_into() {
1329 return interp_ok(ops);
1330 }
1331 throw_ub_format!(
1332 "incorrect number of arguments for intrinsic: got {}, expected {}",
1333 args.len(),
1334 N
1335 )
1336}
1337
1338pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
1342 name: &'a str,
1343 args: &'a [OpTy<'tcx>],
1344) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
1345 if let Some((ops, _)) = args.split_first_chunk() {
1346 return interp_ok(ops);
1347 }
1348 throw_ub_format!(
1349 "not enough variadic arguments for `{name}`: got {}, expected at least {}",
1350 args.len(),
1351 N
1352 )
1353}
1354
1355pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
1356 throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
1357 "{name} not available when isolation is enabled",
1358 )))
1359}
1360
1361pub fn get_local_crates(tcx: TyCtxt<'_>) -> Vec<CrateNum> {
1364 let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
1367 .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
1368 .unwrap_or_default();
1369 let mut local_crates = Vec::new();
1370 for &crate_num in tcx.crates(()) {
1371 let name = tcx.crate_name(crate_num);
1372 let name = name.as_str();
1373 if local_crate_names.iter().any(|local_name| local_name == name) {
1374 local_crates.push(crate_num);
1375 }
1376 }
1377 local_crates
1378}
1379
1380pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
1381 let val = if b { -1 } else { 0 };
1385 Scalar::from_int(val, size)
1386}
1387
1388pub(crate) fn simd_element_to_bool(elem: ImmTy<'_>) -> InterpResult<'_, bool> {
1389 assert!(
1390 matches!(elem.layout.ty.kind(), ty::Int(_) | ty::Uint(_)),
1391 "SIMD mask element type must be an integer, but this is `{}`",
1392 elem.layout.ty
1393 );
1394 let val = elem.to_scalar().to_int(elem.layout.size)?;
1395 interp_ok(match val {
1396 0 => false,
1397 -1 => true,
1398 _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
1399 })
1400}
1401
1402pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
1406 if success {
1407 u32::try_from(len.strict_sub(1)).unwrap()
1410 } else {
1411 u32::try_from(len).unwrap()
1414 }
1415}
1416
1417pub trait ToUsize {
1419 fn to_usize(self) -> usize;
1420}
1421
1422impl ToUsize for u32 {
1423 fn to_usize(self) -> usize {
1424 self.try_into().unwrap()
1425 }
1426}
1427
1428pub trait ToU64 {
1431 fn to_u64(self) -> u64;
1432}
1433
1434impl ToU64 for usize {
1435 fn to_u64(self) -> u64 {
1436 self.try_into().unwrap()
1437 }
1438}