]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_mir/src/interpret/memory.rs
feat(rustdoc): open sidebar menu when links inside it are focused
[rust.git] / compiler / rustc_mir / src / interpret / memory.rs
1 //! The memory subsystem.
2 //!
3 //! Generally, we use `Pointer` to denote memory addresses. However, some operations
4 //! have a "size"-like parameter, and they take `Scalar` for the address because
5 //! if the size is 0, then the pointer can also be a (properly aligned, non-null)
6 //! integer. It is crucial that these operations call `check_align` *before*
7 //! short-circuiting the empty case!
8
9 use std::borrow::Cow;
10 use std::collections::VecDeque;
11 use std::convert::{TryFrom, TryInto};
12 use std::fmt;
13 use std::ptr;
14
15 use rustc_ast::Mutability;
16 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
17 use rustc_middle::ty::{Instance, ParamEnv, TyCtxt};
18 use rustc_target::abi::{Align, HasDataLayout, Size, TargetDataLayout};
19
20 use super::{
21     alloc_range, AllocId, AllocMap, AllocRange, Allocation, CheckInAllocMsg, GlobalAlloc,
22     InterpResult, Machine, MayLeak, Pointer, PointerArithmetic, Scalar, ScalarMaybeUninit,
23 };
24 use crate::util::pretty;
25
26 #[derive(Debug, PartialEq, Copy, Clone)]
27 pub enum MemoryKind<T> {
28     /// Stack memory. Error if deallocated except during a stack pop.
29     Stack,
30     /// Memory allocated by `caller_location` intrinsic. Error if ever deallocated.
31     CallerLocation,
32     /// Additional memory kinds a machine wishes to distinguish from the builtin ones.
33     Machine(T),
34 }
35
36 impl<T: MayLeak> MayLeak for MemoryKind<T> {
37     #[inline]
38     fn may_leak(self) -> bool {
39         match self {
40             MemoryKind::Stack => false,
41             MemoryKind::CallerLocation => true,
42             MemoryKind::Machine(k) => k.may_leak(),
43         }
44     }
45 }
46
47 impl<T: fmt::Display> fmt::Display for MemoryKind<T> {
48     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49         match self {
50             MemoryKind::Stack => write!(f, "stack variable"),
51             MemoryKind::CallerLocation => write!(f, "caller location"),
52             MemoryKind::Machine(m) => write!(f, "{}", m),
53         }
54     }
55 }
56
57 /// Used by `get_size_and_align` to indicate whether the allocation needs to be live.
58 #[derive(Debug, Copy, Clone)]
59 pub enum AllocCheck {
60     /// Allocation must be live and not a function pointer.
61     Dereferenceable,
62     /// Allocations needs to be live, but may be a function pointer.
63     Live,
64     /// Allocation may be dead.
65     MaybeDead,
66 }
67
68 /// The value of a function pointer.
69 #[derive(Debug, Copy, Clone)]
70 pub enum FnVal<'tcx, Other> {
71     Instance(Instance<'tcx>),
72     Other(Other),
73 }
74
75 impl<'tcx, Other> FnVal<'tcx, Other> {
76     pub fn as_instance(self) -> InterpResult<'tcx, Instance<'tcx>> {
77         match self {
78             FnVal::Instance(instance) => Ok(instance),
79             FnVal::Other(_) => {
80                 throw_unsup_format!("'foreign' function pointers are not supported in this context")
81             }
82         }
83     }
84 }
85
86 // `Memory` has to depend on the `Machine` because some of its operations
87 // (e.g., `get`) call a `Machine` hook.
88 pub struct Memory<'mir, 'tcx, M: Machine<'mir, 'tcx>> {
89     /// Allocations local to this instance of the miri engine. The kind
90     /// helps ensure that the same mechanism is used for allocation and
91     /// deallocation. When an allocation is not found here, it is a
92     /// global and looked up in the `tcx` for read access. Some machines may
93     /// have to mutate this map even on a read-only access to a global (because
94     /// they do pointer provenance tracking and the allocations in `tcx` have
95     /// the wrong type), so we let the machine override this type.
96     /// Either way, if the machine allows writing to a global, doing so will
97     /// create a copy of the global allocation here.
98     // FIXME: this should not be public, but interning currently needs access to it
99     pub(super) alloc_map: M::MemoryMap,
100
101     /// Map for "extra" function pointers.
102     extra_fn_ptr_map: FxHashMap<AllocId, M::ExtraFnVal>,
103
104     /// To be able to compare pointers with null, and to check alignment for accesses
105     /// to ZSTs (where pointers may dangle), we keep track of the size even for allocations
106     /// that do not exist any more.
107     // FIXME: this should not be public, but interning currently needs access to it
108     pub(super) dead_alloc_map: FxHashMap<AllocId, (Size, Align)>,
109
110     /// Extra data added by the machine.
111     pub extra: M::MemoryExtra,
112
113     /// Lets us implement `HasDataLayout`, which is awfully convenient.
114     pub tcx: TyCtxt<'tcx>,
115 }
116
117 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for Memory<'mir, 'tcx, M> {
118     #[inline]
119     fn data_layout(&self) -> &TargetDataLayout {
120         &self.tcx.data_layout
121     }
122 }
123
124 /// A reference to some allocation that was already bounds-checked for the given region
125 /// and had the on-access machine hooks run.
126 #[derive(Copy, Clone)]
127 pub struct AllocRef<'a, 'tcx, Tag, Extra> {
128     alloc: &'a Allocation<Tag, Extra>,
129     range: AllocRange,
130     tcx: TyCtxt<'tcx>,
131     alloc_id: AllocId,
132 }
133 /// A reference to some allocation that was already bounds-checked for the given region
134 /// and had the on-access machine hooks run.
135 pub struct AllocRefMut<'a, 'tcx, Tag, Extra> {
136     alloc: &'a mut Allocation<Tag, Extra>,
137     range: AllocRange,
138     tcx: TyCtxt<'tcx>,
139     alloc_id: AllocId,
140 }
141
142 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
143     pub fn new(tcx: TyCtxt<'tcx>, extra: M::MemoryExtra) -> Self {
144         Memory {
145             alloc_map: M::MemoryMap::default(),
146             extra_fn_ptr_map: FxHashMap::default(),
147             dead_alloc_map: FxHashMap::default(),
148             extra,
149             tcx,
150         }
151     }
152
153     /// Call this to turn untagged "global" pointers (obtained via `tcx`) into
154     /// the machine pointer to the allocation.  Must never be used
155     /// for any other pointers, nor for TLS statics.
156     ///
157     /// Using the resulting pointer represents a *direct* access to that memory
158     /// (e.g. by directly using a `static`),
159     /// as opposed to access through a pointer that was created by the program.
160     ///
161     /// This function can fail only if `ptr` points to an `extern static`.
162     #[inline]
163     pub fn global_base_pointer(
164         &self,
165         mut ptr: Pointer,
166     ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
167         // We need to handle `extern static`.
168         let ptr = match self.tcx.get_global_alloc(ptr.alloc_id) {
169             Some(GlobalAlloc::Static(def_id)) if self.tcx.is_thread_local_static(def_id) => {
170                 bug!("global memory cannot point to thread-local static")
171             }
172             Some(GlobalAlloc::Static(def_id)) if self.tcx.is_foreign_item(def_id) => {
173                 ptr.alloc_id = M::extern_static_alloc_id(self, def_id)?;
174                 ptr
175             }
176             _ => {
177                 // No need to change the `AllocId`.
178                 ptr
179             }
180         };
181         // And we need to get the tag.
182         let tag = M::tag_global_base_pointer(&self.extra, ptr.alloc_id);
183         Ok(ptr.with_tag(tag))
184     }
185
186     pub fn create_fn_alloc(
187         &mut self,
188         fn_val: FnVal<'tcx, M::ExtraFnVal>,
189     ) -> Pointer<M::PointerTag> {
190         let id = match fn_val {
191             FnVal::Instance(instance) => self.tcx.create_fn_alloc(instance),
192             FnVal::Other(extra) => {
193                 // FIXME(RalfJung): Should we have a cache here?
194                 let id = self.tcx.reserve_alloc_id();
195                 let old = self.extra_fn_ptr_map.insert(id, extra);
196                 assert!(old.is_none());
197                 id
198             }
199         };
200         // Functions are global allocations, so make sure we get the right base pointer.
201         // We know this is not an `extern static` so this cannot fail.
202         self.global_base_pointer(Pointer::from(id)).unwrap()
203     }
204
205     pub fn allocate(
206         &mut self,
207         size: Size,
208         align: Align,
209         kind: MemoryKind<M::MemoryKind>,
210     ) -> InterpResult<'static, Pointer<M::PointerTag>> {
211         let alloc = Allocation::uninit(size, align, M::PANIC_ON_ALLOC_FAIL)?;
212         Ok(self.allocate_with(alloc, kind))
213     }
214
215     pub fn allocate_bytes(
216         &mut self,
217         bytes: &[u8],
218         align: Align,
219         kind: MemoryKind<M::MemoryKind>,
220         mutability: Mutability,
221     ) -> Pointer<M::PointerTag> {
222         let alloc = Allocation::from_bytes(bytes, align, mutability);
223         self.allocate_with(alloc, kind)
224     }
225
226     pub fn allocate_with(
227         &mut self,
228         alloc: Allocation,
229         kind: MemoryKind<M::MemoryKind>,
230     ) -> Pointer<M::PointerTag> {
231         let id = self.tcx.reserve_alloc_id();
232         debug_assert_ne!(
233             Some(kind),
234             M::GLOBAL_KIND.map(MemoryKind::Machine),
235             "dynamically allocating global memory"
236         );
237         // This is a new allocation, not a new global one, so no `global_base_ptr`.
238         let (alloc, tag) = M::init_allocation_extra(&self.extra, id, Cow::Owned(alloc), Some(kind));
239         self.alloc_map.insert(id, (kind, alloc.into_owned()));
240         Pointer::from(id).with_tag(tag)
241     }
242
243     pub fn reallocate(
244         &mut self,
245         ptr: Pointer<M::PointerTag>,
246         old_size_and_align: Option<(Size, Align)>,
247         new_size: Size,
248         new_align: Align,
249         kind: MemoryKind<M::MemoryKind>,
250     ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
251         if ptr.offset.bytes() != 0 {
252             throw_ub_format!(
253                 "reallocating {:?} which does not point to the beginning of an object",
254                 ptr
255             );
256         }
257
258         // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc".
259         // This happens so rarely, the perf advantage is outweighed by the maintenance cost.
260         let new_ptr = self.allocate(new_size, new_align, kind)?;
261         let old_size = match old_size_and_align {
262             Some((size, _align)) => size,
263             None => self.get_raw(ptr.alloc_id)?.size(),
264         };
265         // This will also call the access hooks.
266         self.copy(
267             ptr.into(),
268             Align::ONE,
269             new_ptr.into(),
270             Align::ONE,
271             old_size.min(new_size),
272             /*nonoverlapping*/ true,
273         )?;
274         self.deallocate(ptr, old_size_and_align, kind)?;
275
276         Ok(new_ptr)
277     }
278
279     pub fn deallocate(
280         &mut self,
281         ptr: Pointer<M::PointerTag>,
282         old_size_and_align: Option<(Size, Align)>,
283         kind: MemoryKind<M::MemoryKind>,
284     ) -> InterpResult<'tcx> {
285         trace!("deallocating: {}", ptr.alloc_id);
286
287         if ptr.offset.bytes() != 0 {
288             throw_ub_format!(
289                 "deallocating {:?} which does not point to the beginning of an object",
290                 ptr
291             );
292         }
293
294         let (alloc_kind, mut alloc) = match self.alloc_map.remove(&ptr.alloc_id) {
295             Some(alloc) => alloc,
296             None => {
297                 // Deallocating global memory -- always an error
298                 return Err(match self.tcx.get_global_alloc(ptr.alloc_id) {
299                     Some(GlobalAlloc::Function(..)) => {
300                         err_ub_format!("deallocating {}, which is a function", ptr.alloc_id)
301                     }
302                     Some(GlobalAlloc::Static(..) | GlobalAlloc::Memory(..)) => {
303                         err_ub_format!("deallocating {}, which is static memory", ptr.alloc_id)
304                     }
305                     None => err_ub!(PointerUseAfterFree(ptr.alloc_id)),
306                 }
307                 .into());
308             }
309         };
310
311         if alloc.mutability == Mutability::Not {
312             throw_ub_format!("deallocating immutable allocation {}", ptr.alloc_id);
313         }
314         if alloc_kind != kind {
315             throw_ub_format!(
316                 "deallocating {}, which is {} memory, using {} deallocation operation",
317                 ptr.alloc_id,
318                 alloc_kind,
319                 kind
320             );
321         }
322         if let Some((size, align)) = old_size_and_align {
323             if size != alloc.size() || align != alloc.align {
324                 throw_ub_format!(
325                     "incorrect layout on deallocation: {} has size {} and alignment {}, but gave size {} and alignment {}",
326                     ptr.alloc_id,
327                     alloc.size().bytes(),
328                     alloc.align.bytes(),
329                     size.bytes(),
330                     align.bytes(),
331                 )
332             }
333         }
334
335         // Let the machine take some extra action
336         let size = alloc.size();
337         M::memory_deallocated(&mut self.extra, &mut alloc.extra, ptr, size)?;
338
339         // Don't forget to remember size and align of this now-dead allocation
340         let old = self.dead_alloc_map.insert(ptr.alloc_id, (size, alloc.align));
341         if old.is_some() {
342             bug!("Nothing can be deallocated twice");
343         }
344
345         Ok(())
346     }
347
348     /// Internal helper function for APIs that offer memory access based on `Scalar` pointers.
349     #[inline(always)]
350     pub(super) fn check_ptr_access(
351         &self,
352         sptr: Scalar<M::PointerTag>,
353         size: Size,
354         align: Align,
355     ) -> InterpResult<'tcx, Option<Pointer<M::PointerTag>>> {
356         let align = M::enforce_alignment(&self.extra).then_some(align);
357         self.check_and_deref_ptr(sptr, size, align, CheckInAllocMsg::MemoryAccessTest, |ptr| {
358             let (size, align) =
359                 self.get_size_and_align(ptr.alloc_id, AllocCheck::Dereferenceable)?;
360             Ok((size, align, ptr))
361         })
362     }
363
364     /// Check if the given scalar is allowed to do a memory access of given `size` and `align`
365     /// (ignoring `M::enforce_alignment`). The caller can control the error message for the
366     /// out-of-bounds case.
367     #[inline(always)]
368     pub fn check_ptr_access_align(
369         &self,
370         sptr: Scalar<M::PointerTag>,
371         size: Size,
372         align: Align,
373         msg: CheckInAllocMsg,
374     ) -> InterpResult<'tcx> {
375         self.check_and_deref_ptr(sptr, size, Some(align), msg, |ptr| {
376             let (size, align) =
377                 self.get_size_and_align(ptr.alloc_id, AllocCheck::Dereferenceable)?;
378             Ok((size, align, ()))
379         })?;
380         Ok(())
381     }
382
383     /// Low-level helper function to check if a ptr is in-bounds and potentially return a reference
384     /// to the allocation it points to. Supports both shared and mutable references, to the actual
385     /// checking is offloaded to a helper closure. `align` defines whether and which alignment check
386     /// is done. Returns `None` for size 0, and otherwise `Some` of what `alloc_size` returned.
387     fn check_and_deref_ptr<T>(
388         &self,
389         sptr: Scalar<M::PointerTag>,
390         size: Size,
391         align: Option<Align>,
392         msg: CheckInAllocMsg,
393         alloc_size: impl FnOnce(Pointer<M::PointerTag>) -> InterpResult<'tcx, (Size, Align, T)>,
394     ) -> InterpResult<'tcx, Option<T>> {
395         fn check_offset_align(offset: u64, align: Align) -> InterpResult<'static> {
396             if offset % align.bytes() == 0 {
397                 Ok(())
398             } else {
399                 // The biggest power of two through which `offset` is divisible.
400                 let offset_pow2 = 1 << offset.trailing_zeros();
401                 throw_ub!(AlignmentCheckFailed {
402                     has: Align::from_bytes(offset_pow2).unwrap(),
403                     required: align,
404                 })
405             }
406         }
407
408         // Normalize to a `Pointer` if we definitely need one.
409         let normalized = if size.bytes() == 0 {
410             // Can be an integer, just take what we got.  We do NOT `force_bits` here;
411             // if this is already a `Pointer` we want to do the bounds checks!
412             sptr
413         } else {
414             // A "real" access, we must get a pointer to be able to check the bounds.
415             Scalar::from(self.force_ptr(sptr)?)
416         };
417         Ok(match normalized.to_bits_or_ptr(self.pointer_size(), self) {
418             Ok(bits) => {
419                 let bits = u64::try_from(bits).unwrap(); // it's ptr-sized
420                 assert!(size.bytes() == 0);
421                 // Must be non-null.
422                 if bits == 0 {
423                     throw_ub!(DanglingIntPointer(0, msg))
424                 }
425                 // Must be aligned.
426                 if let Some(align) = align {
427                     check_offset_align(bits, align)?;
428                 }
429                 None
430             }
431             Err(ptr) => {
432                 let (allocation_size, alloc_align, ret_val) = alloc_size(ptr)?;
433                 // Test bounds. This also ensures non-null.
434                 // It is sufficient to check this for the end pointer. The addition
435                 // checks for overflow.
436                 let end_ptr = ptr.offset(size, self)?;
437                 if end_ptr.offset > allocation_size {
438                     // equal is okay!
439                     throw_ub!(PointerOutOfBounds { ptr: end_ptr.erase_tag(), msg, allocation_size })
440                 }
441                 // Test align. Check this last; if both bounds and alignment are violated
442                 // we want the error to be about the bounds.
443                 if let Some(align) = align {
444                     if M::force_int_for_alignment_check(&self.extra) {
445                         let bits = self
446                             .force_bits(ptr.into(), self.pointer_size())
447                             .expect("ptr-to-int cast for align check should never fail");
448                         check_offset_align(bits.try_into().unwrap(), align)?;
449                     } else {
450                         // Check allocation alignment and offset alignment.
451                         if alloc_align.bytes() < align.bytes() {
452                             throw_ub!(AlignmentCheckFailed { has: alloc_align, required: align });
453                         }
454                         check_offset_align(ptr.offset.bytes(), align)?;
455                     }
456                 }
457
458                 // We can still be zero-sized in this branch, in which case we have to
459                 // return `None`.
460                 if size.bytes() == 0 { None } else { Some(ret_val) }
461             }
462         })
463     }
464
465     /// Test if the pointer might be null.
466     pub fn ptr_may_be_null(&self, ptr: Pointer<M::PointerTag>) -> bool {
467         let (size, _align) = self
468             .get_size_and_align(ptr.alloc_id, AllocCheck::MaybeDead)
469             .expect("alloc info with MaybeDead cannot fail");
470         // If the pointer is out-of-bounds, it may be null.
471         // Note that one-past-the-end (offset == size) is still inbounds, and never null.
472         ptr.offset > size
473     }
474 }
475
476 /// Allocation accessors
477 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
478     /// Helper function to obtain a global (tcx) allocation.
479     /// This attempts to return a reference to an existing allocation if
480     /// one can be found in `tcx`. That, however, is only possible if `tcx` and
481     /// this machine use the same pointer tag, so it is indirected through
482     /// `M::tag_allocation`.
483     fn get_global_alloc(
484         memory_extra: &M::MemoryExtra,
485         tcx: TyCtxt<'tcx>,
486         id: AllocId,
487         is_write: bool,
488     ) -> InterpResult<'tcx, Cow<'tcx, Allocation<M::PointerTag, M::AllocExtra>>> {
489         let (alloc, def_id) = match tcx.get_global_alloc(id) {
490             Some(GlobalAlloc::Memory(mem)) => {
491                 // Memory of a constant or promoted or anonymous memory referenced by a static.
492                 (mem, None)
493             }
494             Some(GlobalAlloc::Function(..)) => throw_ub!(DerefFunctionPointer(id)),
495             None => throw_ub!(PointerUseAfterFree(id)),
496             Some(GlobalAlloc::Static(def_id)) => {
497                 assert!(tcx.is_static(def_id));
498                 assert!(!tcx.is_thread_local_static(def_id));
499                 // Notice that every static has two `AllocId` that will resolve to the same
500                 // thing here: one maps to `GlobalAlloc::Static`, this is the "lazy" ID,
501                 // and the other one is maps to `GlobalAlloc::Memory`, this is returned by
502                 // `eval_static_initializer` and it is the "resolved" ID.
503                 // The resolved ID is never used by the interpreted program, it is hidden.
504                 // This is relied upon for soundness of const-patterns; a pointer to the resolved
505                 // ID would "sidestep" the checks that make sure consts do not point to statics!
506                 // The `GlobalAlloc::Memory` branch here is still reachable though; when a static
507                 // contains a reference to memory that was created during its evaluation (i.e., not
508                 // to another static), those inner references only exist in "resolved" form.
509                 if tcx.is_foreign_item(def_id) {
510                     throw_unsup!(ReadExternStatic(def_id));
511                 }
512
513                 (tcx.eval_static_initializer(def_id)?, Some(def_id))
514             }
515         };
516         M::before_access_global(memory_extra, id, alloc, def_id, is_write)?;
517         let alloc = Cow::Borrowed(alloc);
518         // We got tcx memory. Let the machine initialize its "extra" stuff.
519         let (alloc, tag) = M::init_allocation_extra(
520             memory_extra,
521             id, // always use the ID we got as input, not the "hidden" one.
522             alloc,
523             M::GLOBAL_KIND.map(MemoryKind::Machine),
524         );
525         // Sanity check that this is the same pointer we would have gotten via `global_base_pointer`.
526         debug_assert_eq!(tag, M::tag_global_base_pointer(memory_extra, id));
527         Ok(alloc)
528     }
529
530     /// Gives raw access to the `Allocation`, without bounds or alignment checks.
531     /// The caller is responsible for calling the access hooks!
532     fn get_raw(
533         &self,
534         id: AllocId,
535     ) -> InterpResult<'tcx, &Allocation<M::PointerTag, M::AllocExtra>> {
536         // The error type of the inner closure here is somewhat funny.  We have two
537         // ways of "erroring": An actual error, or because we got a reference from
538         // `get_global_alloc` that we can actually use directly without inserting anything anywhere.
539         // So the error type is `InterpResult<'tcx, &Allocation<M::PointerTag>>`.
540         let a = self.alloc_map.get_or(id, || {
541             let alloc = Self::get_global_alloc(&self.extra, self.tcx, id, /*is_write*/ false)
542                 .map_err(Err)?;
543             match alloc {
544                 Cow::Borrowed(alloc) => {
545                     // We got a ref, cheaply return that as an "error" so that the
546                     // map does not get mutated.
547                     Err(Ok(alloc))
548                 }
549                 Cow::Owned(alloc) => {
550                     // Need to put it into the map and return a ref to that
551                     let kind = M::GLOBAL_KIND.expect(
552                         "I got a global allocation that I have to copy but the machine does \
553                             not expect that to happen",
554                     );
555                     Ok((MemoryKind::Machine(kind), alloc))
556                 }
557             }
558         });
559         // Now unpack that funny error type
560         match a {
561             Ok(a) => Ok(&a.1),
562             Err(a) => a,
563         }
564     }
565
566     /// "Safe" (bounds and align-checked) allocation access.
567     pub fn get<'a>(
568         &'a self,
569         sptr: Scalar<M::PointerTag>,
570         size: Size,
571         align: Align,
572     ) -> InterpResult<'tcx, Option<AllocRef<'a, 'tcx, M::PointerTag, M::AllocExtra>>> {
573         let align = M::enforce_alignment(&self.extra).then_some(align);
574         let ptr_and_alloc = self.check_and_deref_ptr(
575             sptr,
576             size,
577             align,
578             CheckInAllocMsg::MemoryAccessTest,
579             |ptr| {
580                 let alloc = self.get_raw(ptr.alloc_id)?;
581                 Ok((alloc.size(), alloc.align, (ptr, alloc)))
582             },
583         )?;
584         if let Some((ptr, alloc)) = ptr_and_alloc {
585             M::memory_read(&self.extra, &alloc.extra, ptr, size)?;
586             let range = alloc_range(ptr.offset, size);
587             Ok(Some(AllocRef { alloc, range, tcx: self.tcx, alloc_id: ptr.alloc_id }))
588         } else {
589             // Even in this branch we have to be sure that we actually access the allocation, in
590             // order to ensure that `static FOO: Type = FOO;` causes a cycle error instead of
591             // magically pulling *any* ZST value from the ether. However, the `get_raw` above is
592             // always called when `sptr` is truly a `Pointer`, so we are good.
593             Ok(None)
594         }
595     }
596
597     /// Return the `extra` field of the given allocation.
598     pub fn get_alloc_extra<'a>(&'a self, id: AllocId) -> InterpResult<'tcx, &'a M::AllocExtra> {
599         Ok(&self.get_raw(id)?.extra)
600     }
601
602     /// Gives raw mutable access to the `Allocation`, without bounds or alignment checks.
603     /// The caller is responsible for calling the access hooks!
604     ///
605     /// Also returns a ptr to `self.extra` so that the caller can use it in parallel with the
606     /// allocation.
607     fn get_raw_mut(
608         &mut self,
609         id: AllocId,
610     ) -> InterpResult<'tcx, (&mut Allocation<M::PointerTag, M::AllocExtra>, &mut M::MemoryExtra)>
611     {
612         let tcx = self.tcx;
613         let memory_extra = &mut self.extra;
614         let a = self.alloc_map.get_mut_or(id, || {
615             // Need to make a copy, even if `get_global_alloc` is able
616             // to give us a cheap reference.
617             let alloc = Self::get_global_alloc(memory_extra, tcx, id, /*is_write*/ true)?;
618             let kind = M::GLOBAL_KIND.expect(
619                 "I got a global allocation that I have to copy but the machine does \
620                     not expect that to happen",
621             );
622             Ok((MemoryKind::Machine(kind), alloc.into_owned()))
623         });
624         // Unpack the error type manually because type inference doesn't
625         // work otherwise (and we cannot help it because `impl Trait`)
626         match a {
627             Err(e) => Err(e),
628             Ok(a) => {
629                 let a = &mut a.1;
630                 if a.mutability == Mutability::Not {
631                     throw_ub!(WriteToReadOnly(id))
632                 }
633                 Ok((a, memory_extra))
634             }
635         }
636     }
637
638     /// "Safe" (bounds and align-checked) allocation access.
639     pub fn get_mut<'a>(
640         &'a mut self,
641         sptr: Scalar<M::PointerTag>,
642         size: Size,
643         align: Align,
644     ) -> InterpResult<'tcx, Option<AllocRefMut<'a, 'tcx, M::PointerTag, M::AllocExtra>>> {
645         let ptr = self.check_ptr_access(sptr, size, align)?;
646         if let Some(ptr) = ptr {
647             let tcx = self.tcx;
648             // FIXME: can we somehow avoid looking up the allocation twice here?
649             // We cannot call `get_raw_mut` inside `check_and_deref_ptr` as that would duplicate `&mut self`.
650             let (alloc, extra) = self.get_raw_mut(ptr.alloc_id)?;
651             M::memory_written(extra, &mut alloc.extra, ptr, size)?;
652             let range = alloc_range(ptr.offset, size);
653             Ok(Some(AllocRefMut { alloc, range, tcx, alloc_id: ptr.alloc_id }))
654         } else {
655             Ok(None)
656         }
657     }
658
659     /// Return the `extra` field of the given allocation.
660     pub fn get_alloc_extra_mut<'a>(
661         &'a mut self,
662         id: AllocId,
663     ) -> InterpResult<'tcx, (&'a mut M::AllocExtra, &'a mut M::MemoryExtra)> {
664         let (alloc, memory_extra) = self.get_raw_mut(id)?;
665         Ok((&mut alloc.extra, memory_extra))
666     }
667
668     /// Obtain the size and alignment of an allocation, even if that allocation has
669     /// been deallocated.
670     ///
671     /// If `liveness` is `AllocCheck::MaybeDead`, this function always returns `Ok`.
672     pub fn get_size_and_align(
673         &self,
674         id: AllocId,
675         liveness: AllocCheck,
676     ) -> InterpResult<'static, (Size, Align)> {
677         // # Regular allocations
678         // Don't use `self.get_raw` here as that will
679         // a) cause cycles in case `id` refers to a static
680         // b) duplicate a global's allocation in miri
681         if let Some((_, alloc)) = self.alloc_map.get(id) {
682             return Ok((alloc.size(), alloc.align));
683         }
684
685         // # Function pointers
686         // (both global from `alloc_map` and local from `extra_fn_ptr_map`)
687         if self.get_fn_alloc(id).is_some() {
688             return if let AllocCheck::Dereferenceable = liveness {
689                 // The caller requested no function pointers.
690                 throw_ub!(DerefFunctionPointer(id))
691             } else {
692                 Ok((Size::ZERO, Align::ONE))
693             };
694         }
695
696         // # Statics
697         // Can't do this in the match argument, we may get cycle errors since the lock would
698         // be held throughout the match.
699         match self.tcx.get_global_alloc(id) {
700             Some(GlobalAlloc::Static(did)) => {
701                 assert!(!self.tcx.is_thread_local_static(did));
702                 // Use size and align of the type.
703                 let ty = self.tcx.type_of(did);
704                 let layout = self.tcx.layout_of(ParamEnv::empty().and(ty)).unwrap();
705                 Ok((layout.size, layout.align.abi))
706             }
707             Some(GlobalAlloc::Memory(alloc)) => {
708                 // Need to duplicate the logic here, because the global allocations have
709                 // different associated types than the interpreter-local ones.
710                 Ok((alloc.size(), alloc.align))
711             }
712             Some(GlobalAlloc::Function(_)) => bug!("We already checked function pointers above"),
713             // The rest must be dead.
714             None => {
715                 if let AllocCheck::MaybeDead = liveness {
716                     // Deallocated pointers are allowed, we should be able to find
717                     // them in the map.
718                     Ok(*self
719                         .dead_alloc_map
720                         .get(&id)
721                         .expect("deallocated pointers should all be recorded in `dead_alloc_map`"))
722                 } else {
723                     throw_ub!(PointerUseAfterFree(id))
724                 }
725             }
726         }
727     }
728
729     fn get_fn_alloc(&self, id: AllocId) -> Option<FnVal<'tcx, M::ExtraFnVal>> {
730         trace!("reading fn ptr: {}", id);
731         if let Some(extra) = self.extra_fn_ptr_map.get(&id) {
732             Some(FnVal::Other(*extra))
733         } else {
734             match self.tcx.get_global_alloc(id) {
735                 Some(GlobalAlloc::Function(instance)) => Some(FnVal::Instance(instance)),
736                 _ => None,
737             }
738         }
739     }
740
741     pub fn get_fn(
742         &self,
743         ptr: Scalar<M::PointerTag>,
744     ) -> InterpResult<'tcx, FnVal<'tcx, M::ExtraFnVal>> {
745         let ptr = self.force_ptr(ptr)?; // We definitely need a pointer value.
746         if ptr.offset.bytes() != 0 {
747             throw_ub!(InvalidFunctionPointer(ptr.erase_tag()))
748         }
749         self.get_fn_alloc(ptr.alloc_id)
750             .ok_or_else(|| err_ub!(InvalidFunctionPointer(ptr.erase_tag())).into())
751     }
752
753     pub fn mark_immutable(&mut self, id: AllocId) -> InterpResult<'tcx> {
754         self.get_raw_mut(id)?.0.mutability = Mutability::Not;
755         Ok(())
756     }
757
758     /// Create a lazy debug printer that prints the given allocation and all allocations it points
759     /// to, recursively.
760     #[must_use]
761     pub fn dump_alloc<'a>(&'a self, id: AllocId) -> DumpAllocs<'a, 'mir, 'tcx, M> {
762         self.dump_allocs(vec![id])
763     }
764
765     /// Create a lazy debug printer for a list of allocations and all allocations they point to,
766     /// recursively.
767     #[must_use]
768     pub fn dump_allocs<'a>(&'a self, mut allocs: Vec<AllocId>) -> DumpAllocs<'a, 'mir, 'tcx, M> {
769         allocs.sort();
770         allocs.dedup();
771         DumpAllocs { mem: self, allocs }
772     }
773
774     /// Print leaked memory. Allocations reachable from `static_roots` or a `Global` allocation
775     /// are not considered leaked. Leaks whose kind `may_leak()` returns true are not reported.
776     pub fn leak_report(&self, static_roots: &[AllocId]) -> usize {
777         // Collect the set of allocations that are *reachable* from `Global` allocations.
778         let reachable = {
779             let mut reachable = FxHashSet::default();
780             let global_kind = M::GLOBAL_KIND.map(MemoryKind::Machine);
781             let mut todo: Vec<_> = self.alloc_map.filter_map_collect(move |&id, &(kind, _)| {
782                 if Some(kind) == global_kind { Some(id) } else { None }
783             });
784             todo.extend(static_roots);
785             while let Some(id) = todo.pop() {
786                 if reachable.insert(id) {
787                     // This is a new allocation, add its relocations to `todo`.
788                     if let Some((_, alloc)) = self.alloc_map.get(id) {
789                         todo.extend(alloc.relocations().values().map(|&(_, target_id)| target_id));
790                     }
791                 }
792             }
793             reachable
794         };
795
796         // All allocations that are *not* `reachable` and *not* `may_leak` are considered leaking.
797         let leaks: Vec<_> = self.alloc_map.filter_map_collect(|&id, &(kind, _)| {
798             if kind.may_leak() || reachable.contains(&id) { None } else { Some(id) }
799         });
800         let n = leaks.len();
801         if n > 0 {
802             eprintln!("The following memory was leaked: {:?}", self.dump_allocs(leaks));
803         }
804         n
805     }
806
807     /// This is used by [priroda](https://github.com/oli-obk/priroda)
808     pub fn alloc_map(&self) -> &M::MemoryMap {
809         &self.alloc_map
810     }
811 }
812
813 #[doc(hidden)]
814 /// There's no way to use this directly, it's just a helper struct for the `dump_alloc(s)` methods.
815 pub struct DumpAllocs<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> {
816     mem: &'a Memory<'mir, 'tcx, M>,
817     allocs: Vec<AllocId>,
818 }
819
820 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> std::fmt::Debug for DumpAllocs<'a, 'mir, 'tcx, M> {
821     fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
822         // Cannot be a closure because it is generic in `Tag`, `Extra`.
823         fn write_allocation_track_relocs<'tcx, Tag: Copy + fmt::Debug, Extra>(
824             fmt: &mut std::fmt::Formatter<'_>,
825             tcx: TyCtxt<'tcx>,
826             allocs_to_print: &mut VecDeque<AllocId>,
827             alloc: &Allocation<Tag, Extra>,
828         ) -> std::fmt::Result {
829             for &(_, target_id) in alloc.relocations().values() {
830                 allocs_to_print.push_back(target_id);
831             }
832             write!(fmt, "{}", pretty::display_allocation(tcx, alloc))
833         }
834
835         let mut allocs_to_print: VecDeque<_> = self.allocs.iter().copied().collect();
836         // `allocs_printed` contains all allocations that we have already printed.
837         let mut allocs_printed = FxHashSet::default();
838
839         while let Some(id) = allocs_to_print.pop_front() {
840             if !allocs_printed.insert(id) {
841                 // Already printed, so skip this.
842                 continue;
843             }
844
845             write!(fmt, "{}", id)?;
846             match self.mem.alloc_map.get(id) {
847                 Some(&(kind, ref alloc)) => {
848                     // normal alloc
849                     write!(fmt, " ({}, ", kind)?;
850                     write_allocation_track_relocs(
851                         &mut *fmt,
852                         self.mem.tcx,
853                         &mut allocs_to_print,
854                         alloc,
855                     )?;
856                 }
857                 None => {
858                     // global alloc
859                     match self.mem.tcx.get_global_alloc(id) {
860                         Some(GlobalAlloc::Memory(alloc)) => {
861                             write!(fmt, " (unchanged global, ")?;
862                             write_allocation_track_relocs(
863                                 &mut *fmt,
864                                 self.mem.tcx,
865                                 &mut allocs_to_print,
866                                 alloc,
867                             )?;
868                         }
869                         Some(GlobalAlloc::Function(func)) => {
870                             write!(fmt, " (fn: {})", func)?;
871                         }
872                         Some(GlobalAlloc::Static(did)) => {
873                             write!(fmt, " (static: {})", self.mem.tcx.def_path_str(did))?;
874                         }
875                         None => {
876                             write!(fmt, " (deallocated)")?;
877                         }
878                     }
879                 }
880             }
881             writeln!(fmt)?;
882         }
883         Ok(())
884     }
885 }
886
887 /// Reading and writing.
888 impl<'tcx, 'a, Tag: Copy, Extra> AllocRefMut<'a, 'tcx, Tag, Extra> {
889     pub fn write_scalar(
890         &mut self,
891         range: AllocRange,
892         val: ScalarMaybeUninit<Tag>,
893     ) -> InterpResult<'tcx> {
894         Ok(self
895             .alloc
896             .write_scalar(&self.tcx, self.range.subrange(range), val)
897             .map_err(|e| e.to_interp_error(self.alloc_id))?)
898     }
899
900     pub fn write_ptr_sized(
901         &mut self,
902         offset: Size,
903         val: ScalarMaybeUninit<Tag>,
904     ) -> InterpResult<'tcx> {
905         self.write_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size), val)
906     }
907 }
908
909 impl<'tcx, 'a, Tag: Copy, Extra> AllocRef<'a, 'tcx, Tag, Extra> {
910     pub fn read_scalar(&self, range: AllocRange) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
911         Ok(self
912             .alloc
913             .read_scalar(&self.tcx, self.range.subrange(range))
914             .map_err(|e| e.to_interp_error(self.alloc_id))?)
915     }
916
917     pub fn read_ptr_sized(&self, offset: Size) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
918         self.read_scalar(alloc_range(offset, self.tcx.data_layout().pointer_size))
919     }
920
921     pub fn check_bytes(&self, range: AllocRange, allow_uninit_and_ptr: bool) -> InterpResult<'tcx> {
922         Ok(self
923             .alloc
924             .check_bytes(&self.tcx, self.range.subrange(range), allow_uninit_and_ptr)
925             .map_err(|e| e.to_interp_error(self.alloc_id))?)
926     }
927 }
928
929 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
930     /// Reads the given number of bytes from memory. Returns them as a slice.
931     ///
932     /// Performs appropriate bounds checks.
933     pub fn read_bytes(&self, sptr: Scalar<M::PointerTag>, size: Size) -> InterpResult<'tcx, &[u8]> {
934         let alloc_ref = match self.get(sptr, size, Align::ONE)? {
935             Some(a) => a,
936             None => return Ok(&[]), // zero-sized access
937         };
938         // Side-step AllocRef and directly access the underlying bytes more efficiently.
939         // (We are staying inside the bounds here so all is good.)
940         Ok(alloc_ref
941             .alloc
942             .get_bytes(&alloc_ref.tcx, alloc_ref.range)
943             .map_err(|e| e.to_interp_error(alloc_ref.alloc_id))?)
944     }
945
946     /// Writes the given stream of bytes into memory.
947     ///
948     /// Performs appropriate bounds checks.
949     pub fn write_bytes(
950         &mut self,
951         sptr: Scalar<M::PointerTag>,
952         src: impl IntoIterator<Item = u8>,
953     ) -> InterpResult<'tcx> {
954         let mut src = src.into_iter();
955         let (lower, upper) = src.size_hint();
956         let len = upper.expect("can only write bounded iterators");
957         assert_eq!(lower, len, "can only write iterators with a precise length");
958
959         let size = Size::from_bytes(len);
960         let alloc_ref = match self.get_mut(sptr, size, Align::ONE)? {
961             Some(alloc_ref) => alloc_ref,
962             None => {
963                 // zero-sized access
964                 assert_matches!(
965                     src.next(),
966                     None,
967                     "iterator said it was empty but returned an element"
968                 );
969                 return Ok(());
970             }
971         };
972
973         // Side-step AllocRef and directly access the underlying bytes more efficiently.
974         // (We are staying inside the bounds here so all is good.)
975         let bytes = alloc_ref.alloc.get_bytes_mut(&alloc_ref.tcx, alloc_ref.range);
976         // `zip` would stop when the first iterator ends; we want to definitely
977         // cover all of `bytes`.
978         for dest in bytes {
979             *dest = src.next().expect("iterator was shorter than it said it would be");
980         }
981         assert_matches!(src.next(), None, "iterator was longer than it said it would be");
982         Ok(())
983     }
984
985     pub fn copy(
986         &mut self,
987         src: Scalar<M::PointerTag>,
988         src_align: Align,
989         dest: Scalar<M::PointerTag>,
990         dest_align: Align,
991         size: Size,
992         nonoverlapping: bool,
993     ) -> InterpResult<'tcx> {
994         self.copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
995     }
996
997     pub fn copy_repeatedly(
998         &mut self,
999         src: Scalar<M::PointerTag>,
1000         src_align: Align,
1001         dest: Scalar<M::PointerTag>,
1002         dest_align: Align,
1003         size: Size,
1004         num_copies: u64,
1005         nonoverlapping: bool,
1006     ) -> InterpResult<'tcx> {
1007         let tcx = self.tcx;
1008         // We need to do our own bounds-checks.
1009         let src = self.check_ptr_access(src, size, src_align)?;
1010         let dest = self.check_ptr_access(dest, size * num_copies, dest_align)?; // `Size` multiplication
1011
1012         // FIXME: we look up both allocations twice here, once ebfore for the `check_ptr_access`
1013         // and once below to get the underlying `&[mut] Allocation`.
1014
1015         // Source alloc preparations and access hooks.
1016         let src = match src {
1017             None => return Ok(()), // Zero-sized *source*, that means dst is also zero-sized and we have nothing to do.
1018             Some(src_ptr) => src_ptr,
1019         };
1020         let src_alloc = self.get_raw(src.alloc_id)?;
1021         M::memory_read(&self.extra, &src_alloc.extra, src, size)?;
1022         // We need the `dest` ptr for the next operation, so we get it now.
1023         // We already did the source checks and called the hooks so we are good to return early.
1024         let dest = match dest {
1025             None => return Ok(()), // Zero-sized *destiantion*.
1026             Some(dest_ptr) => dest_ptr,
1027         };
1028
1029         // first copy the relocations to a temporary buffer, because
1030         // `get_bytes_mut` will clear the relocations, which is correct,
1031         // since we don't want to keep any relocations at the target.
1032         // (`get_bytes_with_uninit_and_ptr` below checks that there are no
1033         // relocations overlapping the edges; those would not be handled correctly).
1034         let relocations = src_alloc.prepare_relocation_copy(
1035             self,
1036             alloc_range(src.offset, size),
1037             dest.offset,
1038             num_copies,
1039         );
1040         // Prepare a copy of the initialization mask.
1041         let compressed = src_alloc.compress_uninit_range(alloc_range(src.offset, size));
1042         // This checks relocation edges on the src.
1043         let src_bytes = src_alloc
1044             .get_bytes_with_uninit_and_ptr(&tcx, alloc_range(src.offset, size))
1045             .map_err(|e| e.to_interp_error(src.alloc_id))?
1046             .as_ptr(); // raw ptr, so we can also get a ptr to the destination allocation
1047
1048         // Destination alloc preparations and access hooks.
1049         let (dest_alloc, extra) = self.get_raw_mut(dest.alloc_id)?;
1050         M::memory_written(extra, &mut dest_alloc.extra, dest, size * num_copies)?;
1051         let dest_bytes = dest_alloc
1052             .get_bytes_mut_ptr(&tcx, alloc_range(dest.offset, size * num_copies))
1053             .as_mut_ptr();
1054
1055         if compressed.no_bytes_init() {
1056             // Fast path: If all bytes are `uninit` then there is nothing to copy. The target range
1057             // is marked as uninitialized but we otherwise omit changing the byte representation which may
1058             // be arbitrary for uninitialized bytes.
1059             // This also avoids writing to the target bytes so that the backing allocation is never
1060             // touched if the bytes stay uninitialized for the whole interpreter execution. On contemporary
1061             // operating system this can avoid physically allocating the page.
1062             dest_alloc.mark_init(alloc_range(dest.offset, size * num_copies), false); // `Size` multiplication
1063             dest_alloc.mark_relocation_range(relocations);
1064             return Ok(());
1065         }
1066
1067         // SAFE: The above indexing would have panicked if there weren't at least `size` bytes
1068         // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and
1069         // `dest` could possibly overlap.
1070         // The pointers above remain valid even if the `HashMap` table is moved around because they
1071         // point into the `Vec` storing the bytes.
1072         unsafe {
1073             if src.alloc_id == dest.alloc_id {
1074                 if nonoverlapping {
1075                     // `Size` additions
1076                     if (src.offset <= dest.offset && src.offset + size > dest.offset)
1077                         || (dest.offset <= src.offset && dest.offset + size > src.offset)
1078                     {
1079                         throw_ub_format!("copy_nonoverlapping called on overlapping ranges")
1080                     }
1081                 }
1082
1083                 for i in 0..num_copies {
1084                     ptr::copy(
1085                         src_bytes,
1086                         dest_bytes.add((size * i).bytes_usize()), // `Size` multiplication
1087                         size.bytes_usize(),
1088                     );
1089                 }
1090             } else {
1091                 for i in 0..num_copies {
1092                     ptr::copy_nonoverlapping(
1093                         src_bytes,
1094                         dest_bytes.add((size * i).bytes_usize()), // `Size` multiplication
1095                         size.bytes_usize(),
1096                     );
1097                 }
1098             }
1099         }
1100
1101         // now fill in all the "init" data
1102         dest_alloc.mark_compressed_init_range(
1103             &compressed,
1104             alloc_range(dest.offset, size),
1105             num_copies,
1106         );
1107         // copy the relocations to the destination
1108         dest_alloc.mark_relocation_range(relocations);
1109
1110         Ok(())
1111     }
1112 }
1113
1114 /// Machine pointer introspection.
1115 impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> {
1116     pub fn force_ptr(
1117         &self,
1118         scalar: Scalar<M::PointerTag>,
1119     ) -> InterpResult<'tcx, Pointer<M::PointerTag>> {
1120         match scalar {
1121             Scalar::Ptr(ptr) => Ok(ptr),
1122             _ => M::int_to_ptr(&self, scalar.to_machine_usize(self)?),
1123         }
1124     }
1125
1126     pub fn force_bits(
1127         &self,
1128         scalar: Scalar<M::PointerTag>,
1129         size: Size,
1130     ) -> InterpResult<'tcx, u128> {
1131         match scalar.to_bits_or_ptr(size, self) {
1132             Ok(bits) => Ok(bits),
1133             Err(ptr) => Ok(M::ptr_to_int(&self, ptr)?.into()),
1134         }
1135     }
1136 }