1use std::num::NonZero;
2use std::time::Duration;
3use std::{cmp, iter};
4
5use rand::RngCore;
6use rustc_abi::{Align, 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, FloatTy, IntTy, Ty, TyCtxt, UintTy};
18use rustc_session::config::CrateType;
19use rustc_span::{Span, Symbol};
20use rustc_target::callconv::{Conv, FnAbi};
21
22use crate::*;
23
24#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
26pub enum AccessKind {
27 Read,
28 Write,
29}
30
31fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>) -> Option<DefId> {
35 fn find_children<'tcx: 'a, 'a>(
37 tcx: TyCtxt<'tcx>,
38 item: DefId,
39 name: &'a str,
40 ) -> impl Iterator<Item = DefId> + 'a {
41 let name = Symbol::intern(name);
42 tcx.module_children(item)
43 .iter()
44 .filter(move |item| item.ident.name == name)
45 .map(move |item| item.res.def_id())
46 }
47
48 let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
50 let (modules, item) = if let Some(namespace) = namespace {
51 let (&item_name, modules) =
52 path.split_last().expect("non-module paths must have at least 2 segments");
53 (modules, Some((item_name, namespace)))
54 } else {
55 (path, None)
56 };
57
58 'crates: for krate in
63 tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
64 {
65 let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
66 for &segment in modules {
68 let Some(next_item) = find_children(tcx, cur_item, segment)
69 .find(|item| tcx.def_kind(item) == DefKind::Mod)
70 else {
71 continue 'crates;
72 };
73 cur_item = next_item;
74 }
75 match item {
77 Some((item_name, namespace)) => {
78 let Some(item) = find_children(tcx, cur_item, item_name)
79 .find(|item| tcx.def_kind(item).ns() == Some(namespace))
80 else {
81 continue 'crates;
82 };
83 return Some(item);
84 }
85 None => {
86 return Some(cur_item);
88 }
89 }
90 }
91 None
93}
94
95pub fn try_resolve_path<'tcx>(
97 tcx: TyCtxt<'tcx>,
98 path: &[&str],
99 namespace: Namespace,
100) -> Option<ty::Instance<'tcx>> {
101 let did = try_resolve_did(tcx, path, Some(namespace))?;
102 Some(ty::Instance::mono(tcx, did))
103}
104
105#[track_caller]
107pub fn resolve_path<'tcx>(
108 tcx: TyCtxt<'tcx>,
109 path: &[&str],
110 namespace: Namespace,
111) -> ty::Instance<'tcx> {
112 try_resolve_path(tcx, path, namespace)
113 .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
114}
115
116#[track_caller]
118pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
119 let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
120 cx.layout_of(ty).to_result().ok().unwrap()
121}
122
123pub fn iter_exported_symbols<'tcx>(
125 tcx: TyCtxt<'tcx>,
126 mut f: impl FnMut(CrateNum, DefId) -> InterpResult<'tcx>,
127) -> InterpResult<'tcx> {
128 let crate_items = tcx.hir_crate_items(());
132 for def_id in crate_items.definitions() {
133 let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
134 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
135 codegen_attrs.contains_extern_indicator()
136 || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
137 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
138 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
139 };
140 if exported {
141 f(LOCAL_CRATE, def_id.into())?;
142 }
143 }
144
145 let dependency_formats = tcx.dependency_formats(());
150 let dependency_format = dependency_formats
152 .get(&CrateType::Executable)
153 .expect("interpreting a non-executable crate");
154 for cnum in dependency_format
155 .iter_enumerated()
156 .filter_map(|(num, &linkage)| (linkage != Linkage::NotLinked).then_some(num))
157 {
158 if cnum == LOCAL_CRATE {
159 continue; }
161
162 for &(symbol, _export_info) in tcx.exported_symbols(cnum) {
165 if let ExportedSymbol::NonGeneric(def_id) = symbol {
166 f(cnum, def_id)?;
167 }
168 }
169 }
170 interp_ok(())
171}
172
173pub trait ToHost {
175 type HostFloat;
176 fn to_host(self) -> Self::HostFloat;
177}
178
179pub trait ToSoft {
181 type SoftFloat;
182 fn to_soft(self) -> Self::SoftFloat;
183}
184
185impl ToHost for rustc_apfloat::ieee::Double {
186 type HostFloat = f64;
187
188 fn to_host(self) -> Self::HostFloat {
189 f64::from_bits(self.to_bits().try_into().unwrap())
190 }
191}
192
193impl ToSoft for f64 {
194 type SoftFloat = rustc_apfloat::ieee::Double;
195
196 fn to_soft(self) -> Self::SoftFloat {
197 Float::from_bits(self.to_bits().into())
198 }
199}
200
201impl ToHost for rustc_apfloat::ieee::Single {
202 type HostFloat = f32;
203
204 fn to_host(self) -> Self::HostFloat {
205 f32::from_bits(self.to_bits().try_into().unwrap())
206 }
207}
208
209impl ToSoft for f32 {
210 type SoftFloat = rustc_apfloat::ieee::Single;
211
212 fn to_soft(self) -> Self::SoftFloat {
213 Float::from_bits(self.to_bits().into())
214 }
215}
216
217impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
218pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
219 fn have_module(&self, path: &[&str]) -> bool {
221 try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
222 }
223
224 fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> {
226 let this = self.eval_context_ref();
227 let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
228 this.eval_global(instance).unwrap_or_else(|err| {
230 panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
231 })
232 }
233 fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
234 let this = self.eval_context_ref();
235 let val = this.eval_path(path);
236 this.read_scalar(&val)
237 .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
238 }
239
240 fn eval_libc(&self, name: &str) -> Scalar {
242 if self.eval_context_ref().tcx.sess.target.os == "windows" {
243 panic!(
244 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
245 );
246 }
247 self.eval_path_scalar(&["libc", name])
248 }
249
250 fn eval_libc_i32(&self, name: &str) -> i32 {
252 self.eval_libc(name).to_i32().unwrap_or_else(|_err| {
254 panic!("required libc item has unexpected type (not `i32`): {name}")
255 })
256 }
257
258 fn eval_libc_u32(&self, name: &str) -> u32 {
260 self.eval_libc(name).to_u32().unwrap_or_else(|_err| {
262 panic!("required libc item has unexpected type (not `u32`): {name}")
263 })
264 }
265
266 fn eval_libc_u64(&self, name: &str) -> u64 {
268 self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
270 panic!("required libc item has unexpected type (not `u64`): {name}")
271 })
272 }
273
274 fn eval_windows(&self, module: &str, name: &str) -> Scalar {
276 self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
277 }
278
279 fn eval_windows_u32(&self, module: &str, name: &str) -> u32 {
281 self.eval_windows(module, name).to_u32().unwrap_or_else(|_err| {
283 panic!("required Windows item has unexpected type (not `u32`): {module}::{name}")
284 })
285 }
286
287 fn eval_windows_u64(&self, module: &str, name: &str) -> u64 {
289 self.eval_windows(module, name).to_u64().unwrap_or_else(|_err| {
291 panic!("required Windows item has unexpected type (not `u64`): {module}::{name}")
292 })
293 }
294
295 fn libc_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
297 let this = self.eval_context_ref();
298 if this.tcx.sess.target.os == "windows" {
299 panic!(
300 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
301 );
302 }
303 path_ty_layout(this, &["libc", name])
304 }
305
306 fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
308 let this = self.eval_context_ref();
309 path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
310 }
311
312 fn libc_array_ty_layout(&self, name: &str, size: u64) -> TyAndLayout<'tcx> {
314 let this = self.eval_context_ref();
315 let elem_ty_layout = this.libc_ty_layout(name);
316 let array_ty = Ty::new_array(*this.tcx, elem_ty_layout.ty, size);
317 this.layout_of(array_ty).unwrap()
318 }
319
320 fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
322 &self,
323 base: &P,
324 name: &str,
325 ) -> InterpResult<'tcx, Option<P>> {
326 let this = self.eval_context_ref();
327 let adt = base.layout().ty.ty_adt_def().unwrap();
328 for (idx, field) in adt.non_enum_variant().fields.iter().enumerate() {
329 if field.name.as_str() == name {
330 return interp_ok(Some(this.project_field(base, idx)?));
331 }
332 }
333 interp_ok(None)
334 }
335
336 fn project_field_named<P: Projectable<'tcx, Provenance>>(
338 &self,
339 base: &P,
340 name: &str,
341 ) -> InterpResult<'tcx, P> {
342 interp_ok(
343 self.try_project_field_named(base, name)?
344 .unwrap_or_else(|| bug!("no field named {} in type {}", name, base.layout().ty)),
345 )
346 }
347
348 fn write_int(
352 &mut self,
353 i: impl Into<i128>,
354 dest: &impl Writeable<'tcx, Provenance>,
355 ) -> InterpResult<'tcx> {
356 assert!(
357 dest.layout().backend_repr.is_scalar(),
358 "write_int on non-scalar type {}",
359 dest.layout().ty
360 );
361 let val = if dest.layout().backend_repr.is_signed() {
362 Scalar::from_int(i, dest.layout().size)
363 } else {
364 Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
366 };
367 self.eval_context_mut().write_scalar(val, dest)
368 }
369
370 fn write_int_fields(
372 &mut self,
373 values: &[i128],
374 dest: &impl Writeable<'tcx, Provenance>,
375 ) -> InterpResult<'tcx> {
376 let this = self.eval_context_mut();
377 for (idx, &val) in values.iter().enumerate() {
378 let field = this.project_field(dest, idx)?;
379 this.write_int(val, &field)?;
380 }
381 interp_ok(())
382 }
383
384 fn write_int_fields_named(
386 &mut self,
387 values: &[(&str, i128)],
388 dest: &impl Writeable<'tcx, Provenance>,
389 ) -> InterpResult<'tcx> {
390 let this = self.eval_context_mut();
391 for &(name, val) in values.iter() {
392 let field = this.project_field_named(dest, name)?;
393 this.write_int(val, &field)?;
394 }
395 interp_ok(())
396 }
397
398 fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> {
400 self.write_int(0, dest)
401 }
402
403 fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
405 interp_ok(ptr.addr().bytes() == 0)
406 }
407
408 fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
410 if len == 0 {
416 return interp_ok(());
417 }
418 let this = self.eval_context_mut();
419
420 let mut data = vec![0; usize::try_from(len).unwrap()];
421
422 if this.machine.communicate() {
423 getrandom::fill(&mut data)
425 .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
426 } else {
427 let rng = this.machine.rng.get_mut();
428 rng.fill_bytes(&mut data);
429 }
430
431 this.write_bytes_ptr(ptr, data.iter().copied())
432 }
433
434 fn call_function(
440 &mut self,
441 f: ty::Instance<'tcx>,
442 caller_abi: ExternAbi,
443 args: &[ImmTy<'tcx>],
444 dest: Option<&MPlaceTy<'tcx>>,
445 stack_pop: StackPopCleanup,
446 ) -> InterpResult<'tcx> {
447 let this = self.eval_context_mut();
448
449 let mir = this.load_mir(f.def, None)?;
451 let dest = match dest {
452 Some(dest) => dest.clone(),
453 None => MPlaceTy::fake_alloc_zst(this.layout_of(mir.return_ty())?),
454 };
455
456 let sig = this.tcx.mk_fn_sig(
458 args.iter().map(|a| a.layout.ty),
459 dest.layout.ty,
460 false,
461 Safety::Safe,
462 caller_abi,
463 );
464 let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
465
466 this.init_stack_frame(
467 f,
468 mir,
469 caller_fn_abi,
470 &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
471 false,
472 &dest,
473 stack_pop,
474 )
475 }
476
477 fn visit_freeze_sensitive(
481 &self,
482 place: &MPlaceTy<'tcx>,
483 size: Size,
484 mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
485 ) -> InterpResult<'tcx> {
486 let this = self.eval_context_ref();
487 trace!("visit_frozen(place={:?}, size={:?})", *place, size);
488 debug_assert_eq!(
489 size,
490 this.size_and_align_of_mplace(place)?
491 .map(|(size, _)| size)
492 .unwrap_or_else(|| place.layout.size)
493 );
494 let start_addr = place.ptr().addr();
498 let mut cur_addr = start_addr;
499 let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
502 let unsafe_cell_addr = unsafe_cell_ptr.addr();
505 assert!(unsafe_cell_addr >= cur_addr);
506 let frozen_size = unsafe_cell_addr - cur_addr;
507 if frozen_size != Size::ZERO {
509 action(alloc_range(cur_addr - start_addr, frozen_size), true)?;
510 }
511 cur_addr += frozen_size;
512 if unsafe_cell_size != Size::ZERO {
514 action(
515 alloc_range(cur_addr - start_addr, unsafe_cell_size),
516 false,
517 )?;
518 }
519 cur_addr += unsafe_cell_size;
520 interp_ok(())
522 };
523 {
525 let mut visitor = UnsafeCellVisitor {
526 ecx: this,
527 unsafe_cell_action: |place| {
528 trace!("unsafe_cell_action on {:?}", place.ptr());
529 let unsafe_cell_size = this
531 .size_and_align_of_mplace(place)?
532 .map(|(size, _)| size)
533 .unwrap_or_else(|| place.layout.size);
535 if unsafe_cell_size != Size::ZERO {
537 unsafe_cell_action(&place.ptr(), unsafe_cell_size)
538 } else {
539 interp_ok(())
540 }
541 },
542 };
543 visitor.visit_value(place)?;
544 }
545 unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?;
548 return interp_ok(());
550
551 struct UnsafeCellVisitor<'ecx, 'tcx, F>
554 where
555 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
556 {
557 ecx: &'ecx MiriInterpCx<'tcx>,
558 unsafe_cell_action: F,
559 }
560
561 impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
562 where
563 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
564 {
565 type V = MPlaceTy<'tcx>;
566
567 #[inline(always)]
568 fn ecx(&self) -> &MiriInterpCx<'tcx> {
569 self.ecx
570 }
571
572 fn aggregate_field_iter(
573 memory_index: &IndexVec<FieldIdx, u32>,
574 ) -> impl Iterator<Item = FieldIdx> + 'static {
575 let inverse_memory_index = memory_index.invert_bijective_mapping();
576 inverse_memory_index.into_iter()
577 }
578
579 fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
581 trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
582 let is_unsafe_cell = match v.layout.ty.kind() {
583 ty::Adt(adt, _) =>
584 Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
585 _ => false,
586 };
587 if is_unsafe_cell {
588 (self.unsafe_cell_action)(v)
590 } else if self.ecx.type_is_freeze(v.layout.ty) {
591 interp_ok(())
593 } else if matches!(v.layout.fields, FieldsShape::Union(..)) {
594 (self.unsafe_cell_action)(v)
596 } else if matches!(v.layout.ty.kind(), ty::Dynamic(_, _, ty::DynStar)) {
597 (self.unsafe_cell_action)(v)
600 } else {
601 match v.layout.variants {
605 Variants::Multiple { .. } => {
606 (self.unsafe_cell_action)(v)
614 }
615 Variants::Single { .. } | Variants::Empty => {
616 self.walk_value(v)
619 }
620 }
621 }
622 }
623
624 fn visit_union(
625 &mut self,
626 _v: &MPlaceTy<'tcx>,
627 _fields: NonZero<usize>,
628 ) -> InterpResult<'tcx> {
629 bug!("we should have already handled unions in `visit_value`")
630 }
631 }
632 }
633
634 fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
638 if !self.eval_context_ref().machine.communicate() {
639 self.reject_in_isolation(name, RejectOpWith::Abort)?;
640 }
641 interp_ok(())
642 }
643
644 fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> InterpResult<'tcx> {
647 let this = self.eval_context_ref();
648 match reject_with {
649 RejectOpWith::Abort => isolation_abort_error(op_name),
650 RejectOpWith::WarningWithoutBacktrace => {
651 let mut emitted_warnings = this.machine.reject_in_isolation_warned.borrow_mut();
652 if !emitted_warnings.contains(op_name) {
653 emitted_warnings.insert(op_name.to_owned());
655 this.tcx
656 .dcx()
657 .warn(format!("{op_name} was made to return an error due to isolation"));
658 }
659
660 interp_ok(())
661 }
662 RejectOpWith::Warning => {
663 this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
664 interp_ok(())
665 }
666 RejectOpWith::NoWarning => interp_ok(()), }
668 }
669
670 fn assert_target_os(&self, target_os: &str, name: &str) {
674 assert_eq!(
675 self.eval_context_ref().tcx.sess.target.os,
676 target_os,
677 "`{name}` is only available on the `{target_os}` target OS",
678 )
679 }
680
681 fn check_target_os(&self, target_oses: &[&str], name: Symbol) -> InterpResult<'tcx> {
685 let target_os = self.eval_context_ref().tcx.sess.target.os.as_ref();
686 if !target_oses.contains(&target_os) {
687 throw_unsup_format!("`{name}` is not supported on {target_os}");
688 }
689 interp_ok(())
690 }
691
692 fn assert_target_os_is_unix(&self, name: &str) {
696 assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",);
697 }
698
699 fn target_os_is_unix(&self) -> bool {
700 self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
701 }
702
703 fn deref_pointer_as(
705 &self,
706 op: &impl Projectable<'tcx, Provenance>,
707 layout: TyAndLayout<'tcx>,
708 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
709 let this = self.eval_context_ref();
710 let ptr = this.read_pointer(op)?;
711 interp_ok(this.ptr_to_mplace(ptr, layout))
712 }
713
714 fn deref_pointer_and_offset(
716 &self,
717 op: &impl Projectable<'tcx, Provenance>,
718 offset: u64,
719 base_layout: TyAndLayout<'tcx>,
720 value_layout: TyAndLayout<'tcx>,
721 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
722 let this = self.eval_context_ref();
723 let op_place = this.deref_pointer_as(op, base_layout)?;
724 let offset = Size::from_bytes(offset);
725
726 assert!(base_layout.size >= offset + value_layout.size);
728 let value_place = op_place.offset(offset, value_layout, this)?;
729 interp_ok(value_place)
730 }
731
732 fn deref_pointer_and_read(
733 &self,
734 op: &impl Projectable<'tcx, Provenance>,
735 offset: u64,
736 base_layout: TyAndLayout<'tcx>,
737 value_layout: TyAndLayout<'tcx>,
738 ) -> InterpResult<'tcx, Scalar> {
739 let this = self.eval_context_ref();
740 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
741 this.read_scalar(&value_place)
742 }
743
744 fn deref_pointer_and_write(
745 &mut self,
746 op: &impl Projectable<'tcx, Provenance>,
747 offset: u64,
748 value: impl Into<Scalar>,
749 base_layout: TyAndLayout<'tcx>,
750 value_layout: TyAndLayout<'tcx>,
751 ) -> InterpResult<'tcx, ()> {
752 let this = self.eval_context_mut();
753 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
754 this.write_scalar(value, &value_place)
755 }
756
757 fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> {
761 let this = self.eval_context_mut();
762 let seconds_place = this.project_field(tp, 0)?;
763 let seconds_scalar = this.read_scalar(&seconds_place)?;
764 let seconds = seconds_scalar.to_target_isize(this)?;
765 let nanoseconds_place = this.project_field(tp, 1)?;
766 let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?;
767 let nanoseconds = nanoseconds_scalar.to_target_isize(this)?;
768
769 interp_ok(
770 try {
771 let seconds: u64 = seconds.try_into().ok()?;
773 let nanoseconds: u32 = nanoseconds.try_into().ok()?;
775 if nanoseconds >= 1_000_000_000 {
776 None?
778 }
779 Duration::new(seconds, nanoseconds)
780 },
781 )
782 }
783
784 fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
786 where
787 'tcx: 'a,
788 {
789 let this = self.eval_context_ref();
790 let (ptr, len) = slice.to_scalar_pair();
791 let ptr = ptr.to_pointer(this)?;
792 let len = len.to_target_usize(this)?;
793 let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
794 interp_ok(bytes)
795 }
796
797 fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
799 where
800 'tcx: 'a,
801 {
802 let this = self.eval_context_ref();
803 let size1 = Size::from_bytes(1);
804
805 let mut len = Size::ZERO;
807 loop {
808 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()?;
812 if byte == 0 {
813 break;
814 } else {
815 len += size1;
816 }
817 }
818
819 this.read_bytes_ptr_strip_provenance(ptr, len)
821 }
822
823 fn write_c_str(
829 &mut self,
830 c_str: &[u8],
831 ptr: Pointer,
832 size: u64,
833 ) -> InterpResult<'tcx, (bool, u64)> {
834 let string_length = u64::try_from(c_str.len()).unwrap();
837 let string_length = string_length.strict_add(1);
838 if size < string_length {
839 return interp_ok((false, string_length));
840 }
841 self.eval_context_mut()
842 .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
843 interp_ok((true, string_length))
844 }
845
846 fn read_c_str_with_char_size<T>(
849 &self,
850 mut ptr: Pointer,
851 size: Size,
852 align: Align,
853 ) -> InterpResult<'tcx, Vec<T>>
854 where
855 T: TryFrom<u128>,
856 <T as TryFrom<u128>>::Error: std::fmt::Debug,
857 {
858 assert_ne!(size, Size::ZERO);
859
860 let this = self.eval_context_ref();
861
862 this.check_ptr_align(ptr, align)?;
863
864 let mut wchars = Vec::new();
865 loop {
866 let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
870 if wchar_int == 0 {
871 break;
872 } else {
873 wchars.push(wchar_int.try_into().unwrap());
874 ptr = ptr.wrapping_offset(size, this);
875 }
876 }
877
878 interp_ok(wchars)
879 }
880
881 fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
883 self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
884 }
885
886 fn write_wide_str(
893 &mut self,
894 wide_str: &[u16],
895 ptr: Pointer,
896 size: u64,
897 ) -> InterpResult<'tcx, (bool, u64)> {
898 let string_length = u64::try_from(wide_str.len()).unwrap();
901 let string_length = string_length.strict_add(1);
902 if size < string_length {
903 return interp_ok((false, string_length));
904 }
905
906 let size2 = Size::from_bytes(2);
908 let this = self.eval_context_mut();
909 this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
910 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() {
912 let offset = u64::try_from(offset).unwrap();
913 alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
914 }
915 interp_ok((true, string_length))
916 }
917
918 fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
921 let this = self.eval_context_ref();
922 let wchar_t = if this.tcx.sess.target.os == "windows" {
923 this.machine.layouts.u16
925 } else {
926 this.libc_ty_layout("wchar_t")
927 };
928 self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
929 }
930
931 fn check_abi<'a>(&self, fn_abi: &FnAbi<'tcx, Ty<'tcx>>, exp_abi: Conv) -> InterpResult<'a, ()> {
933 if fn_abi.conv != exp_abi {
934 throw_ub_format!(
935 "calling a function with ABI {:?} using caller ABI {:?}",
936 exp_abi,
937 fn_abi.conv
938 );
939 }
940 interp_ok(())
941 }
942
943 fn frame_in_std(&self) -> bool {
944 let this = self.eval_context_ref();
945 let frame = this.frame();
946 let instance: Option<_> = try {
948 let scope = frame.current_source_info()?.scope;
949 let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
950 let source = &frame.body().source_scopes[inlined_parent];
951 source.inlined.expect("inlined_parent_scope points to scope without inline info").0
952 };
953 let instance = instance.unwrap_or(frame.instance());
955 let frame_crate = this.tcx.def_path(instance.def_id()).krate;
960 let crate_name = this.tcx.crate_name(frame_crate);
961 let crate_name = crate_name.as_str();
962 crate_name == "std" || crate_name == "std_miri_test"
964 }
965
966 fn check_abi_and_shim_symbol_clash(
967 &mut self,
968 abi: &FnAbi<'tcx, Ty<'tcx>>,
969 exp_abi: Conv,
970 link_name: Symbol,
971 ) -> InterpResult<'tcx, ()> {
972 self.check_abi(abi, exp_abi)?;
973 if let Some((body, instance)) = self.eval_context_mut().lookup_exported_symbol(link_name)? {
974 if self.eval_context_ref().tcx.is_compiler_builtins(instance.def_id().krate) {
980 return interp_ok(());
981 }
982
983 throw_machine_stop!(TerminationInfo::SymbolShimClashing {
984 link_name,
985 span: body.span.data(),
986 })
987 }
988 interp_ok(())
989 }
990
991 fn check_shim<'a, const N: usize>(
992 &mut self,
993 abi: &FnAbi<'tcx, Ty<'tcx>>,
994 exp_abi: Conv,
995 link_name: Symbol,
996 args: &'a [OpTy<'tcx>],
997 ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
998 where
999 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
1000 {
1001 self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
1002 check_arg_count(args)
1003 }
1004
1005 fn check_shim_variadic<'a, const N: usize>(
1008 &mut self,
1009 abi: &FnAbi<'tcx, Ty<'tcx>>,
1010 exp_abi: Conv,
1011 link_name: Symbol,
1012 args: &'a [OpTy<'tcx>],
1013 ) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])>
1014 where
1015 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
1016 {
1017 self.check_abi_and_shim_symbol_clash(abi, exp_abi, link_name)?;
1018 check_vargarg_fixed_arg_count(link_name, abi, args)
1019 }
1020
1021 fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
1023 let this = self.eval_context_mut();
1024 let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
1026 this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap();
1027 }
1028
1029 fn float_to_int_checked(
1033 &self,
1034 src: &ImmTy<'tcx>,
1035 cast_to: TyAndLayout<'tcx>,
1036 round: rustc_apfloat::Round,
1037 ) -> InterpResult<'tcx, Option<ImmTy<'tcx>>> {
1038 let this = self.eval_context_ref();
1039
1040 fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>(
1041 ecx: &MiriInterpCx<'tcx>,
1042 src: F,
1043 cast_to: TyAndLayout<'tcx>,
1044 round: rustc_apfloat::Round,
1045 ) -> (Scalar, rustc_apfloat::Status) {
1046 let int_size = cast_to.layout.size;
1047 match cast_to.ty.kind() {
1048 ty::Uint(_) => {
1050 let res = src.to_u128_r(int_size.bits_usize(), round, &mut false);
1051 (Scalar::from_uint(res.value, int_size), res.status)
1052 }
1053 ty::Int(_) => {
1055 let res = src.to_i128_r(int_size.bits_usize(), round, &mut false);
1056 (Scalar::from_int(res.value, int_size), res.status)
1057 }
1058 _ =>
1060 span_bug!(
1061 ecx.cur_span(),
1062 "attempted float-to-int conversion with non-int output type {}",
1063 cast_to.ty,
1064 ),
1065 }
1066 }
1067
1068 let ty::Float(fty) = src.layout.ty.kind() else {
1069 bug!("float_to_int_checked: non-float input type {}", src.layout.ty)
1070 };
1071
1072 let (val, status) = match fty {
1073 FloatTy::F16 =>
1074 float_to_int_inner::<Half>(this, src.to_scalar().to_f16()?, cast_to, round),
1075 FloatTy::F32 =>
1076 float_to_int_inner::<Single>(this, src.to_scalar().to_f32()?, cast_to, round),
1077 FloatTy::F64 =>
1078 float_to_int_inner::<Double>(this, src.to_scalar().to_f64()?, cast_to, round),
1079 FloatTy::F128 =>
1080 float_to_int_inner::<Quad>(this, src.to_scalar().to_f128()?, cast_to, round),
1081 };
1082
1083 if status.intersects(
1084 rustc_apfloat::Status::INVALID_OP
1085 | rustc_apfloat::Status::OVERFLOW
1086 | rustc_apfloat::Status::UNDERFLOW,
1087 ) {
1088 interp_ok(None)
1091 } else {
1092 interp_ok(Some(ImmTy::from_scalar(val, cast_to)))
1095 }
1096 }
1097
1098 fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
1100 let this = self.eval_context_ref();
1101 match ty.kind() {
1102 ty::Uint(UintTy::U8) => this.tcx.types.u16,
1104 ty::Uint(UintTy::U16) => this.tcx.types.u32,
1105 ty::Uint(UintTy::U32) => this.tcx.types.u64,
1106 ty::Uint(UintTy::U64) => this.tcx.types.u128,
1107 ty::Int(IntTy::I8) => this.tcx.types.i16,
1109 ty::Int(IntTy::I16) => this.tcx.types.i32,
1110 ty::Int(IntTy::I32) => this.tcx.types.i64,
1111 ty::Int(IntTy::I64) => this.tcx.types.i128,
1112 _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
1113 }
1114 }
1115
1116 fn expect_target_feature_for_intrinsic(
1121 &self,
1122 intrinsic: Symbol,
1123 target_feature: &str,
1124 ) -> InterpResult<'tcx, ()> {
1125 let this = self.eval_context_ref();
1126 if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) {
1127 throw_ub_format!(
1128 "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
1129 );
1130 }
1131 interp_ok(())
1132 }
1133
1134 fn lookup_link_section(&mut self, name: &str) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
1136 let this = self.eval_context_mut();
1137 let tcx = this.tcx.tcx;
1138
1139 let mut array = vec![];
1140
1141 iter_exported_symbols(tcx, |_cnum, def_id| {
1142 let attrs = tcx.codegen_fn_attrs(def_id);
1143 let Some(link_section) = attrs.link_section else {
1144 return interp_ok(());
1145 };
1146 if link_section.as_str() == name {
1147 let instance = ty::Instance::mono(tcx, def_id);
1148 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
1149 panic!(
1150 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
1151 )
1152 });
1153 let val = this.read_immediate(&const_val)?;
1154 array.push(val);
1155 }
1156 interp_ok(())
1157 })?;
1158
1159 interp_ok(array)
1160 }
1161}
1162
1163impl<'tcx> MiriMachine<'tcx> {
1164 pub fn current_span(&self) -> Span {
1169 self.threads.active_thread_ref().current_span()
1170 }
1171
1172 pub fn caller_span(&self) -> Span {
1178 let frame_idx = self.top_user_relevant_frame().unwrap();
1181 let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
1182 self.stack()[frame_idx].current_span()
1183 }
1184
1185 fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] {
1186 self.threads.active_thread_stack()
1187 }
1188
1189 fn top_user_relevant_frame(&self) -> Option<usize> {
1190 self.threads.active_thread_ref().top_user_relevant_frame()
1191 }
1192
1193 pub fn is_user_relevant(&self, frame: &Frame<'tcx, Provenance>) -> bool {
1195 let def_id = frame.instance().def_id();
1196 (def_id.is_local() || self.local_crates.contains(&def_id.krate))
1197 && !frame.instance().def.requires_caller_location(self.tcx)
1198 }
1199}
1200
1201pub fn check_arg_count<'a, 'tcx, const N: usize>(
1203 args: &'a [OpTy<'tcx>],
1204) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
1205where
1206 &'a [OpTy<'tcx>; N]: TryFrom<&'a [OpTy<'tcx>]>,
1207{
1208 if let Ok(ops) = args.try_into() {
1209 return interp_ok(ops);
1210 }
1211 throw_ub_format!("incorrect number of arguments: got {}, expected {}", args.len(), N)
1212}
1213
1214pub fn check_min_vararg_count<'a, 'tcx, const N: usize>(
1218 name: &'a str,
1219 args: &'a [OpTy<'tcx>],
1220) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]> {
1221 if let Some((ops, _)) = args.split_first_chunk() {
1222 return interp_ok(ops);
1223 }
1224 throw_ub_format!(
1225 "not enough variadic arguments for `{name}`: got {}, expected at least {}",
1226 args.len(),
1227 N
1228 )
1229}
1230
1231fn check_vargarg_fixed_arg_count<'a, 'tcx, const N: usize>(
1234 link_name: Symbol,
1235 abi: &FnAbi<'tcx, Ty<'tcx>>,
1236 args: &'a [OpTy<'tcx>],
1237) -> InterpResult<'tcx, (&'a [OpTy<'tcx>; N], &'a [OpTy<'tcx>])> {
1238 if !abi.c_variadic {
1239 throw_ub_format!("calling a variadic function with a non-variadic caller-side signature");
1240 }
1241 if abi.fixed_count != u32::try_from(N).unwrap() {
1242 throw_ub_format!(
1243 "incorrect number of fixed arguments for variadic function `{}`: got {}, expected {N}",
1244 link_name.as_str(),
1245 abi.fixed_count
1246 )
1247 }
1248 if let Some(args) = args.split_first_chunk() {
1249 return interp_ok(args);
1250 }
1251 throw_ub_format!(
1252 "incorrect number of arguments for `{}`: got {}, expected at least {}",
1253 link_name.as_str(),
1254 args.len(),
1255 N
1256 )
1257}
1258
1259pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
1260 throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
1261 "{name} not available when isolation is enabled",
1262 )))
1263}
1264
1265pub fn get_local_crates(tcx: TyCtxt<'_>) -> Vec<CrateNum> {
1268 let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
1271 .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
1272 .unwrap_or_default();
1273 let mut local_crates = Vec::new();
1274 for &crate_num in tcx.crates(()) {
1275 let name = tcx.crate_name(crate_num);
1276 let name = name.as_str();
1277 if local_crate_names.iter().any(|local_name| local_name == name) {
1278 local_crates.push(crate_num);
1279 }
1280 }
1281 local_crates
1282}
1283
1284pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
1285 let val = if b { -1 } else { 0 };
1289 Scalar::from_int(val, size)
1290}
1291
1292pub(crate) fn simd_element_to_bool(elem: ImmTy<'_>) -> InterpResult<'_, bool> {
1293 let val = elem.to_scalar().to_int(elem.layout.size)?;
1294 interp_ok(match val {
1295 0 => false,
1296 -1 => true,
1297 _ => throw_ub_format!("each element of a SIMD mask must be all-0-bits or all-1-bits"),
1298 })
1299}
1300
1301pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
1305 if success {
1306 u32::try_from(len.strict_sub(1)).unwrap()
1309 } else {
1310 u32::try_from(len).unwrap()
1313 }
1314}