1 use cranelift_module::{ModuleError, ModuleResult}; 2 3 #[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))] 4 use memmap2::MmapMut; 5 6 #[cfg(not(any(feature = "selinux-fix", windows)))] 7 use std::alloc; 8 use std::io; 9 use std::mem; 10 use std::ptr; 11 12 use super::BranchProtection; 13 use super::JITMemoryProvider; 14 15 /// A simple struct consisting of a pointer and length. 16 struct PtrLen { 17 #[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))] 18 map: Option<MmapMut>, 19 20 ptr: *mut u8, 21 len: usize, 22 } 23 24 impl PtrLen { 25 /// Create a new empty `PtrLen`. 26 fn new() -> Self { 27 Self { 28 #[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))] 29 map: None, 30 31 ptr: ptr::null_mut(), 32 len: 0, 33 } 34 } 35 36 /// Create a new `PtrLen` pointing to at least `size` bytes of memory, 37 /// suitably sized and aligned for memory protection. 38 #[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))] 39 fn with_size(size: usize) -> io::Result<Self> { 40 let alloc_size = region::page::ceil(size as *const ()) as usize; 41 MmapMut::map_anon(alloc_size).map(|mut mmap| { 42 // The order here is important; we assign the pointer first to get 43 // around compile time borrow errors. 44 Self { 45 ptr: mmap.as_mut_ptr(), 46 map: Some(mmap), 47 len: alloc_size, 48 } 49 }) 50 } 51 52 #[cfg(all(not(target_os = "windows"), not(feature = "selinux-fix")))] 53 fn with_size(size: usize) -> io::Result<Self> { 54 assert_ne!(size, 0); 55 let page_size = region::page::size(); 56 let alloc_size = region::page::ceil(size as *const ()) as usize; 57 let layout = alloc::Layout::from_size_align(alloc_size, page_size).unwrap(); 58 // Safety: We assert that the size is non-zero above. 59 let ptr = unsafe { alloc::alloc(layout) }; 60 61 if !ptr.is_null() { 62 Ok(Self { 63 ptr, 64 len: alloc_size, 65 }) 66 } else { 67 Err(io::Error::from(io::ErrorKind::OutOfMemory)) 68 } 69 } 70 71 #[cfg(target_os = "windows")] 72 fn with_size(size: usize) -> io::Result<Self> { 73 use windows_sys::Win32::System::Memory::{ 74 MEM_COMMIT, MEM_RESERVE, PAGE_READWRITE, VirtualAlloc, 75 }; 76 77 // VirtualAlloc always rounds up to the next multiple of the page size 78 let ptr = unsafe { 79 VirtualAlloc( 80 ptr::null_mut(), 81 size, 82 MEM_COMMIT | MEM_RESERVE, 83 PAGE_READWRITE, 84 ) 85 }; 86 if !ptr.is_null() { 87 Ok(Self { 88 ptr: ptr as *mut u8, 89 len: region::page::ceil(size as *const ()) as usize, 90 }) 91 } else { 92 Err(io::Error::last_os_error()) 93 } 94 } 95 } 96 97 // `MMapMut` from `cfg(feature = "selinux-fix")` already deallocates properly. 98 #[cfg(all(not(target_os = "windows"), not(feature = "selinux-fix")))] 99 impl Drop for PtrLen { 100 fn drop(&mut self) { 101 if !self.ptr.is_null() { 102 let page_size = region::page::size(); 103 let layout = alloc::Layout::from_size_align(self.len, page_size).unwrap(); 104 unsafe { 105 region::protect(self.ptr, self.len, region::Protection::READ_WRITE) 106 .expect("unable to unprotect memory"); 107 alloc::dealloc(self.ptr, layout) 108 } 109 } 110 } 111 } 112 113 // TODO: add a `Drop` impl for `cfg(target_os = "windows")` 114 115 /// JIT memory manager. This manages pages of suitably aligned and 116 /// accessible memory. Memory will be leaked by default to have 117 /// function pointers remain valid for the remainder of the 118 /// program's life. 119 pub(crate) struct Memory { 120 allocations: Vec<PtrLen>, 121 already_protected: usize, 122 current: PtrLen, 123 position: usize, 124 } 125 126 unsafe impl Send for Memory {} 127 128 impl Memory { 129 pub(crate) fn new() -> Self { 130 Self { 131 allocations: Vec::new(), 132 already_protected: 0, 133 current: PtrLen::new(), 134 position: 0, 135 } 136 } 137 138 fn finish_current(&mut self) { 139 self.allocations 140 .push(mem::replace(&mut self.current, PtrLen::new())); 141 self.position = 0; 142 } 143 144 pub(crate) fn allocate(&mut self, size: usize, align: u64) -> io::Result<*mut u8> { 145 let align = usize::try_from(align).expect("alignment too big"); 146 if self.position % align != 0 { 147 self.position += align - self.position % align; 148 debug_assert!(self.position % align == 0); 149 } 150 151 if size <= self.current.len - self.position { 152 // TODO: Ensure overflow is not possible. 153 let ptr = unsafe { self.current.ptr.add(self.position) }; 154 self.position += size; 155 return Ok(ptr); 156 } 157 158 self.finish_current(); 159 160 // TODO: Allocate more at a time. 161 self.current = PtrLen::with_size(size)?; 162 self.position = size; 163 164 Ok(self.current.ptr) 165 } 166 167 /// Set all memory allocated in this `Memory` up to now as readable and executable. 168 pub(crate) fn set_readable_and_executable( 169 &mut self, 170 branch_protection: BranchProtection, 171 ) -> ModuleResult<()> { 172 self.finish_current(); 173 174 for &PtrLen { ptr, len, .. } in self.non_protected_allocations_iter() { 175 super::set_readable_and_executable(ptr, len, branch_protection)?; 176 } 177 178 // Flush any in-flight instructions from the pipeline 179 wasmtime_jit_icache_coherence::pipeline_flush_mt().expect("Failed pipeline flush"); 180 181 self.already_protected = self.allocations.len(); 182 Ok(()) 183 } 184 185 /// Set all memory allocated in this `Memory` up to now as readonly. 186 pub(crate) fn set_readonly(&mut self) -> ModuleResult<()> { 187 self.finish_current(); 188 189 for &PtrLen { ptr, len, .. } in self.non_protected_allocations_iter() { 190 unsafe { 191 region::protect(ptr, len, region::Protection::READ).map_err(|e| { 192 ModuleError::Backend( 193 anyhow::Error::new(e).context("unable to make memory readonly"), 194 ) 195 })?; 196 } 197 } 198 199 self.already_protected = self.allocations.len(); 200 Ok(()) 201 } 202 203 /// Iterates non protected memory allocations that are of not zero bytes in size. 204 fn non_protected_allocations_iter(&self) -> impl Iterator<Item = &PtrLen> { 205 let iter = self.allocations[self.already_protected..].iter(); 206 207 #[cfg(all(not(target_os = "windows"), feature = "selinux-fix"))] 208 return iter.filter(|&PtrLen { map, len, .. }| *len != 0 && map.is_some()); 209 210 #[cfg(any(target_os = "windows", not(feature = "selinux-fix")))] 211 return iter.filter(|&PtrLen { len, .. }| *len != 0); 212 } 213 214 /// Frees all allocated memory regions that would be leaked otherwise. 215 /// Likely to invalidate existing function pointers, causing unsafety. 216 pub(crate) unsafe fn free_memory(&mut self) { 217 self.allocations.clear(); 218 self.already_protected = 0; 219 } 220 } 221 222 impl Drop for Memory { 223 fn drop(&mut self) { 224 // leak memory to guarantee validity of function pointers 225 mem::replace(&mut self.allocations, Vec::new()) 226 .into_iter() 227 .for_each(mem::forget); 228 } 229 } 230 231 /// A memory provider that allocates memory on-demand using the system 232 /// allocator. 233 /// 234 /// Note: Memory will be leaked by default unless 235 /// [`JITMemoryProvider::free_memory`] is called to ensure function pointers 236 /// remain valid for the remainder of the program's life. 237 pub struct SystemMemoryProvider { 238 code: Memory, 239 readonly: Memory, 240 writable: Memory, 241 } 242 243 impl SystemMemoryProvider { 244 /// Create a new memory handle with the given branch protection. 245 pub fn new() -> Self { 246 Self { 247 code: Memory::new(), 248 readonly: Memory::new(), 249 writable: Memory::new(), 250 } 251 } 252 } 253 254 impl JITMemoryProvider for SystemMemoryProvider { 255 unsafe fn free_memory(&mut self) { 256 self.code.free_memory(); 257 self.readonly.free_memory(); 258 self.writable.free_memory(); 259 } 260 261 fn finalize(&mut self, branch_protection: BranchProtection) -> ModuleResult<()> { 262 self.readonly.set_readonly()?; 263 self.code.set_readable_and_executable(branch_protection) 264 } 265 266 fn allocate_readexec(&mut self, size: usize, align: u64) -> io::Result<*mut u8> { 267 self.code.allocate(size, align) 268 } 269 270 fn allocate_readwrite(&mut self, size: usize, align: u64) -> io::Result<*mut u8> { 271 self.writable.allocate(size, align) 272 } 273 274 fn allocate_readonly(&mut self, size: usize, align: u64) -> io::Result<*mut u8> { 275 self.readonly.allocate(size, align) 276 } 277 } 278