substrate_wasmtime_runtime/
memory.rs1use crate::mmap::Mmap;
6use crate::vmcontext::VMMemoryDefinition;
7use more_asserts::{assert_ge, assert_le};
8use std::cell::RefCell;
9use std::convert::TryFrom;
10use wasmtime_environ::{MemoryPlan, MemoryStyle, WASM_MAX_PAGES, WASM_PAGE_SIZE};
11
12pub trait RuntimeMemoryCreator: Send + Sync {
14 fn new_memory(&self, plan: &MemoryPlan) -> Result<Box<dyn RuntimeLinearMemory>, String>;
16}
17
18pub struct DefaultMemoryCreator;
20
21impl RuntimeMemoryCreator for DefaultMemoryCreator {
22 fn new_memory(&self, plan: &MemoryPlan) -> Result<Box<dyn RuntimeLinearMemory>, String> {
24 Ok(Box::new(MmapMemory::new(plan)?) as Box<dyn RuntimeLinearMemory>)
25 }
26}
27
28pub trait RuntimeLinearMemory {
30 fn size(&self) -> u32;
32
33 fn grow(&self, delta: u32) -> Option<u32>;
38
39 fn vmmemory(&self) -> VMMemoryDefinition;
41}
42
43#[derive(Debug)]
45pub struct MmapMemory {
46 mmap: RefCell<WasmMmap>,
48
49 maximum: Option<u32>,
51
52 offset_guard_size: usize,
55
56 pub(crate) needs_signal_handlers: bool,
59}
60
61#[derive(Debug)]
62struct WasmMmap {
63 alloc: Mmap,
65 size: u32,
67}
68
69impl MmapMemory {
70 pub fn new(plan: &MemoryPlan) -> Result<Self, String> {
72 assert_le!(plan.memory.minimum, WASM_MAX_PAGES);
74 assert!(plan.memory.maximum.is_none() || plan.memory.maximum.unwrap() <= WASM_MAX_PAGES);
75
76 let offset_guard_bytes = plan.offset_guard_size as usize;
77
78 let needs_signal_handlers = offset_guard_bytes > 0
82 || match plan.style {
83 MemoryStyle::Dynamic => false,
84 MemoryStyle::Static { .. } => true,
85 };
86
87 let minimum_pages = match plan.style {
88 MemoryStyle::Dynamic => plan.memory.minimum,
89 MemoryStyle::Static { bound } => {
90 assert_ge!(bound, plan.memory.minimum);
91 bound
92 }
93 } as usize;
94 let minimum_bytes = minimum_pages.checked_mul(WASM_PAGE_SIZE as usize).unwrap();
95 let request_bytes = minimum_bytes.checked_add(offset_guard_bytes).unwrap();
96 let mapped_pages = plan.memory.minimum as usize;
97 let mapped_bytes = mapped_pages * WASM_PAGE_SIZE as usize;
98
99 let mmap = WasmMmap {
100 alloc: Mmap::accessible_reserved(mapped_bytes, request_bytes)?,
101 size: plan.memory.minimum,
102 };
103
104 Ok(Self {
105 mmap: mmap.into(),
106 maximum: plan.memory.maximum,
107 offset_guard_size: offset_guard_bytes,
108 needs_signal_handlers,
109 })
110 }
111}
112
113impl RuntimeLinearMemory for MmapMemory {
114 fn size(&self) -> u32 {
116 self.mmap.borrow().size
117 }
118
119 fn grow(&self, delta: u32) -> Option<u32> {
124 let mut mmap = self.mmap.borrow_mut();
126 if delta == 0 {
127 return Some(mmap.size);
128 }
129
130 let new_pages = match mmap.size.checked_add(delta) {
131 Some(new_pages) => new_pages,
132 None => return None,
134 };
135 let prev_pages = mmap.size;
136
137 if let Some(maximum) = self.maximum {
138 if new_pages > maximum {
139 return None;
141 }
142 }
143
144 if new_pages >= WASM_MAX_PAGES {
148 return None;
150 }
151
152 let delta_bytes = usize::try_from(delta).unwrap() * WASM_PAGE_SIZE as usize;
153 let prev_bytes = usize::try_from(prev_pages).unwrap() * WASM_PAGE_SIZE as usize;
154 let new_bytes = usize::try_from(new_pages).unwrap() * WASM_PAGE_SIZE as usize;
155
156 if new_bytes > mmap.alloc.len() - self.offset_guard_size {
157 let guard_bytes = self.offset_guard_size;
160 let request_bytes = new_bytes.checked_add(guard_bytes)?;
161
162 let mut new_mmap = Mmap::accessible_reserved(new_bytes, request_bytes).ok()?;
163
164 let copy_len = mmap.alloc.len() - self.offset_guard_size;
165 new_mmap.as_mut_slice()[..copy_len].copy_from_slice(&mmap.alloc.as_slice()[..copy_len]);
166
167 mmap.alloc = new_mmap;
168 } else if delta_bytes > 0 {
169 mmap.alloc.make_accessible(prev_bytes, delta_bytes).ok()?;
171 }
172
173 mmap.size = new_pages;
174
175 Some(prev_pages)
176 }
177
178 fn vmmemory(&self) -> VMMemoryDefinition {
180 let mut mmap = self.mmap.borrow_mut();
181 VMMemoryDefinition {
182 base: mmap.alloc.as_mut_ptr(),
183 current_length: mmap.size as usize * WASM_PAGE_SIZE as usize,
184 }
185 }
186}