1use std::alloc::Layout;
2use std::borrow::Cow;
3use std::{alloc, slice};
45use rustc_abi::{Align, Size};
6use rustc_middle::mir::interpret::AllocBytes;
78/// Allocation bytes that explicitly handle the layout of the data they're storing.
9/// This is necessary to interface with native code that accesses the program store in Miri.
10#[derive(Debug)]
11pub struct MiriAllocBytes {
12/// Stored layout information about the allocation.
13layout: alloc::Layout,
14/// Pointer to the allocation contents.
15 /// Invariant:
16 /// * If `self.layout.size() == 0`, then `self.ptr` was allocated with the equivalent layout with size 1.
17 /// * Otherwise, `self.ptr` points to memory allocated with `self.layout`.
18ptr: *mut u8,
19}
2021impl Clone for MiriAllocBytes {
22fn clone(&self) -> Self {
23let bytes: Cow<'_, [u8]> = Cow::Borrowed(self);
24let align = Align::from_bytes(self.layout.align().try_into().unwrap()).unwrap();
25 MiriAllocBytes::from_bytes(bytes, align)
26 }
27}
2829impl Drop for MiriAllocBytes {
30fn drop(&mut self) {
31// We have to reconstruct the actual layout used for allocation.
32 // (`Deref` relies on `size` so we can't just always set it to at least 1.)
33let alloc_layout = if self.layout.size() == 0 {
34 Layout::from_size_align(1, self.layout.align()).unwrap()
35 } else {
36self.layout
37 };
38// SAFETY: Invariant, `self.ptr` points to memory allocated with `self.layout`.
39unsafe { alloc::dealloc(self.ptr, alloc_layout) }
40 }
41}
4243impl std::ops::Deref for MiriAllocBytes {
44type Target = [u8];
4546fn deref(&self) -> &Self::Target {
47// SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes.
48 // Note that due to the invariant this is true even if `self.layout.size() == 0`.
49unsafe { slice::from_raw_parts(self.ptr, self.layout.size()) }
50 }
51}
5253impl std::ops::DerefMut for MiriAllocBytes {
54fn deref_mut(&mut self) -> &mut Self::Target {
55// SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes.
56 // Note that due to the invariant this is true even if `self.layout.size() == 0`.
57unsafe { slice::from_raw_parts_mut(self.ptr, self.layout.size()) }
58 }
59}
6061impl MiriAllocBytes {
62/// This method factors out how a `MiriAllocBytes` object is allocated, given a specific allocation function.
63 /// If `size == 0` we allocate using a different `alloc_layout` with `size = 1`, to ensure each allocation has a unique address.
64 /// Returns `Err(alloc_layout)` if the allocation function returns a `ptr` where `ptr.is_null()`.
65fn alloc_with(
66 size: u64,
67 align: u64,
68 alloc_fn: impl FnOnce(Layout) -> *mut u8,
69 ) -> Result<MiriAllocBytes, ()> {
70let size = usize::try_from(size).map_err(|_| ())?;
71let align = usize::try_from(align).map_err(|_| ())?;
72let layout = Layout::from_size_align(size, align).map_err(|_| ())?;
73// When size is 0 we allocate 1 byte anyway, to ensure each allocation has a unique address.
74let alloc_layout =
75if size == 0 { Layout::from_size_align(1, align).unwrap() } else { layout };
76let ptr = alloc_fn(alloc_layout);
77if ptr.is_null() {
78Err(())
79 } else {
80// SAFETY: All `MiriAllocBytes` invariants are fulfilled.
81Ok(Self { ptr, layout })
82 }
83 }
84}
8586impl AllocBytes for MiriAllocBytes {
87fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align) -> Self {
88let slice = slice.into();
89let size = slice.len();
90let align = align.bytes();
91// SAFETY: `alloc_fn` will only be used with `size != 0`.
92let alloc_fn = |layout| unsafe { alloc::alloc(layout) };
93let alloc_bytes = MiriAllocBytes::alloc_with(size.try_into().unwrap(), align, alloc_fn)
94 .unwrap_or_else(|()| {
95panic!("Miri ran out of memory: cannot create allocation of {size} bytes")
96 });
97// SAFETY: `alloc_bytes.ptr` and `slice.as_ptr()` are non-null, properly aligned
98 // and valid for the `size`-many bytes to be copied.
99unsafe { alloc_bytes.ptr.copy_from(slice.as_ptr(), size) };
100 alloc_bytes
101 }
102103fn zeroed(size: Size, align: Align) -> Option<Self> {
104let size = size.bytes();
105let align = align.bytes();
106// SAFETY: `alloc_fn` will only be used with `size != 0`.
107let alloc_fn = |layout| unsafe { alloc::alloc_zeroed(layout) };
108 MiriAllocBytes::alloc_with(size, align, alloc_fn).ok()
109 }
110111fn as_mut_ptr(&mut self) -> *mut u8 {
112self.ptr
113 }
114115fn as_ptr(&self) -> *const u8 {
116self.ptr
117 }
118}