1 //! Support for implementing the [`RuntimeLinearMemory`] trait in terms of a 2 //! platform memory allocation primitive (e.g. `malloc`) 3 //! 4 //! Note that memory is allocated here using `Vec::try_reserve` to explicitly 5 //! handle memory allocation failures. 6 7 use crate::prelude::*; 8 use crate::runtime::vm::memory::{MemoryBase, RuntimeLinearMemory}; 9 use crate::runtime::vm::SendSyncPtr; 10 use core::mem; 11 use core::ptr::NonNull; 12 use wasmtime_environ::Tunables; 13 14 #[repr(C, align(16))] 15 #[derive(Copy, Clone)] 16 pub struct Align16(u128); 17 18 /// An instance of linear memory backed by the default system allocator. 19 pub struct MallocMemory { 20 storage: Vec<Align16>, 21 base_ptr: SendSyncPtr<u8>, 22 byte_len: usize, 23 } 24 25 impl MallocMemory { 26 pub fn new( 27 _ty: &wasmtime_environ::Memory, 28 tunables: &Tunables, 29 minimum: usize, 30 ) -> Result<Self> { 31 if tunables.memory_guard_size > 0 { 32 bail!("malloc memory is only compatible if guard pages aren't used"); 33 } 34 if tunables.memory_reservation > 0 { 35 bail!("malloc memory is only compatible with no ahead-of-time memory reservation"); 36 } 37 if tunables.memory_init_cow { 38 bail!("malloc memory cannot be used with CoW images"); 39 } 40 41 let initial_allocation_byte_size = minimum 42 .checked_add(tunables.memory_reservation_for_growth.try_into()?) 43 .context("memory allocation size too large")?; 44 45 let initial_allocation_len = byte_size_to_element_len(initial_allocation_byte_size); 46 let mut storage = Vec::new(); 47 storage.try_reserve(initial_allocation_len)?; 48 49 let initial_len = byte_size_to_element_len(minimum); 50 if initial_len > 0 { 51 grow_storage_to(&mut storage, initial_len); 52 } 53 Ok(MallocMemory { 54 base_ptr: SendSyncPtr::new(NonNull::new(storage.as_mut_ptr()).unwrap()).cast(), 55 storage, 56 byte_len: minimum, 57 }) 58 } 59 } 60 61 impl RuntimeLinearMemory for MallocMemory { 62 fn byte_size(&self) -> usize { 63 self.byte_len 64 } 65 66 fn byte_capacity(&self) -> usize { 67 self.storage.capacity() * mem::size_of::<Align16>() 68 } 69 70 fn grow_to(&mut self, new_size: usize) -> Result<()> { 71 let new_element_len = byte_size_to_element_len(new_size); 72 if new_element_len > self.storage.len() { 73 self.storage 74 .try_reserve(new_element_len - self.storage.len())?; 75 grow_storage_to(&mut self.storage, new_element_len); 76 self.base_ptr = 77 SendSyncPtr::new(NonNull::new(self.storage.as_mut_ptr()).unwrap()).cast(); 78 } 79 self.byte_len = new_size; 80 Ok(()) 81 } 82 83 fn base(&self) -> MemoryBase { 84 MemoryBase::Raw(self.base_ptr) 85 } 86 } 87 88 fn byte_size_to_element_len(byte_size: usize) -> usize { 89 let align = mem::align_of::<Align16>(); 90 91 // Round up the requested byte size to the size of each vector element. 92 let byte_size_rounded_up = 93 byte_size.checked_add(align - 1).unwrap_or(usize::MAX) & !(align - 1); 94 95 // Next divide this aligned size by the size of each element to get the 96 // element length of our vector. 97 byte_size_rounded_up / align 98 } 99 100 /// Helper that is the equivalent of `storage.resize(new_len, Align16(0))` 101 /// except it's also optimized to perform well in debug mode. Just using 102 /// `resize` leads to a per-element iteration which can be quite slow in debug 103 /// mode as it's not optimized to a memcpy, so it's manually optimized here 104 /// instead. 105 fn grow_storage_to(storage: &mut Vec<Align16>, new_len: usize) { 106 debug_assert!(new_len > storage.len()); 107 assert!(new_len <= storage.capacity()); 108 let capacity_to_set = new_len - storage.len(); 109 let slice_to_initialize = &mut storage.spare_capacity_mut()[..capacity_to_set]; 110 let byte_size = mem::size_of_val(slice_to_initialize); 111 112 // SAFETY: The `slice_to_initialize` is guaranteed to be in the capacity of 113 // the vector via the slicing above, so it's all owned memory by the 114 // vector. Additionally the `byte_size` is the exact size of the 115 // `slice_to_initialize` itself, so this `memset` should be in-bounds. 116 // Finally the `Align16` is a simple wrapper around `u128` for which 0 117 // is a valid byte pattern. This should make the initial `write_bytes` safe. 118 // 119 // Afterwards the `set_len` call should also be safe because we've 120 // initialized the tail end of the vector with zeros so it's safe to 121 // consider it having a new length now. 122 unsafe { 123 core::ptr::write_bytes(slice_to_initialize.as_mut_ptr().cast::<u8>(), 0, byte_size); 124 storage.set_len(new_len); 125 } 126 } 127 128 #[cfg(test)] 129 mod tests { 130 use super::*; 131 132 // This is currently required by the constructor but otherwise ignored in 133 // the creation of a `MallocMemory`, so just have a single one used in 134 // tests below. 135 const TY: wasmtime_environ::Memory = wasmtime_environ::Memory { 136 idx_type: wasmtime_environ::IndexType::I32, 137 limits: wasmtime_environ::Limits { min: 0, max: None }, 138 shared: false, 139 page_size_log2: 16, 140 }; 141 142 // Valid tunables that can be used to create a `MallocMemory`. 143 const TUNABLES: Tunables = Tunables { 144 memory_reservation: 0, 145 memory_guard_size: 0, 146 memory_init_cow: false, 147 ..Tunables::default_miri() 148 }; 149 150 #[test] 151 fn simple() { 152 let mut memory = MallocMemory::new(&TY, &TUNABLES, 10).unwrap(); 153 assert_eq!(memory.storage.len(), 1); 154 assert_valid(&memory); 155 156 memory.grow_to(11).unwrap(); 157 assert_eq!(memory.storage.len(), 1); 158 assert_valid(&memory); 159 160 memory.grow_to(16).unwrap(); 161 assert_eq!(memory.storage.len(), 1); 162 assert_valid(&memory); 163 164 memory.grow_to(17).unwrap(); 165 assert_eq!(memory.storage.len(), 2); 166 assert_valid(&memory); 167 168 memory.grow_to(65).unwrap(); 169 assert_eq!(memory.storage.len(), 5); 170 assert_valid(&memory); 171 } 172 173 #[test] 174 fn reservation_not_initialized() { 175 let tunables = Tunables { 176 memory_reservation_for_growth: 1 << 20, 177 ..TUNABLES 178 }; 179 let mut memory = MallocMemory::new(&TY, &tunables, 10).unwrap(); 180 assert_eq!(memory.storage.len(), 1); 181 assert_eq!( 182 memory.storage.capacity(), 183 (tunables.memory_reservation_for_growth / 16) as usize + 1, 184 ); 185 assert_valid(&memory); 186 187 memory.grow_to(100).unwrap(); 188 assert_eq!(memory.storage.len(), 7); 189 assert_eq!( 190 memory.storage.capacity(), 191 (tunables.memory_reservation_for_growth / 16) as usize + 1, 192 ); 193 assert_valid(&memory); 194 } 195 196 fn assert_valid(mem: &MallocMemory) { 197 assert_eq!(mem.storage.as_ptr().cast::<u8>(), mem.base_ptr.as_ptr()); 198 assert!(mem.byte_len <= mem.storage.len() * 16); 199 for slot in mem.storage.iter() { 200 assert_eq!(slot.0, 0); 201 } 202 } 203 } 204