1use std::{cell::RefCell, str, sync::Arc};
22
23use log::{error, trace};
24use wasmi::{
25 memory_units::Pages,
26 FuncInstance, ImportsBuilder, MemoryRef, Module, ModuleInstance, ModuleRef,
27 RuntimeValue::{self, I32, I64},
28 TableRef,
29};
30
31use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator};
32use sc_executor_common::{
33 error::{Error, MessageWithBacktrace, WasmError},
34 runtime_blob::{DataSegmentsSnapshot, RuntimeBlob},
35 wasm_runtime::{HeapAllocStrategy, InvokeMethod, WasmInstance, WasmModule},
36};
37use sp_runtime_interface::unpack_ptr_and_len;
38use sp_wasm_interface::{Function, FunctionContext, Pointer, Result as WResult, WordSize};
39
40struct MemoryWrapper<'a>(&'a MemoryRef);
42
43impl sc_allocator::Memory for MemoryWrapper<'_> {
44 fn with_access_mut<R>(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R {
45 self.0.with_direct_access_mut(run)
46 }
47
48 fn with_access<R>(&self, run: impl FnOnce(&[u8]) -> R) -> R {
49 self.0.with_direct_access(run)
50 }
51
52 fn pages(&self) -> u32 {
53 self.0.current_size().0 as _
54 }
55
56 fn max_pages(&self) -> Option<u32> {
57 self.0.maximum().map(|p| p.0 as _)
58 }
59
60 fn grow(&mut self, additional: u32) -> Result<(), ()> {
61 self.0
62 .grow(Pages(additional as _))
63 .map_err(|e| {
64 log::error!(
65 target: "wasm-executor",
66 "Failed to grow memory by {} pages: {}",
67 additional,
68 e,
69 )
70 })
71 .map(drop)
72 }
73}
74
75struct FunctionExecutor {
76 heap: RefCell<sc_allocator::FreeingBumpHeapAllocator>,
77 memory: MemoryRef,
78 host_functions: Arc<Vec<&'static dyn Function>>,
79 allow_missing_func_imports: bool,
80 missing_functions: Arc<Vec<String>>,
81 panic_message: Option<String>,
82}
83
84impl FunctionExecutor {
85 fn new(
86 m: MemoryRef,
87 heap_base: u32,
88 host_functions: Arc<Vec<&'static dyn Function>>,
89 allow_missing_func_imports: bool,
90 missing_functions: Arc<Vec<String>>,
91 ) -> Result<Self, Error> {
92 Ok(FunctionExecutor {
93 heap: RefCell::new(FreeingBumpHeapAllocator::new(heap_base)),
94 memory: m,
95 host_functions,
96 allow_missing_func_imports,
97 missing_functions,
98 panic_message: None,
99 })
100 }
101}
102
103impl FunctionContext for FunctionExecutor {
104 fn read_memory_into(&self, address: Pointer<u8>, dest: &mut [u8]) -> WResult<()> {
105 self.memory.get_into(address.into(), dest).map_err(|e| e.to_string())
106 }
107
108 fn write_memory(&mut self, address: Pointer<u8>, data: &[u8]) -> WResult<()> {
109 self.memory.set(address.into(), data).map_err(|e| e.to_string())
110 }
111
112 fn allocate_memory(&mut self, size: WordSize) -> WResult<Pointer<u8>> {
113 self.heap
114 .borrow_mut()
115 .allocate(&mut MemoryWrapper(&self.memory), size)
116 .map_err(|e| e.to_string())
117 }
118
119 fn deallocate_memory(&mut self, ptr: Pointer<u8>) -> WResult<()> {
120 self.heap
121 .borrow_mut()
122 .deallocate(&mut MemoryWrapper(&self.memory), ptr)
123 .map_err(|e| e.to_string())
124 }
125
126 fn register_panic_error_message(&mut self, message: &str) {
127 self.panic_message = Some(message.to_owned());
128 }
129}
130
131struct Resolver<'a> {
133 host_functions: &'a [&'static dyn Function],
135 allow_missing_func_imports: bool,
139 missing_functions: RefCell<Vec<String>>,
141}
142
143impl<'a> Resolver<'a> {
144 fn new(
145 host_functions: &'a [&'static dyn Function],
146 allow_missing_func_imports: bool,
147 ) -> Resolver<'a> {
148 Resolver {
149 host_functions,
150 allow_missing_func_imports,
151 missing_functions: RefCell::new(Vec::new()),
152 }
153 }
154}
155
156impl<'a> wasmi::ModuleImportResolver for Resolver<'a> {
157 fn resolve_func(
158 &self,
159 name: &str,
160 signature: &wasmi::Signature,
161 ) -> std::result::Result<wasmi::FuncRef, wasmi::Error> {
162 let signature = sp_wasm_interface::Signature::from(signature);
163 for (function_index, function) in self.host_functions.iter().enumerate() {
164 if name == function.name() {
165 if signature == function.signature() {
166 return Ok(wasmi::FuncInstance::alloc_host(signature.into(), function_index))
167 } else {
168 return Err(wasmi::Error::Instantiation(format!(
169 "Invalid signature for function `{}` expected `{:?}`, got `{:?}`",
170 function.name(),
171 signature,
172 function.signature(),
173 )))
174 }
175 }
176 }
177
178 if self.allow_missing_func_imports {
179 trace!(
180 target: "wasm-executor",
181 "Could not find function `{}`, a stub will be provided instead.",
182 name,
183 );
184 let id = self.missing_functions.borrow().len() + self.host_functions.len();
185 self.missing_functions.borrow_mut().push(name.to_string());
186
187 Ok(wasmi::FuncInstance::alloc_host(signature.into(), id))
188 } else {
189 Err(wasmi::Error::Instantiation(format!("Export {} not found", name)))
190 }
191 }
192
193 fn resolve_memory(
194 &self,
195 _: &str,
196 _: &wasmi::MemoryDescriptor,
197 ) -> Result<MemoryRef, wasmi::Error> {
198 Err(wasmi::Error::Instantiation(
199 "Internal error, wasmi expects that the wasm blob exports memory.".into(),
200 ))
201 }
202}
203
204impl wasmi::Externals for FunctionExecutor {
205 fn invoke_index(
206 &mut self,
207 index: usize,
208 args: wasmi::RuntimeArgs,
209 ) -> Result<Option<wasmi::RuntimeValue>, wasmi::Trap> {
210 let mut args = args.as_ref().iter().copied().map(Into::into);
211
212 if let Some(function) = self.host_functions.clone().get(index) {
213 function
214 .execute(self, &mut args)
215 .map_err(|msg| Error::FunctionExecution(function.name().to_string(), msg))
216 .map_err(wasmi::Trap::from)
217 .map(|v| v.map(Into::into))
218 } else if self.allow_missing_func_imports &&
219 index >= self.host_functions.len() &&
220 index < self.host_functions.len() + self.missing_functions.len()
221 {
222 Err(Error::from(format!(
223 "Function `{}` is only a stub. Calling a stub is not allowed.",
224 self.missing_functions[index - self.host_functions.len()],
225 ))
226 .into())
227 } else {
228 Err(Error::from(format!("Could not find host function with index: {}", index)).into())
229 }
230 }
231}
232
233fn get_mem_instance(module: &ModuleRef) -> Result<MemoryRef, Error> {
234 Ok(module
235 .export_by_name("memory")
236 .ok_or(Error::InvalidMemoryReference)?
237 .as_memory()
238 .ok_or(Error::InvalidMemoryReference)?
239 .clone())
240}
241
242fn get_heap_base(module: &ModuleRef) -> Result<u32, Error> {
245 let heap_base_val = module
246 .export_by_name("__heap_base")
247 .ok_or(Error::HeapBaseNotFoundOrInvalid)?
248 .as_global()
249 .ok_or(Error::HeapBaseNotFoundOrInvalid)?
250 .get();
251
252 match heap_base_val {
253 wasmi::RuntimeValue::I32(v) => Ok(v as u32),
254 _ => Err(Error::HeapBaseNotFoundOrInvalid),
255 }
256}
257
258fn call_in_wasm_module(
260 module_instance: &ModuleRef,
261 memory: &MemoryRef,
262 method: InvokeMethod,
263 data: &[u8],
264 host_functions: Arc<Vec<&'static dyn Function>>,
265 allow_missing_func_imports: bool,
266 missing_functions: Arc<Vec<String>>,
267 allocation_stats: &mut Option<AllocationStats>,
268) -> Result<Vec<u8>, Error> {
269 let table: Option<TableRef> = module_instance
271 .export_by_name("__indirect_function_table")
272 .and_then(|e| e.as_table().cloned());
273 let heap_base = get_heap_base(module_instance)?;
274
275 let mut function_executor = FunctionExecutor::new(
276 memory.clone(),
277 heap_base,
278 host_functions,
279 allow_missing_func_imports,
280 missing_functions,
281 )?;
282
283 let offset = function_executor.allocate_memory(data.len() as u32)?;
285 function_executor.write_memory(offset, data)?;
286
287 fn convert_trap(executor: &mut FunctionExecutor, trap: wasmi::Trap) -> Error {
288 if let Some(message) = executor.panic_message.take() {
289 Error::AbortedDueToPanic(MessageWithBacktrace { message, backtrace: None })
290 } else {
291 Error::AbortedDueToTrap(MessageWithBacktrace {
292 message: trap.to_string(),
293 backtrace: None,
294 })
295 }
296 }
297
298 let result = match method {
299 InvokeMethod::Export(method) => module_instance
300 .invoke_export(
301 method,
302 &[I32(u32::from(offset) as i32), I32(data.len() as i32)],
303 &mut function_executor,
304 )
305 .map_err(|error| {
306 if let wasmi::Error::Trap(trap) = error {
307 convert_trap(&mut function_executor, trap)
308 } else {
309 error.into()
310 }
311 }),
312 InvokeMethod::Table(func_ref) => {
313 let func = table
314 .ok_or(Error::NoTable)?
315 .get(func_ref)?
316 .ok_or(Error::NoTableEntryWithIndex(func_ref))?;
317 FuncInstance::invoke(
318 &func,
319 &[I32(u32::from(offset) as i32), I32(data.len() as i32)],
320 &mut function_executor,
321 )
322 .map_err(|trap| convert_trap(&mut function_executor, trap))
323 },
324 InvokeMethod::TableWithWrapper { dispatcher_ref, func } => {
325 let dispatcher = table
326 .ok_or(Error::NoTable)?
327 .get(dispatcher_ref)?
328 .ok_or(Error::NoTableEntryWithIndex(dispatcher_ref))?;
329
330 FuncInstance::invoke(
331 &dispatcher,
332 &[I32(func as _), I32(u32::from(offset) as i32), I32(data.len() as i32)],
333 &mut function_executor,
334 )
335 .map_err(|trap| convert_trap(&mut function_executor, trap))
336 },
337 };
338
339 *allocation_stats = Some(function_executor.heap.borrow().stats());
340
341 match result {
342 Ok(Some(I64(r))) => {
343 let (ptr, length) = unpack_ptr_and_len(r as u64);
344 #[allow(deprecated)]
345 memory.get(ptr, length as usize).map_err(|_| Error::Runtime)
346 },
347 Err(e) => {
348 trace!(
349 target: "wasm-executor",
350 "Failed to execute code with {} pages",
351 memory.current_size().0,
352 );
353 Err(e)
354 },
355 _ => Err(Error::InvalidReturn),
356 }
357}
358
359fn instantiate_module(
361 module: &Module,
362 host_functions: &[&'static dyn Function],
363 allow_missing_func_imports: bool,
364) -> Result<(ModuleRef, Vec<String>, MemoryRef), Error> {
365 let resolver = Resolver::new(host_functions, allow_missing_func_imports);
366 let intermediate_instance =
368 ModuleInstance::new(module, &ImportsBuilder::new().with_resolver("env", &resolver))?;
369
370 let _ = get_heap_base(intermediate_instance.not_started_instance())?;
372
373 let memory = get_mem_instance(intermediate_instance.not_started_instance())?;
377
378 if intermediate_instance.has_start() {
379 Err(Error::RuntimeHasStartFn)
381 } else {
382 Ok((
383 intermediate_instance.assert_no_start(),
384 resolver.missing_functions.into_inner(),
385 memory,
386 ))
387 }
388}
389
390#[derive(Clone)]
394struct GlobalValsSnapshot {
395 global_mut_values: Vec<RuntimeValue>,
397}
398
399impl GlobalValsSnapshot {
400 fn take(module_instance: &ModuleRef) -> Self {
402 let global_mut_values = module_instance
404 .globals()
405 .iter()
406 .filter(|g| g.is_mutable())
407 .map(|g| g.get())
408 .collect();
409 Self { global_mut_values }
410 }
411
412 fn apply(&self, instance: &ModuleRef) -> Result<(), WasmError> {
417 for (global_ref, global_val) in instance
418 .globals()
419 .iter()
420 .filter(|g| g.is_mutable())
421 .zip(self.global_mut_values.iter())
422 {
423 global_ref.set(*global_val).map_err(|_| WasmError::ApplySnapshotFailed)?;
427 }
428 Ok(())
429 }
430}
431
432pub struct WasmiRuntime {
434 module: Module,
436 host_functions: Arc<Vec<&'static dyn Function>>,
438 allow_missing_func_imports: bool,
441
442 global_vals_snapshot: GlobalValsSnapshot,
443 data_segments_snapshot: DataSegmentsSnapshot,
444}
445
446impl WasmModule for WasmiRuntime {
447 fn new_instance(&self) -> Result<Box<dyn WasmInstance>, Error> {
448 let (instance, missing_functions, memory) =
450 instantiate_module(&self.module, &self.host_functions, self.allow_missing_func_imports)
451 .map_err(|e| WasmError::Instantiation(e.to_string()))?;
452
453 Ok(Box::new(WasmiInstance {
454 instance,
455 memory,
456 global_vals_snapshot: self.global_vals_snapshot.clone(),
457 data_segments_snapshot: self.data_segments_snapshot.clone(),
458 host_functions: self.host_functions.clone(),
459 allow_missing_func_imports: self.allow_missing_func_imports,
460 missing_functions: Arc::new(missing_functions),
461 memory_zeroed: true,
462 }))
463 }
464}
465
466pub fn create_runtime(
469 mut blob: RuntimeBlob,
470 heap_alloc_strategy: HeapAllocStrategy,
471 host_functions: Vec<&'static dyn Function>,
472 allow_missing_func_imports: bool,
473) -> Result<WasmiRuntime, WasmError> {
474 let data_segments_snapshot =
475 DataSegmentsSnapshot::take(&blob).map_err(|e| WasmError::Other(e.to_string()))?;
476
477 blob.convert_memory_import_into_export()?;
479 blob.setup_memory_according_to_heap_alloc_strategy(heap_alloc_strategy)?;
481
482 let module =
483 Module::from_parity_wasm_module(blob.into_inner()).map_err(|_| WasmError::InvalidModule)?;
484
485 let global_vals_snapshot = {
486 let (instance, _, _) =
487 instantiate_module(&module, &host_functions, allow_missing_func_imports)
488 .map_err(|e| WasmError::Instantiation(e.to_string()))?;
489 GlobalValsSnapshot::take(&instance)
490 };
491
492 Ok(WasmiRuntime {
493 module,
494 data_segments_snapshot,
495 global_vals_snapshot,
496 host_functions: Arc::new(host_functions),
497 allow_missing_func_imports,
498 })
499}
500
501pub struct WasmiInstance {
503 instance: ModuleRef,
505 memory: MemoryRef,
507 memory_zeroed: bool,
509 global_vals_snapshot: GlobalValsSnapshot,
511 data_segments_snapshot: DataSegmentsSnapshot,
513 host_functions: Arc<Vec<&'static dyn Function>>,
515 allow_missing_func_imports: bool,
518 missing_functions: Arc<Vec<String>>,
520}
521
522unsafe impl Send for WasmiInstance {}
525
526impl WasmiInstance {
527 fn call_impl(
528 &mut self,
529 method: InvokeMethod,
530 data: &[u8],
531 allocation_stats: &mut Option<AllocationStats>,
532 ) -> Result<Vec<u8>, Error> {
533 if !self.memory_zeroed {
537 self.memory.erase().map_err(|e| {
539 error!(target: "wasm-executor", "snapshot restoration failed: {}", e);
543 WasmError::ErasingFailed(e.to_string())
544 })?;
545 }
546
547 self.data_segments_snapshot
549 .apply(|offset, contents| self.memory.set(offset, contents))?;
550
551 self.global_vals_snapshot.apply(&self.instance)?;
553
554 let res = call_in_wasm_module(
555 &self.instance,
556 &self.memory,
557 method,
558 data,
559 self.host_functions.clone(),
560 self.allow_missing_func_imports,
561 self.missing_functions.clone(),
562 allocation_stats,
563 );
564
565 self.memory_zeroed = self.memory.erase().is_ok();
567
568 res
569 }
570}
571
572impl WasmInstance for WasmiInstance {
573 fn call_with_allocation_stats(
574 &mut self,
575 method: InvokeMethod,
576 data: &[u8],
577 ) -> (Result<Vec<u8>, Error>, Option<AllocationStats>) {
578 let mut allocation_stats = None;
579 let result = self.call_impl(method, data, &mut allocation_stats);
580 (result, allocation_stats)
581 }
582
583 fn get_global_const(&mut self, name: &str) -> Result<Option<sp_wasm_interface::Value>, Error> {
584 match self.instance.export_by_name(name) {
585 Some(global) => Ok(Some(
586 global
587 .as_global()
588 .ok_or_else(|| format!("`{}` is not a global", name))?
589 .get()
590 .into(),
591 )),
592 None => Ok(None),
593 }
594 }
595
596 fn linear_memory_base_ptr(&self) -> Option<*const u8> {
597 Some(self.memory.direct_access().as_ref().as_ptr())
598 }
599}