cranelift_jit/memory/
system.rs1use cranelift_module::{ModuleError, ModuleResult};
2
3#[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
4use memmap2::MmapMut;
5
6#[cfg(not(any(feature = "selinux-fix", windows)))]
7use std::alloc;
8use std::io;
9use std::mem;
10use std::ptr;
11
12use super::{BranchProtection, JITMemoryKind, JITMemoryProvider};
13
14struct PtrLen {
16 #[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
17 map: Option<MmapMut>,
18
19 ptr: *mut u8,
20 len: usize,
21}
22
23impl PtrLen {
24 fn new() -> Self {
26 Self {
27 #[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
28 map: None,
29
30 ptr: ptr::null_mut(),
31 len: 0,
32 }
33 }
34
35 #[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
38 fn with_size(size: usize) -> io::Result<Self> {
39 let alloc_size = region::page::ceil(size as *const ()) as usize;
40 MmapMut::map_anon(alloc_size).map(|mut mmap| {
41 Self {
44 ptr: mmap.as_mut_ptr(),
45 map: Some(mmap),
46 len: alloc_size,
47 }
48 })
49 }
50
51 #[cfg(all(not(target_os = "windows"), not(feature = "selinux-fix")))]
52 fn with_size(size: usize) -> io::Result<Self> {
53 assert_ne!(size, 0);
54 let page_size = region::page::size();
55 let alloc_size = region::page::ceil(size as *const ()) as usize;
56 let layout = alloc::Layout::from_size_align(alloc_size, page_size).unwrap();
57 let ptr = unsafe { alloc::alloc(layout) };
59
60 if !ptr.is_null() {
61 Ok(Self {
62 ptr,
63 len: alloc_size,
64 })
65 } else {
66 Err(io::Error::from(io::ErrorKind::OutOfMemory))
67 }
68 }
69
70 #[cfg(target_os = "windows")]
71 fn with_size(size: usize) -> io::Result<Self> {
72 use windows_sys::Win32::System::Memory::{
73 MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE, VirtualAlloc,
74 };
75
76 let ptr = unsafe {
78 VirtualAlloc(
79 ptr::null_mut(),
80 size,
81 MEM_COMMIT | MEM_RESERVE,
82 PAGE_READWRITE,
83 )
84 };
85 if !ptr.is_null() {
86 Ok(Self {
87 ptr: ptr as *mut u8,
88 len: region::page::ceil(size as *const ()) as usize,
89 })
90 } else {
91 Err(io::Error::last_os_error())
92 }
93 }
94}
95
96#[cfg(all(not(target_os = "windows"), not(feature = "selinux-fix")))]
98impl Drop for PtrLen {
99 fn drop(&mut self) {
100 if !self.ptr.is_null() {
101 let page_size = region::page::size();
102 let layout = alloc::Layout::from_size_align(self.len, page_size).unwrap();
103 unsafe {
104 region::protect(self.ptr, self.len, region::Protection::READ_WRITE)
105 .expect("unable to unprotect memory");
106 alloc::dealloc(self.ptr, layout)
107 }
108 }
109 }
110}
111
112pub(crate) struct Memory {
119 allocations: Vec<PtrLen>,
120 already_protected: usize,
121 current: PtrLen,
122 position: usize,
123}
124
125unsafe impl Send for Memory {}
126
127impl Memory {
128 pub(crate) fn new() -> Self {
129 Self {
130 allocations: Vec::new(),
131 already_protected: 0,
132 current: PtrLen::new(),
133 position: 0,
134 }
135 }
136
137 fn finish_current(&mut self) {
138 self.allocations
139 .push(mem::replace(&mut self.current, PtrLen::new()));
140 self.position = 0;
141 }
142
143 pub(crate) fn allocate(&mut self, size: usize, align: u64) -> io::Result<*mut u8> {
144 let align = usize::try_from(align).expect("alignment too big");
145 if self.position % align != 0 {
146 self.position += align - self.position % align;
147 debug_assert!(self.position % align == 0);
148 }
149
150 if size <= self.current.len - self.position {
151 let ptr = unsafe { self.current.ptr.add(self.position) };
153 self.position += size;
154 return Ok(ptr);
155 }
156
157 self.finish_current();
158
159 self.current = PtrLen::with_size(size)?;
161 self.position = size;
162
163 Ok(self.current.ptr)
164 }
165
166 pub(crate) fn set_readable_and_executable(
168 &mut self,
169 branch_protection: BranchProtection,
170 ) -> ModuleResult<()> {
171 self.finish_current();
172
173 for &PtrLen { ptr, len, .. } in self.non_protected_allocations_iter() {
174 super::set_readable_and_executable(ptr, len, branch_protection)?;
175 }
176
177 wasmtime_jit_icache_coherence::pipeline_flush_mt().expect("Failed pipeline flush");
179
180 self.already_protected = self.allocations.len();
181 Ok(())
182 }
183
184 pub(crate) fn set_readonly(&mut self) -> ModuleResult<()> {
186 self.finish_current();
187
188 for &PtrLen { ptr, len, .. } in self.non_protected_allocations_iter() {
189 unsafe {
190 region::protect(ptr, len, region::Protection::READ).map_err(|e| {
191 ModuleError::Backend(
192 anyhow::Error::new(e).context("unable to make memory readonly"),
193 )
194 })?;
195 }
196 }
197
198 self.already_protected = self.allocations.len();
199 Ok(())
200 }
201
202 fn non_protected_allocations_iter(&self) -> impl Iterator<Item = &PtrLen> {
204 let iter = self.allocations[self.already_protected..].iter();
205
206 #[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))]
207 return iter.filter(|&PtrLen { map, len, .. }| *len != 0 && map.is_some());
208
209 #[cfg(any(target_os = "windows", not(feature = "selinux-fix")))]
210 return iter.filter(|&PtrLen { len, .. }| *len != 0);
211 }
212
213 pub(crate) unsafe fn free_memory(&mut self) {
216 self.allocations.clear();
217 self.already_protected = 0;
218 }
219}
220
221impl Drop for Memory {
222 fn drop(&mut self) {
223 mem::replace(&mut self.allocations, Vec::new())
225 .into_iter()
226 .for_each(mem::forget);
227 }
228}
229
230pub struct SystemMemoryProvider {
237 code: Memory,
238 readonly: Memory,
239 writable: Memory,
240}
241
242impl SystemMemoryProvider {
243 pub fn new() -> Self {
245 Self {
246 code: Memory::new(),
247 readonly: Memory::new(),
248 writable: Memory::new(),
249 }
250 }
251}
252
253impl JITMemoryProvider for SystemMemoryProvider {
254 unsafe fn free_memory(&mut self) {
255 self.code.free_memory();
256 self.readonly.free_memory();
257 self.writable.free_memory();
258 }
259
260 fn finalize(&mut self, branch_protection: BranchProtection) -> ModuleResult<()> {
261 self.readonly.set_readonly()?;
262 self.code.set_readable_and_executable(branch_protection)
263 }
264
265 fn allocate(&mut self, size: usize, align: u64, kind: JITMemoryKind) -> io::Result<*mut u8> {
266 match kind {
267 JITMemoryKind::Executable => self.code.allocate(size, align),
268 JITMemoryKind::Writable => self.writable.allocate(size, align),
269 JITMemoryKind::ReadOnly => self.readonly.allocate(size, align),
270 }
271 }
272}