1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! The memory subsystem.
13 //! Generally, we use `Pointer` to denote memory addresses. However, some operations
14 //! have a "size"-like parameter, and they take `Scalar` for the address because
15 //! if the size is 0, then the pointer can also be a (properly aligned, non-NULL)
16 //! integer. It is crucial that these operations call `check_align` *before*
17 //! short-circuiting the empty case!
19 use std::collections::VecDeque;
22 use rustc::ty::{self, Instance, query::TyCtxtAt};
23 use rustc::ty::layout::{self, Align, TargetDataLayout, Size, HasDataLayout};
24 use rustc::mir::interpret::{Pointer, AllocId, Allocation, ConstValue, ScalarMaybeUndef, GlobalId,
25 EvalResult, Scalar, EvalErrorKind, AllocType, PointerArithmetic,
27 pub use rustc::mir::interpret::{write_target_uint, read_target_uint};
28 use rustc_data_structures::fx::{FxHashSet, FxHashMap};
30 use syntax::ast::Mutability;
34 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
35 pub enum MemoryKind<T> {
36 /// Error if deallocated except during a stack pop
38 /// Additional memory kinds a machine wishes to distinguish from the builtin ones
43 pub struct Memory<'a, 'mir, 'tcx: 'a + 'mir, M: Machine<'mir, 'tcx>> {
44 /// Additional data required by the Machine
45 pub data: M::MemoryData,
47 /// Allocations local to this instance of the miri engine. The kind
48 /// helps ensure that the same mechanism is used for allocation and
49 /// deallocation. When an allocation is not found here, it is a
50 /// static and looked up in the `tcx` for read access. Writing to
51 /// a static creates a copy here, in the machine.
52 alloc_map: FxHashMap<AllocId, (MemoryKind<M::MemoryKinds>, Allocation)>,
54 /// To be able to compare pointers with NULL, and to check alignment for accesses
55 /// to ZSTs (where pointers may dangle), we keep track of the size even for allocations
56 /// that do not exist any more.
57 dead_alloc_map: FxHashMap<AllocId, (Size, Align)>,
59 pub tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
62 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout for &'a Memory<'a, 'mir, 'tcx, M> {
64 fn data_layout(&self) -> &TargetDataLayout {
68 impl<'a, 'b, 'c, 'mir, 'tcx, M: Machine<'mir, 'tcx>> HasDataLayout
69 for &'b &'c mut Memory<'a, 'mir, 'tcx, M>
72 fn data_layout(&self) -> &TargetDataLayout {
77 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
78 pub fn new(tcx: TyCtxtAt<'a, 'tcx, 'tcx>, data: M::MemoryData) -> Self {
81 alloc_map: FxHashMap::default(),
82 dead_alloc_map: FxHashMap::default(),
87 pub fn create_fn_alloc(&mut self, instance: Instance<'tcx>) -> Pointer {
88 self.tcx.alloc_map.lock().create_fn_alloc(instance).into()
91 pub fn allocate_static_bytes(&mut self, bytes: &[u8]) -> Pointer {
92 self.tcx.allocate_bytes(bytes).into()
98 kind: MemoryKind<M::MemoryKinds>,
99 ) -> EvalResult<'tcx, AllocId> {
100 let id = self.tcx.alloc_map.lock().reserve();
101 self.alloc_map.insert(id, (kind, alloc));
109 kind: MemoryKind<M::MemoryKinds>,
110 ) -> EvalResult<'tcx, Pointer> {
111 self.allocate_with(Allocation::undef(size, align), kind).map(Pointer::from)
121 kind: MemoryKind<M::MemoryKinds>,
122 ) -> EvalResult<'tcx, Pointer> {
123 if ptr.offset.bytes() != 0 {
124 return err!(ReallocateNonBasePtr);
127 // For simplicities' sake, we implement reallocate as "alloc, copy, dealloc"
128 let new_ptr = self.allocate(new_size, new_align, kind)?;
134 old_size.min(new_size),
135 /*nonoverlapping*/ true,
137 self.deallocate(ptr, Some((old_size, old_align)), kind)?;
142 /// Deallocate a local, or do nothing if that local has been made into a static
143 pub fn deallocate_local(&mut self, ptr: Pointer) -> EvalResult<'tcx> {
144 // The allocation might be already removed by static interning.
145 // This can only really happen in the CTFE instance, not in miri.
146 if self.alloc_map.contains_key(&ptr.alloc_id) {
147 self.deallocate(ptr, None, MemoryKind::Stack)
156 size_and_align: Option<(Size, Align)>,
157 kind: MemoryKind<M::MemoryKinds>,
158 ) -> EvalResult<'tcx> {
159 debug!("deallocating: {}", ptr.alloc_id);
161 if ptr.offset.bytes() != 0 {
162 return err!(DeallocateNonBasePtr);
165 let (alloc_kind, alloc) = match self.alloc_map.remove(&ptr.alloc_id) {
166 Some(alloc) => alloc,
168 // Deallocating static memory -- always an error
169 return match self.tcx.alloc_map.lock().get(ptr.alloc_id) {
170 Some(AllocType::Function(..)) => err!(DeallocatedWrongMemoryKind(
171 "function".to_string(),
172 format!("{:?}", kind),
174 Some(AllocType::Static(..)) |
175 Some(AllocType::Memory(..)) => err!(DeallocatedWrongMemoryKind(
176 "static".to_string(),
177 format!("{:?}", kind),
179 None => err!(DoubleFree)
184 if alloc_kind != kind {
185 return err!(DeallocatedWrongMemoryKind(
186 format!("{:?}", alloc_kind),
187 format!("{:?}", kind),
190 if let Some((size, align)) = size_and_align {
191 if size.bytes() != alloc.bytes.len() as u64 || align != alloc.align {
192 let bytes = Size::from_bytes(alloc.bytes.len() as u64);
193 return err!(IncorrectAllocationInformation(size,
200 // Don't forget to remember size and align of this now-dead allocation
201 let old = self.dead_alloc_map.insert(
203 (Size::from_bytes(alloc.bytes.len() as u64), alloc.align)
206 bug!("Nothing can be deallocated twice");
212 /// Check that the pointer is aligned AND non-NULL. This supports ZSTs in two ways:
213 /// You can pass a scalar, and a `Pointer` does not have to actually still be allocated.
214 pub fn check_align(&self, ptr: Scalar, required_align: Align) -> EvalResult<'tcx> {
215 // Check non-NULL/Undef, extract offset
216 let (offset, alloc_align) = match ptr {
217 Scalar::Ptr(ptr) => {
218 let (size, align) = self.get_size_and_align(ptr.alloc_id)?;
219 // check this is not NULL -- which we can ensure only if this is in-bounds
220 // of some (potentially dead) allocation.
221 if ptr.offset > size {
222 return err!(PointerOutOfBounds {
225 allocation_size: size,
228 // keep data for alignment check
229 (ptr.offset.bytes(), align)
231 Scalar::Bits { bits, size } => {
232 assert_eq!(size as u64, self.pointer_size().bytes());
233 assert!(bits < (1u128 << self.pointer_size().bits()));
234 // check this is not NULL
236 return err!(InvalidNullPointerUsage);
238 // the "base address" is 0 and hence always aligned
239 (bits as u64, required_align)
243 if alloc_align.abi() < required_align.abi() {
244 return err!(AlignmentCheckFailed {
246 required: required_align,
249 if offset % required_align.abi() == 0 {
252 let has = offset % required_align.abi();
253 err!(AlignmentCheckFailed {
254 has: Align::from_bytes(has, has).unwrap(),
255 required: required_align,
260 /// Check if the pointer is "in-bounds". Notice that a pointer pointing at the end
261 /// of an allocation (i.e., at the first *inaccessible* location) *is* considered
262 /// in-bounds! This follows C's/LLVM's rules. The `access` boolean is just used
263 /// for the error message.
264 /// If you want to check bounds before doing a memory access, be sure to
265 /// check the pointer one past the end of your access, then everything will
266 /// work out exactly.
267 pub fn check_bounds(&self, ptr: Pointer, access: bool) -> EvalResult<'tcx> {
268 let alloc = self.get(ptr.alloc_id)?;
269 let allocation_size = alloc.bytes.len() as u64;
270 if ptr.offset.bytes() > allocation_size {
271 return err!(PointerOutOfBounds {
274 allocation_size: Size::from_bytes(allocation_size),
281 /// Allocation accessors
282 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
283 /// Helper function to obtain the global (tcx) allocation for a static
285 tcx: TyCtxtAt<'a, 'tcx, 'tcx>,
287 ) -> EvalResult<'tcx, &'tcx Allocation> {
288 let alloc = tcx.alloc_map.lock().get(id);
289 let def_id = match alloc {
290 Some(AllocType::Memory(mem)) => {
293 Some(AllocType::Function(..)) => {
294 return err!(DerefFunctionPointer)
296 Some(AllocType::Static(did)) => {
300 return err!(DanglingPointerDeref),
302 // We got a "lazy" static that has not been computed yet, do some work
303 trace!("static_alloc: Need to compute {:?}", def_id);
304 if tcx.is_foreign_item(def_id) {
305 return M::find_foreign_static(tcx, def_id);
307 let instance = Instance::mono(tcx.tcx, def_id);
312 tcx.const_eval(ty::ParamEnv::reveal_all().and(gid)).map_err(|err| {
313 // no need to report anything, the const_eval call takes care of that for statics
314 assert!(tcx.is_static(def_id).is_some());
315 EvalErrorKind::ReferencedConstant(err).into()
317 if let ConstValue::ByRef(_, allocation, _) = const_val.val {
320 bug!("Matching on non-ByRef static")
325 pub fn get(&self, id: AllocId) -> EvalResult<'tcx, &Allocation> {
326 match self.alloc_map.get(&id) {
328 Some(alloc) => Ok(&alloc.1),
329 // Static. No need to make any copies, just provide read access to the global static
331 None => Self::get_static_alloc(self.tcx, id),
335 pub fn get_size_and_align(&self, id: AllocId) -> EvalResult<'tcx, (Size, Align)> {
336 Ok(match self.get(id) {
337 Ok(alloc) => (Size::from_bytes(alloc.bytes.len() as u64), alloc.align),
338 Err(err) => match err.kind {
339 EvalErrorKind::DanglingPointerDeref =>
340 // This should be in the dead allocation map
341 *self.dead_alloc_map.get(&id).expect(
342 "allocation missing in dead_alloc_map"
344 // E.g. a function ptr allocation
353 ) -> EvalResult<'tcx, &mut Allocation> {
355 if !self.alloc_map.contains_key(&id) {
356 // Ask the machine for what to do
357 if let Some(kind) = M::MUT_STATIC_KIND {
358 // The machine supports mutating statics. Make a copy, use that.
359 self.deep_copy_static(id, MemoryKind::Machine(kind))?;
361 return err!(ModifiedConstantMemory)
364 // If we come here, we know the allocation is in our map
365 let alloc = &mut self.alloc_map.get_mut(&id).unwrap().1;
366 // See if we are allowed to mutate this
367 if alloc.mutability == Mutability::Immutable {
368 err!(ModifiedConstantMemory)
374 pub fn get_fn(&self, ptr: Pointer) -> EvalResult<'tcx, Instance<'tcx>> {
375 if ptr.offset.bytes() != 0 {
376 return err!(InvalidFunctionPointer);
378 trace!("reading fn ptr: {}", ptr.alloc_id);
379 match self.tcx.alloc_map.lock().get(ptr.alloc_id) {
380 Some(AllocType::Function(instance)) => Ok(instance),
381 _ => Err(EvalErrorKind::ExecuteMemory.into()),
385 /// For debugging, print an allocation and all allocations it points to, recursively.
386 pub fn dump_alloc(&self, id: AllocId) {
387 if !log_enabled!(::log::Level::Trace) {
390 self.dump_allocs(vec![id]);
393 /// For debugging, print a list of allocations and all allocations they point to, recursively.
394 pub fn dump_allocs(&self, mut allocs: Vec<AllocId>) {
395 if !log_enabled!(::log::Level::Trace) {
401 let mut allocs_to_print = VecDeque::from(allocs);
402 let mut allocs_seen = FxHashSet::default();
404 while let Some(id) = allocs_to_print.pop_front() {
405 let mut msg = format!("Alloc {:<5} ", format!("{}:", id));
406 let prefix_len = msg.len();
407 let mut relocations = vec![];
409 let (alloc, immutable) =
411 match self.alloc_map.get(&id) {
412 Some((kind, alloc)) => (alloc, match kind {
413 MemoryKind::Stack => " (stack)".to_owned(),
414 MemoryKind::Machine(m) => format!(" ({:?})", m),
418 match self.tcx.alloc_map.lock().get(id) {
419 Some(AllocType::Memory(a)) => (a, " (immutable)".to_owned()),
420 Some(AllocType::Function(func)) => {
421 trace!("{} {}", msg, func);
424 Some(AllocType::Static(did)) => {
425 trace!("{} {:?}", msg, did);
429 trace!("{} (deallocated)", msg);
436 for i in 0..(alloc.bytes.len() as u64) {
437 let i = Size::from_bytes(i);
438 if let Some(&target_id) = alloc.relocations.get(&i) {
439 if allocs_seen.insert(target_id) {
440 allocs_to_print.push_back(target_id);
442 relocations.push((i, target_id));
444 if alloc.undef_mask.is_range_defined(i, i + Size::from_bytes(1)).is_ok() {
445 // this `as usize` is fine, since `i` came from a `usize`
446 write!(msg, "{:02x} ", alloc.bytes[i.bytes() as usize]).unwrap();
453 "{}({} bytes, alignment {}){}",
460 if !relocations.is_empty() {
462 write!(msg, "{:1$}", "", prefix_len).unwrap(); // Print spaces.
463 let mut pos = Size::ZERO;
464 let relocation_width = (self.pointer_size().bytes() - 1) * 3;
465 for (i, target_id) in relocations {
466 // this `as usize` is fine, since we can't print more chars than `usize::MAX`
467 write!(msg, "{:1$}", "", ((i - pos) * 3).bytes() as usize).unwrap();
468 let target = format!("({})", target_id);
469 // this `as usize` is fine, since we can't print more chars than `usize::MAX`
470 write!(msg, "└{0:─^1$}┘ ", target, relocation_width as usize).unwrap();
471 pos = i + self.pointer_size();
478 pub fn leak_report(&self) -> usize {
479 trace!("### LEAK REPORT ###");
480 let mut_static_kind = M::MUT_STATIC_KIND.map(|k| MemoryKind::Machine(k));
481 let leaks: Vec<_> = self.alloc_map
483 .filter_map(|(&id, &(kind, _))|
484 // exclude mutable statics
485 if Some(kind) == mut_static_kind { None } else { Some(id) } )
488 self.dump_allocs(leaks);
494 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
495 /// The last argument controls whether we error out when there are undefined
496 /// or pointer bytes. You should never call this, call `get_bytes` or
497 /// `get_bytes_with_undef_and_ptr` instead,
498 fn get_bytes_internal(
503 check_defined_and_ptr: bool,
504 ) -> EvalResult<'tcx, &[u8]> {
505 assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
506 self.check_align(ptr.into(), align)?;
507 // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
508 self.check_bounds(ptr.offset(size, &*self)?, true)?;
510 if check_defined_and_ptr {
511 self.check_defined(ptr, size)?;
512 self.check_relocations(ptr, size)?;
514 // We still don't want relocations on the *edges*
515 self.check_relocation_edges(ptr, size)?;
518 let alloc = self.get(ptr.alloc_id)?;
519 assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
520 assert_eq!(size.bytes() as usize as u64, size.bytes());
521 let offset = ptr.offset.bytes() as usize;
522 Ok(&alloc.bytes[offset..offset + size.bytes() as usize])
526 fn get_bytes(&self, ptr: Pointer, size: Size, align: Align) -> EvalResult<'tcx, &[u8]> {
527 self.get_bytes_internal(ptr, size, align, true)
530 /// It is the caller's responsibility to handle undefined and pointer bytes.
531 /// However, this still checks that there are no relocations on the egdes.
533 fn get_bytes_with_undef_and_ptr(
538 ) -> EvalResult<'tcx, &[u8]> {
539 self.get_bytes_internal(ptr, size, align, false)
542 /// Just calling this already marks everything as defined and removes relocations,
543 /// so be sure to actually put data there!
549 ) -> EvalResult<'tcx, &mut [u8]> {
550 assert_ne!(size.bytes(), 0, "0-sized accesses should never even get a `Pointer`");
551 self.check_align(ptr.into(), align)?;
552 // if ptr.offset is in bounds, then so is ptr (because offset checks for overflow)
553 self.check_bounds(ptr.offset(size, &self)?, true)?;
555 self.mark_definedness(ptr, size, true)?;
556 self.clear_relocations(ptr, size)?;
558 let alloc = self.get_mut(ptr.alloc_id)?;
559 assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
560 assert_eq!(size.bytes() as usize as u64, size.bytes());
561 let offset = ptr.offset.bytes() as usize;
562 Ok(&mut alloc.bytes[offset..offset + size.bytes() as usize])
566 /// Reading and writing
567 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
568 /// mark an allocation as static and initialized, either mutable or not
569 pub fn intern_static(
572 mutability: Mutability,
573 ) -> EvalResult<'tcx> {
575 "mark_static_initialized {:?}, mutability: {:?}",
580 let (kind, mut alloc) = self.alloc_map.remove(&alloc_id).unwrap();
582 MemoryKind::Machine(_) => bug!("Static cannot refer to machine memory"),
583 MemoryKind::Stack => {},
585 // ensure llvm knows not to put this into immutable memory
586 alloc.mutability = mutability;
587 let alloc = self.tcx.intern_const_alloc(alloc);
588 self.tcx.alloc_map.lock().set_id_memory(alloc_id, alloc);
589 // recurse into inner allocations
590 for &alloc in alloc.relocations.values() {
591 // FIXME: Reusing the mutability here is likely incorrect. It is originally
592 // determined via `is_freeze`, and data is considered frozen if there is no
593 // `UnsafeCell` *immediately* in that data -- however, this search stops
594 // at references. So whenever we follow a reference, we should likely
595 // assume immutability -- and we should make sure that the compiler
596 // does not permit code that would break this!
597 if self.alloc_map.contains_key(&alloc) {
598 // Not yet interned, so proceed recursively
599 self.intern_static(alloc, mutability)?;
605 /// The alloc_id must refer to a (mutable) static; a deep copy of that
606 /// static is made into this memory.
610 kind: MemoryKind<M::MemoryKinds>,
611 ) -> EvalResult<'tcx> {
612 let alloc = Self::get_static_alloc(self.tcx, id)?;
613 if alloc.mutability == Mutability::Immutable {
614 return err!(ModifiedConstantMemory);
616 let old = self.alloc_map.insert(id, (kind, alloc.clone()));
617 assert!(old.is_none(), "deep_copy_static: must not overwrite existing memory");
628 nonoverlapping: bool,
629 ) -> EvalResult<'tcx> {
630 self.copy_repeatedly(src, src_align, dest, dest_align, size, 1, nonoverlapping)
633 pub fn copy_repeatedly(
641 nonoverlapping: bool,
642 ) -> EvalResult<'tcx> {
643 if size.bytes() == 0 {
644 // Nothing to do for ZST, other than checking alignment and non-NULLness.
645 self.check_align(src, src_align)?;
646 self.check_align(dest, dest_align)?;
649 let src = src.to_ptr()?;
650 let dest = dest.to_ptr()?;
652 // first copy the relocations to a temporary buffer, because
653 // `get_bytes_mut` will clear the relocations, which is correct,
654 // since we don't want to keep any relocations at the target.
655 // (`get_bytes_with_undef_and_ptr` below checks that there are no
656 // relocations overlapping the edges; those would not be handled correctly).
658 let relocations = self.relocations(src, size)?;
659 let mut new_relocations = Vec::with_capacity(relocations.len() * (length as usize));
661 new_relocations.extend(
664 .map(|&(offset, alloc_id)| {
665 (offset + dest.offset - src.offset + (i * size * relocations.len() as u64),
674 // This also checks alignment, and relocation edges on the src.
675 let src_bytes = self.get_bytes_with_undef_and_ptr(src, size, src_align)?.as_ptr();
676 let dest_bytes = self.get_bytes_mut(dest, size * length, dest_align)?.as_mut_ptr();
678 // SAFE: The above indexing would have panicked if there weren't at least `size` bytes
679 // behind `src` and `dest`. Also, we use the overlapping-safe `ptr::copy` if `src` and
680 // `dest` could possibly overlap.
682 assert_eq!(size.bytes() as usize as u64, size.bytes());
683 if src.alloc_id == dest.alloc_id {
685 if (src.offset <= dest.offset && src.offset + size > dest.offset) ||
686 (dest.offset <= src.offset && dest.offset + size > src.offset)
688 return err!(Intrinsic(
689 "copy_nonoverlapping called on overlapping ranges".to_string(),
696 dest_bytes.offset((size.bytes() * i) as isize),
697 size.bytes() as usize);
701 ptr::copy_nonoverlapping(src_bytes,
702 dest_bytes.offset((size.bytes() * i) as isize),
703 size.bytes() as usize);
708 // copy definedness to the destination
709 self.copy_undef_mask(src, dest, size, length)?;
710 // copy the relocations to the destination
711 self.get_mut(dest.alloc_id)?.relocations.insert_presorted(relocations);
716 pub fn read_c_str(&self, ptr: Pointer) -> EvalResult<'tcx, &[u8]> {
717 let alloc = self.get(ptr.alloc_id)?;
718 assert_eq!(ptr.offset.bytes() as usize as u64, ptr.offset.bytes());
719 let offset = ptr.offset.bytes() as usize;
720 match alloc.bytes[offset..].iter().position(|&c| c == 0) {
722 let p1 = Size::from_bytes((size + 1) as u64);
723 self.check_relocations(ptr, p1)?;
724 self.check_defined(ptr, p1)?;
725 Ok(&alloc.bytes[offset..offset + size])
727 None => err!(UnterminatedCString(ptr)),
731 pub fn read_bytes(&self, ptr: Scalar, size: Size) -> EvalResult<'tcx, &[u8]> {
732 // Empty accesses don't need to be valid pointers, but they should still be non-NULL
733 let align = Align::from_bytes(1, 1).unwrap();
734 if size.bytes() == 0 {
735 self.check_align(ptr, align)?;
738 self.get_bytes(ptr.to_ptr()?, size, align)
741 pub fn write_bytes(&mut self, ptr: Scalar, src: &[u8]) -> EvalResult<'tcx> {
742 // Empty accesses don't need to be valid pointers, but they should still be non-NULL
743 let align = Align::from_bytes(1, 1).unwrap();
745 self.check_align(ptr, align)?;
748 let bytes = self.get_bytes_mut(ptr.to_ptr()?, Size::from_bytes(src.len() as u64), align)?;
749 bytes.clone_from_slice(src);
753 pub fn write_repeat(&mut self, ptr: Scalar, val: u8, count: Size) -> EvalResult<'tcx> {
754 // Empty accesses don't need to be valid pointers, but they should still be non-NULL
755 let align = Align::from_bytes(1, 1).unwrap();
756 if count.bytes() == 0 {
757 self.check_align(ptr, align)?;
760 let bytes = self.get_bytes_mut(ptr.to_ptr()?, count, align)?;
767 /// Read a *non-ZST* scalar
773 ) -> EvalResult<'tcx, ScalarMaybeUndef> {
774 // get_bytes_unchecked tests alignment and relocation edges
775 let bytes = self.get_bytes_with_undef_and_ptr(
776 ptr, size, ptr_align.min(self.int_align(size))
778 // Undef check happens *after* we established that the alignment is correct.
779 // We must not return Ok() for unaligned pointers!
780 if self.check_defined(ptr, size).is_err() {
781 // this inflates undefined bytes to the entire scalar, even if only a few
782 // bytes are undefined
783 return Ok(ScalarMaybeUndef::Undef);
785 // Now we do the actual reading
786 let bits = read_target_uint(self.tcx.data_layout.endian, bytes).unwrap();
787 // See if we got a pointer
788 if size != self.pointer_size() {
789 // *Now* better make sure that the inside also is free of relocations.
790 self.check_relocations(ptr, size)?;
792 let alloc = self.get(ptr.alloc_id)?;
793 match alloc.relocations.get(&ptr.offset) {
795 let ptr = Pointer::new(alloc_id, Size::from_bytes(bits as u64));
796 return Ok(ScalarMaybeUndef::Scalar(ptr.into()))
801 // We don't. Just return the bits.
802 Ok(ScalarMaybeUndef::Scalar(Scalar::from_uint(bits, size)))
805 pub fn read_ptr_sized(&self, ptr: Pointer, ptr_align: Align)
806 -> EvalResult<'tcx, ScalarMaybeUndef> {
807 self.read_scalar(ptr, ptr_align, self.pointer_size())
810 /// Write a *non-ZST* scalar
815 val: ScalarMaybeUndef,
817 ) -> EvalResult<'tcx> {
818 let val = match val {
819 ScalarMaybeUndef::Scalar(scalar) => scalar,
820 ScalarMaybeUndef::Undef => return self.mark_definedness(ptr, type_size, false),
823 let bytes = match val {
824 Scalar::Ptr(val) => {
825 assert_eq!(type_size, self.pointer_size());
826 val.offset.bytes() as u128
829 Scalar::Bits { bits, size } => {
830 assert_eq!(size as u64, type_size.bytes());
831 debug_assert_eq!(truncate(bits, Size::from_bytes(size.into())), bits,
832 "Unexpected value of size {} when writing to memory", size);
838 // get_bytes_mut checks alignment
839 let endian = self.tcx.data_layout.endian;
840 let dst = self.get_bytes_mut(ptr, type_size, ptr_align)?;
841 write_target_uint(endian, dst, bytes).unwrap();
844 // See if we have to also write a relocation
846 Scalar::Ptr(val) => {
847 self.get_mut(ptr.alloc_id)?.relocations.insert(
858 pub fn write_ptr_sized(&mut self, ptr: Pointer, ptr_align: Align, val: ScalarMaybeUndef)
859 -> EvalResult<'tcx> {
860 let ptr_size = self.pointer_size();
861 self.write_scalar(ptr.into(), ptr_align, val, ptr_size)
864 fn int_align(&self, size: Size) -> Align {
865 // We assume pointer-sized integers have the same alignment as pointers.
866 // We also assume signed and unsigned integers of the same size have the same alignment.
867 let ity = match size.bytes() {
873 _ => bug!("bad integer size: {}", size.bytes()),
880 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
881 /// Return all relocations overlapping with the given ptr-offset pair.
886 ) -> EvalResult<'tcx, &[(Size, AllocId)]> {
887 // We have to go back `pointer_size - 1` bytes, as that one would still overlap with
888 // the beginning of this range.
889 let start = ptr.offset.bytes().saturating_sub(self.pointer_size().bytes() - 1);
890 let end = ptr.offset + size; // this does overflow checking
891 Ok(self.get(ptr.alloc_id)?.relocations.range(Size::from_bytes(start)..end))
894 /// Check that there ar eno relocations overlapping with the given range.
896 fn check_relocations(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> {
897 if self.relocations(ptr, size)?.len() != 0 {
898 err!(ReadPointerAsBytes)
904 /// Remove all relocations inside the given range.
905 /// If there are relocations overlapping with the edges, they
906 /// are removed as well *and* the bytes they cover are marked as
907 /// uninitialized. This is a somewhat odd "spooky action at a distance",
908 /// but it allows strictly more code to run than if we would just error
909 /// immediately in that case.
910 fn clear_relocations(&mut self, ptr: Pointer, size: Size) -> EvalResult<'tcx> {
911 // Find the start and end of the given range and its outermost relocations.
912 let (first, last) = {
913 // Find all relocations overlapping the given range.
914 let relocations = self.relocations(ptr, size)?;
915 if relocations.is_empty() {
919 (relocations.first().unwrap().0,
920 relocations.last().unwrap().0 + self.pointer_size())
922 let start = ptr.offset;
923 let end = start + size;
925 let alloc = self.get_mut(ptr.alloc_id)?;
927 // Mark parts of the outermost relocations as undefined if they partially fall outside the
930 alloc.undef_mask.set_range(first, start, false);
933 alloc.undef_mask.set_range(end, last, false);
936 // Forget all the relocations.
937 alloc.relocations.remove_range(first..last);
942 /// Error if there are relocations overlapping with the egdes of the
943 /// given memory range.
945 fn check_relocation_edges(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> {
946 self.check_relocations(ptr, Size::ZERO)?;
947 self.check_relocations(ptr.offset(size, self)?, Size::ZERO)?;
953 impl<'a, 'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'a, 'mir, 'tcx, M> {
954 // FIXME(solson): This is a very naive, slow version.
961 ) -> EvalResult<'tcx> {
962 // The bits have to be saved locally before writing to dest in case src and dest overlap.
963 assert_eq!(size.bytes() as usize as u64, size.bytes());
965 let undef_mask = self.get(src.alloc_id)?.undef_mask.clone();
966 let dest_allocation = self.get_mut(dest.alloc_id)?;
968 for i in 0..size.bytes() {
969 let defined = undef_mask.get(src.offset + Size::from_bytes(i));
972 dest_allocation.undef_mask.set(
973 dest.offset + Size::from_bytes(i + (size.bytes() * j)),
982 /// Checks that a range of bytes is defined. If not, returns the `ReadUndefBytes`
983 /// error which will report the first byte which is undefined.
985 fn check_defined(&self, ptr: Pointer, size: Size) -> EvalResult<'tcx> {
986 let alloc = self.get(ptr.alloc_id)?;
987 alloc.undef_mask.is_range_defined(
990 ).or_else(|idx| err!(ReadUndefBytes(idx)))
993 pub fn mark_definedness(
998 ) -> EvalResult<'tcx> {
999 if size.bytes() == 0 {
1002 let alloc = self.get_mut(ptr.alloc_id)?;
1003 alloc.undef_mask.set_range(