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_apfloat::Float;
9use rustc_hash::FxHashSet;
10use rustc_hir::Safety;
11use rustc_hir::def::{DefKind, Namespace};
12use rustc_hir::def_id::{CRATE_DEF_INDEX, CrateNum, DefId, LOCAL_CRATE};
13use rustc_index::IndexVec;
14use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
15use rustc_middle::middle::dependency_format::Linkage;
16use rustc_middle::middle::exported_symbols::ExportedSymbol;
17use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout};
18use rustc_middle::ty::{self, IntTy, Ty, TyCtxt, UintTy};
19use rustc_session::config::CrateType;
20use rustc_span::{Span, Symbol};
21use rustc_symbol_mangling::mangle_internal_symbol;
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 let _trace = enter_trace_span!("try_resolve_did", ?path);
37
38 fn find_children<'tcx: 'a, 'a>(
40 tcx: TyCtxt<'tcx>,
41 item: DefId,
42 name: &'a str,
43 ) -> impl Iterator<Item = DefId> + 'a {
44 let name = Symbol::intern(name);
45 tcx.module_children(item)
46 .iter()
47 .filter(move |item| item.ident.name == name)
48 .map(move |item| item.res.def_id())
49 }
50
51 let (&crate_name, path) = path.split_first().expect("paths must have at least one segment");
53 let (modules, item) = if let Some(namespace) = namespace {
54 let (&item_name, modules) =
55 path.split_last().expect("non-module paths must have at least 2 segments");
56 (modules, Some((item_name, namespace)))
57 } else {
58 (path, None)
59 };
60
61 'crates: for krate in
66 tcx.crates(()).iter().filter(|&&krate| tcx.crate_name(krate).as_str() == crate_name)
67 {
68 let mut cur_item = DefId { krate: *krate, index: CRATE_DEF_INDEX };
69 for &segment in modules {
71 let Some(next_item) = find_children(tcx, cur_item, segment)
72 .find(|item| tcx.def_kind(item) == DefKind::Mod)
73 else {
74 continue 'crates;
75 };
76 cur_item = next_item;
77 }
78 match item {
80 Some((item_name, namespace)) => {
81 let Some(item) = find_children(tcx, cur_item, item_name)
82 .find(|item| tcx.def_kind(item).ns() == Some(namespace))
83 else {
84 continue 'crates;
85 };
86 return Some(item);
87 }
88 None => {
89 return Some(cur_item);
91 }
92 }
93 }
94 None
96}
97
98pub fn try_resolve_path<'tcx>(
100 tcx: TyCtxt<'tcx>,
101 path: &[&str],
102 namespace: Namespace,
103) -> Option<ty::Instance<'tcx>> {
104 let did = try_resolve_did(tcx, path, Some(namespace))?;
105 Some(ty::Instance::mono(tcx, did))
106}
107
108#[track_caller]
110pub fn resolve_path<'tcx>(
111 tcx: TyCtxt<'tcx>,
112 path: &[&str],
113 namespace: Namespace,
114) -> ty::Instance<'tcx> {
115 try_resolve_path(tcx, path, namespace)
116 .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
117}
118
119#[track_caller]
121pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
122 let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
123 cx.layout_of(ty).to_result().ok().unwrap()
124}
125
126pub fn iter_exported_symbols<'tcx>(
128 tcx: TyCtxt<'tcx>,
129 mut f: impl FnMut(CrateNum, DefId) -> InterpResult<'tcx>,
130) -> InterpResult<'tcx> {
131 let crate_items = tcx.hir_crate_items(());
135 for def_id in crate_items.definitions() {
136 let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
137 let codegen_attrs = tcx.codegen_fn_attrs(def_id);
138 codegen_attrs.contains_extern_indicator()
139 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
140 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
141 };
142 if exported {
143 f(LOCAL_CRATE, def_id.into())?;
144 }
145 }
146
147 let dependency_formats = tcx.dependency_formats(());
152 let dependency_format = dependency_formats
154 .get(&CrateType::Executable)
155 .expect("interpreting a non-executable crate");
156 for cnum in dependency_format
157 .iter_enumerated()
158 .filter_map(|(num, &linkage)| (linkage != Linkage::NotLinked).then_some(num))
159 {
160 if cnum == LOCAL_CRATE {
161 continue; }
163
164 for &(symbol, _export_info) in tcx.exported_non_generic_symbols(cnum) {
167 if let ExportedSymbol::NonGeneric(def_id) = symbol {
168 f(cnum, def_id)?;
169 }
170 }
171 }
172 interp_ok(())
173}
174
175pub trait ToHost {
177 type HostFloat;
178 fn to_host(self) -> Self::HostFloat;
179}
180
181pub trait ToSoft {
183 type SoftFloat;
184 fn to_soft(self) -> Self::SoftFloat;
185}
186
187impl ToHost for rustc_apfloat::ieee::Double {
188 type HostFloat = f64;
189
190 fn to_host(self) -> Self::HostFloat {
191 f64::from_bits(self.to_bits().try_into().unwrap())
192 }
193}
194
195impl ToSoft for f64 {
196 type SoftFloat = rustc_apfloat::ieee::Double;
197
198 fn to_soft(self) -> Self::SoftFloat {
199 Float::from_bits(self.to_bits().into())
200 }
201}
202
203impl ToHost for rustc_apfloat::ieee::Single {
204 type HostFloat = f32;
205
206 fn to_host(self) -> Self::HostFloat {
207 f32::from_bits(self.to_bits().try_into().unwrap())
208 }
209}
210
211impl ToSoft for f32 {
212 type SoftFloat = rustc_apfloat::ieee::Single;
213
214 fn to_soft(self) -> Self::SoftFloat {
215 Float::from_bits(self.to_bits().into())
216 }
217}
218
219impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
220pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
221 fn have_module(&self, path: &[&str]) -> bool {
223 try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
224 }
225
226 fn eval_path(&self, path: &[&str]) -> MPlaceTy<'tcx> {
228 let this = self.eval_context_ref();
229 let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
230 this.eval_global(instance).unwrap_or_else(|err| {
232 panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
233 })
234 }
235 fn eval_path_scalar(&self, path: &[&str]) -> Scalar {
236 let this = self.eval_context_ref();
237 let val = this.eval_path(path);
238 this.read_scalar(&val)
239 .unwrap_or_else(|err| panic!("failed to read required Rust item: {path:?}\n{err:?}"))
240 }
241
242 fn eval_libc(&self, name: &str) -> Scalar {
244 if self.eval_context_ref().tcx.sess.target.os == "windows" {
245 panic!(
246 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
247 );
248 }
249 self.eval_path_scalar(&["libc", name])
250 }
251
252 fn eval_libc_i32(&self, name: &str) -> i32 {
254 self.eval_libc(name).to_i32().unwrap_or_else(|_err| {
256 panic!("required libc item has unexpected type (not `i32`): {name}")
257 })
258 }
259
260 fn eval_libc_u32(&self, name: &str) -> u32 {
262 self.eval_libc(name).to_u32().unwrap_or_else(|_err| {
264 panic!("required libc item has unexpected type (not `u32`): {name}")
265 })
266 }
267
268 fn eval_libc_u64(&self, name: &str) -> u64 {
270 self.eval_libc(name).to_u64().unwrap_or_else(|_err| {
272 panic!("required libc item has unexpected type (not `u64`): {name}")
273 })
274 }
275
276 fn eval_windows(&self, module: &str, name: &str) -> Scalar {
278 self.eval_context_ref().eval_path_scalar(&["std", "sys", "pal", "windows", module, name])
279 }
280
281 fn eval_windows_u32(&self, module: &str, name: &str) -> u32 {
283 self.eval_windows(module, name).to_u32().unwrap_or_else(|_err| {
285 panic!("required Windows item has unexpected type (not `u32`): {module}::{name}")
286 })
287 }
288
289 fn eval_windows_u64(&self, module: &str, name: &str) -> u64 {
291 self.eval_windows(module, name).to_u64().unwrap_or_else(|_err| {
293 panic!("required Windows item has unexpected type (not `u64`): {module}::{name}")
294 })
295 }
296
297 fn libc_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
299 let this = self.eval_context_ref();
300 if this.tcx.sess.target.os == "windows" {
301 panic!(
302 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
303 );
304 }
305 path_ty_layout(this, &["libc", name])
306 }
307
308 fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
310 let this = self.eval_context_ref();
311 path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
312 }
313
314 fn libc_array_ty_layout(&self, name: &str, size: u64) -> TyAndLayout<'tcx> {
316 let this = self.eval_context_ref();
317 let elem_ty_layout = this.libc_ty_layout(name);
318 let array_ty = Ty::new_array(*this.tcx, elem_ty_layout.ty, size);
319 this.layout_of(array_ty).unwrap()
320 }
321
322 fn try_project_field_named<P: Projectable<'tcx, Provenance>>(
324 &self,
325 base: &P,
326 name: &str,
327 ) -> InterpResult<'tcx, Option<P>> {
328 let this = self.eval_context_ref();
329 let adt = base.layout().ty.ty_adt_def().unwrap();
330 for (idx, field) in adt.non_enum_variant().fields.iter_enumerated() {
331 if field.name.as_str() == name {
332 return interp_ok(Some(this.project_field(base, idx)?));
333 }
334 }
335 interp_ok(None)
336 }
337
338 fn project_field_named<P: Projectable<'tcx, Provenance>>(
340 &self,
341 base: &P,
342 name: &str,
343 ) -> InterpResult<'tcx, P> {
344 interp_ok(
345 self.try_project_field_named(base, name)?
346 .unwrap_or_else(|| bug!("no field named {} in type {}", name, base.layout().ty)),
347 )
348 }
349
350 fn write_int(
354 &mut self,
355 i: impl Into<i128>,
356 dest: &impl Writeable<'tcx, Provenance>,
357 ) -> InterpResult<'tcx> {
358 assert!(
359 dest.layout().backend_repr.is_scalar(),
360 "write_int on non-scalar type {}",
361 dest.layout().ty
362 );
363 let val = if dest.layout().backend_repr.is_signed() {
364 Scalar::from_int(i, dest.layout().size)
365 } else {
366 Scalar::from_uint(u128::try_from(i.into()).unwrap(), dest.layout().size)
368 };
369 self.eval_context_mut().write_scalar(val, dest)
370 }
371
372 fn write_int_fields(
374 &mut self,
375 values: &[i128],
376 dest: &impl Writeable<'tcx, Provenance>,
377 ) -> InterpResult<'tcx> {
378 let this = self.eval_context_mut();
379 for (idx, &val) in values.iter().enumerate() {
380 let idx = FieldIdx::from_usize(idx);
381 let field = this.project_field(dest, idx)?;
382 this.write_int(val, &field)?;
383 }
384 interp_ok(())
385 }
386
387 fn write_int_fields_named(
389 &mut self,
390 values: &[(&str, i128)],
391 dest: &impl Writeable<'tcx, Provenance>,
392 ) -> InterpResult<'tcx> {
393 let this = self.eval_context_mut();
394 for &(name, val) in values.iter() {
395 let field = this.project_field_named(dest, name)?;
396 this.write_int(val, &field)?;
397 }
398 interp_ok(())
399 }
400
401 fn write_null(&mut self, dest: &impl Writeable<'tcx, Provenance>) -> InterpResult<'tcx> {
403 self.write_int(0, dest)
404 }
405
406 fn ptr_is_null(&self, ptr: Pointer) -> InterpResult<'tcx, bool> {
408 interp_ok(ptr.addr().bytes() == 0)
409 }
410
411 fn gen_random(&mut self, ptr: Pointer, len: u64) -> InterpResult<'tcx> {
413 if len == 0 {
419 return interp_ok(());
420 }
421 let this = self.eval_context_mut();
422
423 let mut data = vec![0; usize::try_from(len).unwrap()];
424
425 if this.machine.communicate() {
426 getrandom::fill(&mut data)
428 .map_err(|err| err_unsup_format!("host getrandom failed: {}", err))?;
429 } else {
430 let rng = this.machine.rng.get_mut();
431 rng.fill_bytes(&mut data);
432 }
433
434 this.write_bytes_ptr(ptr, data.iter().copied())
435 }
436
437 fn call_function(
443 &mut self,
444 f: ty::Instance<'tcx>,
445 caller_abi: ExternAbi,
446 args: &[ImmTy<'tcx>],
447 dest: Option<&MPlaceTy<'tcx>>,
448 cont: ReturnContinuation,
449 ) -> InterpResult<'tcx> {
450 let this = self.eval_context_mut();
451
452 let mir = this.load_mir(f.def, None)?;
454 let dest = match dest {
455 Some(dest) => dest.clone(),
456 None => MPlaceTy::fake_alloc_zst(this.machine.layouts.unit),
457 };
458
459 let sig = this.tcx.mk_fn_sig(
461 args.iter().map(|a| a.layout.ty),
462 dest.layout.ty,
463 false,
464 Safety::Safe,
465 caller_abi,
466 );
467 let caller_fn_abi = this.fn_abi_of_fn_ptr(ty::Binder::dummy(sig), ty::List::empty())?;
468
469 this.init_stack_frame(
471 f,
472 mir,
473 caller_fn_abi,
474 &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
475 false,
476 &dest.into(),
477 cont,
478 )
479 }
480
481 fn visit_freeze_sensitive(
485 &self,
486 place: &MPlaceTy<'tcx>,
487 size: Size,
488 mut action: impl FnMut(AllocRange, bool) -> InterpResult<'tcx>,
489 ) -> InterpResult<'tcx> {
490 let this = self.eval_context_ref();
491 trace!("visit_frozen(place={:?}, size={:?})", *place, size);
492 debug_assert_eq!(
493 size,
494 this.size_and_align_of_val(place)?
495 .map(|(size, _)| size)
496 .unwrap_or_else(|| place.layout.size)
497 );
498 let start_addr = place.ptr().addr();
502 let mut cur_addr = start_addr;
503 let mut unsafe_cell_action = |unsafe_cell_ptr: &Pointer, unsafe_cell_size: Size| {
506 let unsafe_cell_addr = unsafe_cell_ptr.addr();
509 assert!(unsafe_cell_addr >= cur_addr);
510 let frozen_size = unsafe_cell_addr - cur_addr;
511 if frozen_size != Size::ZERO {
513 action(alloc_range(cur_addr - start_addr, frozen_size), true)?;
514 }
515 cur_addr += frozen_size;
516 if unsafe_cell_size != Size::ZERO {
518 action(
519 alloc_range(cur_addr - start_addr, unsafe_cell_size),
520 false,
521 )?;
522 }
523 cur_addr += unsafe_cell_size;
524 interp_ok(())
526 };
527 {
529 let mut visitor = UnsafeCellVisitor {
530 ecx: this,
531 unsafe_cell_action: |place| {
532 trace!("unsafe_cell_action on {:?}", place.ptr());
533 let unsafe_cell_size = this
535 .size_and_align_of_val(place)?
536 .map(|(size, _)| size)
537 .unwrap_or_else(|| place.layout.size);
539 if unsafe_cell_size != Size::ZERO {
541 unsafe_cell_action(&place.ptr(), unsafe_cell_size)
542 } else {
543 interp_ok(())
544 }
545 },
546 };
547 visitor.visit_value(place)?;
548 }
549 unsafe_cell_action(&place.ptr().wrapping_offset(size, this), Size::ZERO)?;
552 return interp_ok(());
554
555 struct UnsafeCellVisitor<'ecx, 'tcx, F>
558 where
559 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
560 {
561 ecx: &'ecx MiriInterpCx<'tcx>,
562 unsafe_cell_action: F,
563 }
564
565 impl<'ecx, 'tcx, F> ValueVisitor<'tcx, MiriMachine<'tcx>> for UnsafeCellVisitor<'ecx, 'tcx, F>
566 where
567 F: FnMut(&MPlaceTy<'tcx>) -> InterpResult<'tcx>,
568 {
569 type V = MPlaceTy<'tcx>;
570
571 #[inline(always)]
572 fn ecx(&self) -> &MiriInterpCx<'tcx> {
573 self.ecx
574 }
575
576 fn aggregate_field_iter(
577 memory_index: &IndexVec<FieldIdx, u32>,
578 ) -> impl Iterator<Item = FieldIdx> + 'static {
579 let inverse_memory_index = memory_index.invert_bijective_mapping();
580 inverse_memory_index.into_iter()
581 }
582
583 fn visit_value(&mut self, v: &MPlaceTy<'tcx>) -> InterpResult<'tcx> {
585 trace!("UnsafeCellVisitor: {:?} {:?}", *v, v.layout.ty);
586 let is_unsafe_cell = match v.layout.ty.kind() {
587 ty::Adt(adt, _) =>
588 Some(adt.did()) == self.ecx.tcx.lang_items().unsafe_cell_type(),
589 _ => false,
590 };
591 if is_unsafe_cell {
592 (self.unsafe_cell_action)(v)
594 } else if self.ecx.type_is_freeze(v.layout.ty) {
595 interp_ok(())
597 } else if matches!(v.layout.fields, FieldsShape::Union(..)) {
598 (self.unsafe_cell_action)(v)
600 } else {
601 match v.layout.variants {
608 Variants::Multiple { .. } => {
609 (self.unsafe_cell_action)(v)
617 }
618 Variants::Single { .. } | Variants::Empty => {
619 self.walk_value(v)
622 }
623 }
624 }
625 }
626
627 fn visit_union(
628 &mut self,
629 _v: &MPlaceTy<'tcx>,
630 _fields: NonZero<usize>,
631 ) -> InterpResult<'tcx> {
632 bug!("we should have already handled unions in `visit_value`")
633 }
634 }
635 }
636
637 fn check_no_isolation(&self, name: &str) -> InterpResult<'tcx> {
641 if !self.eval_context_ref().machine.communicate() {
642 self.reject_in_isolation(name, RejectOpWith::Abort)?;
643 }
644 interp_ok(())
645 }
646
647 fn reject_in_isolation(&self, op_name: &str, reject_with: RejectOpWith) -> InterpResult<'tcx> {
650 let this = self.eval_context_ref();
651 match reject_with {
652 RejectOpWith::Abort => isolation_abort_error(op_name),
653 RejectOpWith::WarningWithoutBacktrace => {
654 static DEDUP: Mutex<FxHashSet<String>> =
656 Mutex::new(FxHashSet::with_hasher(rustc_hash::FxBuildHasher));
657 let mut emitted_warnings = DEDUP.lock().unwrap();
658 if !emitted_warnings.contains(op_name) {
659 emitted_warnings.insert(op_name.to_owned());
661 this.tcx
662 .dcx()
663 .warn(format!("{op_name} was made to return an error due to isolation"));
664 }
665
666 interp_ok(())
667 }
668 RejectOpWith::Warning => {
669 this.emit_diagnostic(NonHaltingDiagnostic::RejectedIsolatedOp(op_name.to_string()));
670 interp_ok(())
671 }
672 RejectOpWith::NoWarning => interp_ok(()), }
674 }
675
676 fn assert_target_os(&self, target_os: &str, name: &str) {
680 assert_eq!(
681 self.eval_context_ref().tcx.sess.target.os,
682 target_os,
683 "`{name}` is only available on the `{target_os}` target OS",
684 )
685 }
686
687 fn check_target_os(&self, target_oses: &[&str], name: Symbol) -> InterpResult<'tcx> {
691 let target_os = self.eval_context_ref().tcx.sess.target.os.as_ref();
692 if !target_oses.contains(&target_os) {
693 throw_unsup_format!("`{name}` is not supported on {target_os}");
694 }
695 interp_ok(())
696 }
697
698 fn assert_target_os_is_unix(&self, name: &str) {
702 assert!(self.target_os_is_unix(), "`{name}` is only available for unix targets",);
703 }
704
705 fn target_os_is_unix(&self) -> bool {
706 self.eval_context_ref().tcx.sess.target.families.iter().any(|f| f == "unix")
707 }
708
709 fn deref_pointer_as(
711 &self,
712 op: &impl Projectable<'tcx, Provenance>,
713 layout: TyAndLayout<'tcx>,
714 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
715 let this = self.eval_context_ref();
716 let ptr = this.read_pointer(op)?;
717 interp_ok(this.ptr_to_mplace(ptr, layout))
718 }
719
720 fn deref_pointer_and_offset(
722 &self,
723 op: &impl Projectable<'tcx, Provenance>,
724 offset: u64,
725 base_layout: TyAndLayout<'tcx>,
726 value_layout: TyAndLayout<'tcx>,
727 ) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
728 let this = self.eval_context_ref();
729 let op_place = this.deref_pointer_as(op, base_layout)?;
730 let offset = Size::from_bytes(offset);
731
732 assert!(base_layout.size >= offset + value_layout.size);
734 let value_place = op_place.offset(offset, value_layout, this)?;
735 interp_ok(value_place)
736 }
737
738 fn deref_pointer_and_read(
739 &self,
740 op: &impl Projectable<'tcx, Provenance>,
741 offset: u64,
742 base_layout: TyAndLayout<'tcx>,
743 value_layout: TyAndLayout<'tcx>,
744 ) -> InterpResult<'tcx, Scalar> {
745 let this = self.eval_context_ref();
746 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
747 this.read_scalar(&value_place)
748 }
749
750 fn deref_pointer_and_write(
751 &mut self,
752 op: &impl Projectable<'tcx, Provenance>,
753 offset: u64,
754 value: impl Into<Scalar>,
755 base_layout: TyAndLayout<'tcx>,
756 value_layout: TyAndLayout<'tcx>,
757 ) -> InterpResult<'tcx, ()> {
758 let this = self.eval_context_mut();
759 let value_place = this.deref_pointer_and_offset(op, offset, base_layout, value_layout)?;
760 this.write_scalar(value, &value_place)
761 }
762
763 fn read_timespec(&mut self, tp: &MPlaceTy<'tcx>) -> InterpResult<'tcx, Option<Duration>> {
767 let this = self.eval_context_mut();
768 let seconds_place = this.project_field(tp, FieldIdx::ZERO)?;
769 let seconds_scalar = this.read_scalar(&seconds_place)?;
770 let seconds = seconds_scalar.to_target_isize(this)?;
771 let nanoseconds_place = this.project_field(tp, FieldIdx::ONE)?;
772 let nanoseconds_scalar = this.read_scalar(&nanoseconds_place)?;
773 let nanoseconds = nanoseconds_scalar.to_target_isize(this)?;
774
775 interp_ok(
776 try {
777 let seconds: u64 = seconds.try_into().ok()?;
779 let nanoseconds: u32 = nanoseconds.try_into().ok()?;
781 if nanoseconds >= 1_000_000_000 {
782 None?
784 }
785 Duration::new(seconds, nanoseconds)
786 },
787 )
788 }
789
790 fn read_byte_slice<'a>(&'a self, slice: &ImmTy<'tcx>) -> InterpResult<'tcx, &'a [u8]>
792 where
793 'tcx: 'a,
794 {
795 let this = self.eval_context_ref();
796 let (ptr, len) = slice.to_scalar_pair();
797 let ptr = ptr.to_pointer(this)?;
798 let len = len.to_target_usize(this)?;
799 let bytes = this.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
800 interp_ok(bytes)
801 }
802
803 fn read_c_str<'a>(&'a self, ptr: Pointer) -> InterpResult<'tcx, &'a [u8]>
805 where
806 'tcx: 'a,
807 {
808 let this = self.eval_context_ref();
809 let size1 = Size::from_bytes(1);
810
811 let mut len = Size::ZERO;
813 loop {
814 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()?;
818 if byte == 0 {
819 break;
820 } else {
821 len += size1;
822 }
823 }
824
825 this.read_bytes_ptr_strip_provenance(ptr, len)
827 }
828
829 fn write_c_str(
835 &mut self,
836 c_str: &[u8],
837 ptr: Pointer,
838 size: u64,
839 ) -> InterpResult<'tcx, (bool, u64)> {
840 let string_length = u64::try_from(c_str.len()).unwrap();
843 let string_length = string_length.strict_add(1);
844 if size < string_length {
845 return interp_ok((false, string_length));
846 }
847 self.eval_context_mut()
848 .write_bytes_ptr(ptr, c_str.iter().copied().chain(iter::once(0u8)))?;
849 interp_ok((true, string_length))
850 }
851
852 fn read_c_str_with_char_size<T>(
855 &self,
856 mut ptr: Pointer,
857 size: Size,
858 align: Align,
859 ) -> InterpResult<'tcx, Vec<T>>
860 where
861 T: TryFrom<u128>,
862 <T as TryFrom<u128>>::Error: std::fmt::Debug,
863 {
864 assert_ne!(size, Size::ZERO);
865
866 let this = self.eval_context_ref();
867
868 this.check_ptr_align(ptr, align)?;
869
870 let mut wchars = Vec::new();
871 loop {
872 let alloc = this.get_ptr_alloc(ptr, size)?.unwrap(); let wchar_int = alloc.read_integer(alloc_range(Size::ZERO, size))?.to_bits(size)?;
876 if wchar_int == 0 {
877 break;
878 } else {
879 wchars.push(wchar_int.try_into().unwrap());
880 ptr = ptr.wrapping_offset(size, this);
881 }
882 }
883
884 interp_ok(wchars)
885 }
886
887 fn read_wide_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u16>> {
889 self.read_c_str_with_char_size(ptr, Size::from_bytes(2), Align::from_bytes(2).unwrap())
890 }
891
892 fn write_wide_str(
899 &mut self,
900 wide_str: &[u16],
901 ptr: Pointer,
902 size: u64,
903 ) -> InterpResult<'tcx, (bool, u64)> {
904 let string_length = u64::try_from(wide_str.len()).unwrap();
907 let string_length = string_length.strict_add(1);
908 if size < string_length {
909 return interp_ok((false, string_length));
910 }
911
912 let size2 = Size::from_bytes(2);
914 let this = self.eval_context_mut();
915 this.check_ptr_align(ptr, Align::from_bytes(2).unwrap())?;
916 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() {
918 let offset = u64::try_from(offset).unwrap();
919 alloc.write_scalar(alloc_range(size2 * offset, size2), Scalar::from_u16(wchar))?;
920 }
921 interp_ok((true, string_length))
922 }
923
924 fn read_wchar_t_str(&self, ptr: Pointer) -> InterpResult<'tcx, Vec<u32>> {
927 let this = self.eval_context_ref();
928 let wchar_t = if this.tcx.sess.target.os == "windows" {
929 this.machine.layouts.u16
931 } else {
932 this.libc_ty_layout("wchar_t")
933 };
934 self.read_c_str_with_char_size(ptr, wchar_t.size, wchar_t.align.abi)
935 }
936
937 fn frame_in_std(&self) -> bool {
938 let this = self.eval_context_ref();
939 let frame = this.frame();
940 let instance: Option<_> = try {
942 let scope = frame.current_source_info()?.scope;
943 let inlined_parent = frame.body().source_scopes[scope].inlined_parent_scope?;
944 let source = &frame.body().source_scopes[inlined_parent];
945 source.inlined.expect("inlined_parent_scope points to scope without inline info").0
946 };
947 let instance = instance.unwrap_or(frame.instance());
949 let frame_crate = this.tcx.def_path(instance.def_id()).krate;
954 let crate_name = this.tcx.crate_name(frame_crate);
955 let crate_name = crate_name.as_str();
956 crate_name == "std" || crate_name == "std_miri_test"
958 }
959
960 fn mark_immutable(&mut self, mplace: &MPlaceTy<'tcx>) {
962 let this = self.eval_context_mut();
963 let provenance = mplace.ptr().into_pointer_or_addr().unwrap().provenance;
965 this.alloc_mark_immutable(provenance.get_alloc_id().unwrap()).unwrap();
966 }
967
968 fn get_twice_wide_int_ty(&self, ty: Ty<'tcx>) -> Ty<'tcx> {
970 let this = self.eval_context_ref();
971 match ty.kind() {
972 ty::Uint(UintTy::U8) => this.tcx.types.u16,
974 ty::Uint(UintTy::U16) => this.tcx.types.u32,
975 ty::Uint(UintTy::U32) => this.tcx.types.u64,
976 ty::Uint(UintTy::U64) => this.tcx.types.u128,
977 ty::Int(IntTy::I8) => this.tcx.types.i16,
979 ty::Int(IntTy::I16) => this.tcx.types.i32,
980 ty::Int(IntTy::I32) => this.tcx.types.i64,
981 ty::Int(IntTy::I64) => this.tcx.types.i128,
982 _ => span_bug!(this.cur_span(), "unexpected type: {ty:?}"),
983 }
984 }
985
986 fn expect_target_feature_for_intrinsic(
991 &self,
992 intrinsic: Symbol,
993 target_feature: &str,
994 ) -> InterpResult<'tcx, ()> {
995 let this = self.eval_context_ref();
996 if !this.tcx.sess.unstable_target_features.contains(&Symbol::intern(target_feature)) {
997 throw_ub_format!(
998 "attempted to call intrinsic `{intrinsic}` that requires missing target feature {target_feature}"
999 );
1000 }
1001 interp_ok(())
1002 }
1003
1004 fn lookup_link_section(
1006 &mut self,
1007 include_name: impl Fn(&str) -> bool,
1008 ) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
1009 let this = self.eval_context_mut();
1010 let tcx = this.tcx.tcx;
1011
1012 let mut array = vec![];
1013
1014 iter_exported_symbols(tcx, |_cnum, def_id| {
1015 let attrs = tcx.codegen_fn_attrs(def_id);
1016 let Some(link_section) = attrs.link_section else {
1017 return interp_ok(());
1018 };
1019 if include_name(link_section.as_str()) {
1020 let instance = ty::Instance::mono(tcx, def_id);
1021 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
1022 panic!(
1023 "failed to evaluate static in required link_section: {def_id:?}\n{err:?}"
1024 )
1025 });
1026 match const_val.layout.ty.kind() {
1027 ty::FnPtr(..) => {
1028 array.push(this.read_immediate(&const_val)?);
1029 }
1030 ty::Array(elem_ty, _) if matches!(elem_ty.kind(), ty::FnPtr(..)) => {
1031 let mut elems = this.project_array_fields(&const_val)?;
1032 while let Some((_idx, elem)) = elems.next(this)? {
1033 array.push(this.read_immediate(&elem)?);
1034 }
1035 }
1036 _ =>
1037 throw_unsup_format!(
1038 "only function pointers and arrays of function pointers are supported in well-known linker sections"
1039 ),
1040 }
1041 }
1042 interp_ok(())
1043 })?;
1044
1045 interp_ok(array)
1046 }
1047
1048 fn mangle_internal_symbol<'a>(&'a mut self, name: &'static str) -> &'a str
1049 where
1050 'tcx: 'a,
1051 {
1052 let this = self.eval_context_mut();
1053 let tcx = *this.tcx;
1054 this.machine
1055 .mangle_internal_symbol_cache
1056 .entry(name)
1057 .or_insert_with(|| mangle_internal_symbol(tcx, name))
1058 }
1059}
1060
1061impl<'tcx> MiriMachine<'tcx> {
1062 pub fn current_user_relevant_span(&self) -> Span {
1067 self.threads.active_thread_ref().current_user_relevant_span()
1068 }
1069
1070 pub fn caller_span(&self) -> Span {
1076 let frame_idx = self.top_user_relevant_frame().unwrap();
1079 let frame_idx = cmp::min(frame_idx, self.stack().len().saturating_sub(2));
1080 self.stack()[frame_idx].current_span()
1081 }
1082
1083 fn stack(&self) -> &[Frame<'tcx, Provenance, machine::FrameExtra<'tcx>>] {
1084 self.threads.active_thread_stack()
1085 }
1086
1087 fn top_user_relevant_frame(&self) -> Option<usize> {
1088 self.threads.active_thread_ref().top_user_relevant_frame()
1089 }
1090
1091 pub fn is_user_relevant(&self, frame: &Frame<'tcx, Provenance>) -> bool {
1093 let def_id = frame.instance().def_id();
1094 (def_id.is_local() || self.local_crates.contains(&def_id.krate))
1095 && !frame.instance().def.requires_caller_location(self.tcx)
1096 }
1097}
1098
1099pub fn isolation_abort_error<'tcx>(name: &str) -> InterpResult<'tcx> {
1100 throw_machine_stop!(TerminationInfo::UnsupportedInIsolation(format!(
1101 "{name} not available when isolation is enabled",
1102 )))
1103}
1104
1105pub fn get_local_crates(tcx: TyCtxt<'_>) -> Vec<CrateNum> {
1108 let local_crate_names = std::env::var("MIRI_LOCAL_CRATES")
1111 .map(|crates| crates.split(',').map(|krate| krate.to_string()).collect::<Vec<_>>())
1112 .unwrap_or_default();
1113 let mut local_crates = Vec::new();
1114 for &crate_num in tcx.crates(()) {
1115 let name = tcx.crate_name(crate_num);
1116 let name = name.as_str();
1117 if local_crate_names.iter().any(|local_name| local_name == name) {
1118 local_crates.push(crate_num);
1119 }
1120 }
1121 local_crates
1122}
1123
1124pub(crate) fn bool_to_simd_element(b: bool, size: Size) -> Scalar {
1125 let val = if b { -1 } else { 0 };
1129 Scalar::from_int(val, size)
1130}
1131
1132pub(crate) fn windows_check_buffer_size((success, len): (bool, u64)) -> u32 {
1136 if success {
1137 u32::try_from(len.strict_sub(1)).unwrap()
1140 } else {
1141 u32::try_from(len).unwrap()
1144 }
1145}
1146
1147pub trait ToUsize {
1149 fn to_usize(self) -> usize;
1150}
1151
1152impl ToUsize for u32 {
1153 fn to_usize(self) -> usize {
1154 self.try_into().unwrap()
1155 }
1156}
1157
1158pub trait ToU64 {
1161 fn to_u64(self) -> u64;
1162}
1163
1164impl ToU64 for usize {
1165 fn to_u64(self) -> u64 {
1166 self.try_into().unwrap()
1167 }
1168}
1169
1170#[macro_export]
1176macro_rules! enter_trace_span {
1177 ($($tt:tt)*) => {
1178 rustc_const_eval::enter_trace_span!($crate::MiriMachine<'static>, $($tt)*)
1179 };
1180}