wasmtime/runtime/vm/memory/
malloc.rs1use crate::prelude::*;
8use crate::runtime::vm::SendSyncPtr;
9use crate::runtime::vm::memory::{MemoryBase, RuntimeLinearMemory};
10use core::mem;
11use core::ptr::NonNull;
12use wasmtime_environ::Tunables;
13
14#[repr(C, align(16))]
15#[derive(Copy, Clone)]
16pub struct Align16(u128);
17
18pub struct MallocMemory {
20 storage: Vec<Align16>,
21 base_ptr: SendSyncPtr<u8>,
22 byte_len: usize,
23}
24
25impl 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
61impl 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 fn vmmemory(&self) -> crate::vm::VMMemoryDefinition {
88 let base = self.base_ptr.as_non_null();
89 crate::vm::VMMemoryDefinition {
90 base: base.into(),
91 current_length: self.byte_len.into(),
92 }
93 }
94}
95
96fn byte_size_to_element_len(byte_size: usize) -> usize {
97 let align = mem::align_of::<Align16>();
98
99 let byte_size_rounded_up =
101 byte_size.checked_add(align - 1).unwrap_or(usize::MAX) & !(align - 1);
102
103 byte_size_rounded_up / align
106}
107
108fn grow_storage_to(storage: &mut Vec<Align16>, new_len: usize) {
114 debug_assert!(new_len > storage.len());
115 assert!(new_len <= storage.capacity());
116 let capacity_to_set = new_len - storage.len();
117 let slice_to_initialize = &mut storage.spare_capacity_mut()[..capacity_to_set];
118 let byte_size = mem::size_of_val(slice_to_initialize);
119
120 unsafe {
131 core::ptr::write_bytes(slice_to_initialize.as_mut_ptr().cast::<u8>(), 0, byte_size);
132 storage.set_len(new_len);
133 }
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139
140 const TY: wasmtime_environ::Memory = wasmtime_environ::Memory {
144 idx_type: wasmtime_environ::IndexType::I32,
145 limits: wasmtime_environ::Limits { min: 0, max: None },
146 shared: false,
147 page_size_log2: 16,
148 };
149
150 const TUNABLES: Tunables = Tunables {
152 memory_reservation: 0,
153 memory_guard_size: 0,
154 memory_init_cow: false,
155 ..Tunables::default_miri()
156 };
157
158 #[test]
159 fn simple() {
160 let mut memory = MallocMemory::new(&TY, &TUNABLES, 10).unwrap();
161 assert_eq!(memory.storage.len(), 1);
162 assert_valid(&memory);
163
164 memory.grow_to(11).unwrap();
165 assert_eq!(memory.storage.len(), 1);
166 assert_valid(&memory);
167
168 memory.grow_to(16).unwrap();
169 assert_eq!(memory.storage.len(), 1);
170 assert_valid(&memory);
171
172 memory.grow_to(17).unwrap();
173 assert_eq!(memory.storage.len(), 2);
174 assert_valid(&memory);
175
176 memory.grow_to(65).unwrap();
177 assert_eq!(memory.storage.len(), 5);
178 assert_valid(&memory);
179 }
180
181 #[test]
182 fn reservation_not_initialized() {
183 let tunables = Tunables {
184 memory_reservation_for_growth: 1 << 20,
185 ..TUNABLES
186 };
187 let mut memory = MallocMemory::new(&TY, &tunables, 10).unwrap();
188 assert_eq!(memory.storage.len(), 1);
189 assert_eq!(
190 memory.storage.capacity(),
191 (tunables.memory_reservation_for_growth / 16) as usize + 1,
192 );
193 assert_valid(&memory);
194
195 memory.grow_to(100).unwrap();
196 assert_eq!(memory.storage.len(), 7);
197 assert_eq!(
198 memory.storage.capacity(),
199 (tunables.memory_reservation_for_growth / 16) as usize + 1,
200 );
201 assert_valid(&memory);
202 }
203
204 fn assert_valid(mem: &MallocMemory) {
205 assert_eq!(mem.storage.as_ptr().cast::<u8>(), mem.base_ptr.as_ptr());
206 assert!(mem.byte_len <= mem.storage.len() * 16);
207 for slot in mem.storage.iter() {
208 assert_eq!(slot.0, 0);
209 }
210 }
211}