Skip to main content

cranelift_jit/memory/
system.rs

1use std::io;
2use std::mem;
3use std::ptr;
4
5use cranelift_module::{ModuleError, ModuleResult};
6use memmap2::MmapMut;
7
8use super::{BranchProtection, JITMemoryKind, JITMemoryProvider};
9
10/// A simple struct consisting of a pointer and length.
11struct PtrLen {
12    map: Option<MmapMut>,
13    ptr: *mut u8,
14    len: usize,
15}
16
17impl PtrLen {
18    /// Create a new empty `PtrLen`.
19    fn new() -> Self {
20        Self {
21            map: None,
22            ptr: ptr::null_mut(),
23            len: 0,
24        }
25    }
26
27    /// Create a new `PtrLen` pointing to at least `size` bytes of memory,
28    /// suitably sized and aligned for memory protection.
29    fn with_size(size: usize) -> io::Result<Self> {
30        let alloc_size = region::page::ceil(size as *const ()) as usize;
31        MmapMut::map_anon(alloc_size).map(|mut mmap| {
32            // The order here is important; we assign the pointer first to get
33            // around compile time borrow errors.
34            Self {
35                ptr: mmap.as_mut_ptr(),
36                map: Some(mmap),
37                len: alloc_size,
38            }
39        })
40    }
41}
42
43/// JIT memory manager. This manages pages of suitably aligned and
44/// accessible memory. Memory will be leaked by default to have
45/// function pointers remain valid for the remainder of the
46/// program's life.
47pub(crate) struct Memory {
48    allocations: Vec<PtrLen>,
49    already_protected: usize,
50    current: PtrLen,
51    position: usize,
52}
53
54unsafe impl Send for Memory {}
55
56impl Memory {
57    pub(crate) fn new() -> Self {
58        Self {
59            allocations: Vec::new(),
60            already_protected: 0,
61            current: PtrLen::new(),
62            position: 0,
63        }
64    }
65
66    fn finish_current(&mut self) {
67        self.allocations
68            .push(mem::replace(&mut self.current, PtrLen::new()));
69        self.position = 0;
70    }
71
72    pub(crate) fn allocate(&mut self, size: usize, align: u64) -> io::Result<*mut u8> {
73        let align = usize::try_from(align).expect("alignment too big");
74        if self.position % align != 0 {
75            self.position += align - self.position % align;
76            debug_assert!(self.position % align == 0);
77        }
78
79        if size <= self.current.len - self.position {
80            // TODO: Ensure overflow is not possible.
81            let ptr = unsafe { self.current.ptr.add(self.position) };
82            self.position += size;
83            return Ok(ptr);
84        }
85
86        self.finish_current();
87
88        // TODO: Allocate more at a time.
89        self.current = PtrLen::with_size(size)?;
90        self.position = size;
91
92        Ok(self.current.ptr)
93    }
94
95    /// Set all memory allocated in this `Memory` up to now as readable and executable.
96    pub(crate) fn set_readable_and_executable(
97        &mut self,
98        branch_protection: BranchProtection,
99    ) -> ModuleResult<()> {
100        self.finish_current();
101
102        for &PtrLen { ptr, len, .. } in self.non_protected_allocations_iter() {
103            super::set_readable_and_executable(ptr, len, branch_protection)?;
104        }
105
106        // Flush any in-flight instructions from the pipeline
107        wasmtime_jit_icache_coherence::pipeline_flush_mt().expect("Failed pipeline flush");
108
109        self.already_protected = self.allocations.len();
110        Ok(())
111    }
112
113    /// Set all memory allocated in this `Memory` up to now as readonly.
114    pub(crate) fn set_readonly(&mut self) -> ModuleResult<()> {
115        self.finish_current();
116
117        for &PtrLen { ptr, len, .. } in self.non_protected_allocations_iter() {
118            unsafe {
119                region::protect(ptr, len, region::Protection::READ).map_err(|e| {
120                    ModuleError::Backend(
121                        anyhow::Error::new(e).context("unable to make memory readonly"),
122                    )
123                })?;
124            }
125        }
126
127        self.already_protected = self.allocations.len();
128        Ok(())
129    }
130
131    /// Iterates non protected memory allocations that are of not zero bytes in size.
132    fn non_protected_allocations_iter(&self) -> impl Iterator<Item = &PtrLen> {
133        self.allocations[self.already_protected..]
134            .iter()
135            .filter(|&PtrLen { map, len, .. }| *len != 0 && map.is_some())
136    }
137
138    /// Frees all allocated memory regions that would be leaked otherwise.
139    /// Likely to invalidate existing function pointers, causing unsafety.
140    pub(crate) unsafe fn free_memory(&mut self) {
141        self.allocations.clear();
142        self.already_protected = 0;
143    }
144}
145
146impl Drop for Memory {
147    fn drop(&mut self) {
148        // leak memory to guarantee validity of function pointers
149        mem::replace(&mut self.allocations, Vec::new())
150            .into_iter()
151            .for_each(mem::forget);
152    }
153}
154
155/// A memory provider that allocates memory on-demand using the system
156/// allocator.
157///
158/// Note: Memory will be leaked by default unless
159/// [`JITMemoryProvider::free_memory`] is called to ensure function pointers
160/// remain valid for the remainder of the program's life.
161pub struct SystemMemoryProvider {
162    code: Memory,
163    readonly: Memory,
164    writable: Memory,
165}
166
167impl SystemMemoryProvider {
168    /// Create a new memory handle with the given branch protection.
169    pub fn new() -> Self {
170        Self {
171            code: Memory::new(),
172            readonly: Memory::new(),
173            writable: Memory::new(),
174        }
175    }
176}
177
178impl JITMemoryProvider for SystemMemoryProvider {
179    unsafe fn free_memory(&mut self) {
180        self.code.free_memory();
181        self.readonly.free_memory();
182        self.writable.free_memory();
183    }
184
185    fn finalize(&mut self, branch_protection: BranchProtection) -> ModuleResult<()> {
186        self.readonly.set_readonly()?;
187        self.code.set_readable_and_executable(branch_protection)
188    }
189
190    fn allocate(&mut self, size: usize, align: u64, kind: JITMemoryKind) -> io::Result<*mut u8> {
191        match kind {
192            JITMemoryKind::Executable => self.code.allocate(size, align),
193            JITMemoryKind::Writable => self.writable.allocate(size, align),
194            JITMemoryKind::ReadOnly => self.readonly.allocate(size, align),
195        }
196    }
197}