1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
use std::alloc;
use std::alloc::Layout;
use std::borrow::Cow;
use std::slice;

use rustc_middle::mir::interpret::AllocBytes;
use rustc_target::abi::{Align, Size};

/// Allocation bytes that explicitly handle the layout of the data they're storing.
/// This is necessary to interface with native code that accesses the program store in Miri.
#[derive(Debug)]
pub struct MiriAllocBytes {
    /// Stored layout information about the allocation.
    layout: alloc::Layout,
    /// Pointer to the allocation contents.
    /// Invariant:
    /// * If `self.layout.size() == 0`, then `self.ptr` is some suitably aligned pointer
    ///   without provenance (and no actual memory was allocated).
    /// * Otherwise, `self.ptr` points to memory allocated with `self.layout`.
    ptr: *mut u8,
}

impl Clone for MiriAllocBytes {
    fn clone(&self) -> Self {
        let bytes: Cow<'_, [u8]> = Cow::Borrowed(self);
        let align = Align::from_bytes(self.layout.align().try_into().unwrap()).unwrap();
        MiriAllocBytes::from_bytes(bytes, align)
    }
}

impl Drop for MiriAllocBytes {
    fn drop(&mut self) {
        if self.layout.size() != 0 {
            // SAFETY: Invariant, `self.ptr` points to memory allocated with `self.layout`.
            unsafe { alloc::dealloc(self.ptr, self.layout) }
        }
    }
}

impl std::ops::Deref for MiriAllocBytes {
    type Target = [u8];

    fn deref(&self) -> &Self::Target {
        // SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes.
        // Note that due to the invariant this is true even if `self.layout.size() == 0`.
        unsafe { slice::from_raw_parts(self.ptr, self.layout.size()) }
    }
}

impl std::ops::DerefMut for MiriAllocBytes {
    fn deref_mut(&mut self) -> &mut Self::Target {
        // SAFETY: `ptr` is non-null, properly aligned, and valid for reading out `self.layout.size()`-many bytes.
        // Note that due to the invariant this is true even if `self.layout.size() == 0`.
        unsafe { slice::from_raw_parts_mut(self.ptr, self.layout.size()) }
    }
}

impl MiriAllocBytes {
    /// This method factors out how a `MiriAllocBytes` object is allocated,
    /// specifically given an allocation function `alloc_fn`.
    /// `alloc_fn` is only used if `size != 0`.
    /// Returns `Err(layout)` if the allocation function returns a `ptr` that is `ptr.is_null()`.
    fn alloc_with(
        size: usize,
        align: usize,
        alloc_fn: impl FnOnce(Layout) -> *mut u8,
    ) -> Result<MiriAllocBytes, Layout> {
        let layout = Layout::from_size_align(size, align).unwrap();
        let ptr = if size == 0 {
            std::ptr::without_provenance_mut(align)
        } else {
            let ptr = alloc_fn(layout);
            if ptr.is_null() {
                return Err(layout);
            }
            ptr
        };
        // SAFETY: All `MiriAllocBytes` invariants are fulfilled.
        Ok(Self { ptr, layout })
    }
}

impl AllocBytes for MiriAllocBytes {
    fn from_bytes<'a>(slice: impl Into<Cow<'a, [u8]>>, align: Align) -> Self {
        let slice = slice.into();
        let size = slice.len();
        let align = align.bytes_usize();
        // SAFETY: `alloc_fn` will only be used if `size != 0`.
        let alloc_fn = |layout| unsafe { alloc::alloc(layout) };
        let alloc_bytes = MiriAllocBytes::alloc_with(size, align, alloc_fn)
            .unwrap_or_else(|layout| alloc::handle_alloc_error(layout));
        // SAFETY: `alloc_bytes.ptr` and `slice.as_ptr()` are non-null, properly aligned
        // and valid for the `size`-many bytes to be copied.
        unsafe { alloc_bytes.ptr.copy_from(slice.as_ptr(), size) };
        alloc_bytes
    }

    fn zeroed(size: Size, align: Align) -> Option<Self> {
        let size = size.bytes_usize();
        let align = align.bytes_usize();
        // SAFETY: `alloc_fn` will only be used if `size != 0`.
        let alloc_fn = |layout| unsafe { alloc::alloc_zeroed(layout) };
        MiriAllocBytes::alloc_with(size, align, alloc_fn).ok()
    }

    fn as_mut_ptr(&mut self) -> *mut u8 {
        self.ptr
    }
}