// This file is part of Substrate.
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
//! This crate provides an implementation of `WasmModule` that is baked by wasmi.
use std::{cell::RefCell, str, sync::Arc};
use log::{error, trace};
use wasmi::{
memory_units::Pages,
FuncInstance, ImportsBuilder, MemoryRef, Module, ModuleInstance, ModuleRef,
RuntimeValue::{self, I32, I64},
TableRef,
};
use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator};
use sc_executor_common::{
error::{Error, MessageWithBacktrace, WasmError},
runtime_blob::{DataSegmentsSnapshot, RuntimeBlob},
wasm_runtime::{HeapAllocStrategy, InvokeMethod, WasmInstance, WasmModule},
};
use sp_runtime_interface::unpack_ptr_and_len;
use sp_wasm_interface::{Function, FunctionContext, Pointer, Result as WResult, WordSize};
/// Wrapper around [`MemorRef`] that implements [`sc_allocator::Memory`].
struct MemoryWrapper<'a>(&'a MemoryRef);
impl sc_allocator::Memory for MemoryWrapper<'_> {
fn with_access_mut(&mut self, run: impl FnOnce(&mut [u8]) -> R) -> R {
self.0.with_direct_access_mut(run)
}
fn with_access(&self, run: impl FnOnce(&[u8]) -> R) -> R {
self.0.with_direct_access(run)
}
fn pages(&self) -> u32 {
self.0.current_size().0 as _
}
fn max_pages(&self) -> Option {
self.0.maximum().map(|p| p.0 as _)
}
fn grow(&mut self, additional: u32) -> Result<(), ()> {
self.0
.grow(Pages(additional as _))
.map_err(|e| {
log::error!(
target: "wasm-executor",
"Failed to grow memory by {} pages: {}",
additional,
e,
)
})
.map(drop)
}
}
struct FunctionExecutor {
heap: RefCell,
memory: MemoryRef,
host_functions: Arc>,
allow_missing_func_imports: bool,
missing_functions: Arc>,
panic_message: Option,
}
impl FunctionExecutor {
fn new(
m: MemoryRef,
heap_base: u32,
host_functions: Arc>,
allow_missing_func_imports: bool,
missing_functions: Arc>,
) -> Result {
Ok(FunctionExecutor {
heap: RefCell::new(FreeingBumpHeapAllocator::new(heap_base)),
memory: m,
host_functions,
allow_missing_func_imports,
missing_functions,
panic_message: None,
})
}
}
impl FunctionContext for FunctionExecutor {
fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> {
self.memory.get_into(address.into(), dest).map_err(|e| e.to_string())
}
fn write_memory(&mut self, address: Pointer, data: &[u8]) -> WResult<()> {
self.memory.set(address.into(), data).map_err(|e| e.to_string())
}
fn allocate_memory(&mut self, size: WordSize) -> WResult> {
self.heap
.borrow_mut()
.allocate(&mut MemoryWrapper(&self.memory), size)
.map_err(|e| e.to_string())
}
fn deallocate_memory(&mut self, ptr: Pointer) -> WResult<()> {
self.heap
.borrow_mut()
.deallocate(&mut MemoryWrapper(&self.memory), ptr)
.map_err(|e| e.to_string())
}
fn register_panic_error_message(&mut self, message: &str) {
self.panic_message = Some(message.to_owned());
}
}
/// Will be used on initialization of a module to resolve function and memory imports.
struct Resolver<'a> {
/// All the hot functions that we export for the WASM blob.
host_functions: &'a [&'static dyn Function],
/// Should we allow missing function imports?
///
/// If `true`, we return a stub that will return an error when being called.
allow_missing_func_imports: bool,
/// All the names of functions for that we did not provide a host function.
missing_functions: RefCell>,
}
impl<'a> Resolver<'a> {
fn new(
host_functions: &'a [&'static dyn Function],
allow_missing_func_imports: bool,
) -> Resolver<'a> {
Resolver {
host_functions,
allow_missing_func_imports,
missing_functions: RefCell::new(Vec::new()),
}
}
}
impl<'a> wasmi::ModuleImportResolver for Resolver<'a> {
fn resolve_func(
&self,
name: &str,
signature: &wasmi::Signature,
) -> std::result::Result {
let signature = sp_wasm_interface::Signature::from(signature);
for (function_index, function) in self.host_functions.iter().enumerate() {
if name == function.name() {
if signature == function.signature() {
return Ok(wasmi::FuncInstance::alloc_host(signature.into(), function_index))
} else {
return Err(wasmi::Error::Instantiation(format!(
"Invalid signature for function `{}` expected `{:?}`, got `{:?}`",
function.name(),
signature,
function.signature(),
)))
}
}
}
if self.allow_missing_func_imports {
trace!(
target: "wasm-executor",
"Could not find function `{}`, a stub will be provided instead.",
name,
);
let id = self.missing_functions.borrow().len() + self.host_functions.len();
self.missing_functions.borrow_mut().push(name.to_string());
Ok(wasmi::FuncInstance::alloc_host(signature.into(), id))
} else {
Err(wasmi::Error::Instantiation(format!("Export {} not found", name)))
}
}
fn resolve_memory(
&self,
_: &str,
_: &wasmi::MemoryDescriptor,
) -> Result {
Err(wasmi::Error::Instantiation(
"Internal error, wasmi expects that the wasm blob exports memory.".into(),
))
}
}
impl wasmi::Externals for FunctionExecutor {
fn invoke_index(
&mut self,
index: usize,
args: wasmi::RuntimeArgs,
) -> Result