// This file is part of Substrate.
// Copyright (C) 2019-2021 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 .
//! Wasmi specific impls for sandbox
use std::{fmt, rc::Rc};
use codec::{Decode, Encode};
use sp_sandbox::HostError;
use sp_wasm_interface::{FunctionContext, Pointer, ReturnValue, Value, WordSize};
use wasmi::{
memory_units::Pages, ImportResolver, MemoryInstance, Module, ModuleInstance, RuntimeArgs,
RuntimeValue, Trap,
};
use crate::{
error::{self, Error},
sandbox::{
BackendInstance, GuestEnvironment, GuestExternals, GuestFuncIndex, Imports,
InstantiationError, Memory, SandboxContext, SandboxInstance,
},
util::{checked_range, MemoryTransfer},
};
environmental::environmental!(SandboxContextStore: trait SandboxContext);
#[derive(Debug)]
struct CustomHostError(String);
impl fmt::Display for CustomHostError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "HostError: {}", self.0)
}
}
impl wasmi::HostError for CustomHostError {}
/// Construct trap error from specified message
fn trap(msg: &'static str) -> Trap {
Trap::host(CustomHostError(msg.into()))
}
impl ImportResolver for Imports {
fn resolve_func(
&self,
module_name: &str,
field_name: &str,
signature: &wasmi::Signature,
) -> std::result::Result {
let idx = self.func_by_name(module_name, field_name).ok_or_else(|| {
wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name))
})?;
Ok(wasmi::FuncInstance::alloc_host(signature.clone(), idx.0))
}
fn resolve_memory(
&self,
module_name: &str,
field_name: &str,
_memory_type: &wasmi::MemoryDescriptor,
) -> std::result::Result {
let mem = self.memory_by_name(module_name, field_name).ok_or_else(|| {
wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name))
})?;
let wrapper = mem.as_wasmi().ok_or_else(|| {
wasmi::Error::Instantiation(format!(
"Unsupported non-wasmi export {}:{}",
module_name, field_name
))
})?;
// Here we use inner memory reference only to resolve the imports
// without accessing the memory contents. All subsequent memory accesses
// should happen through the wrapper, that enforces the memory access protocol.
let mem = wrapper.0;
Ok(mem)
}
fn resolve_global(
&self,
module_name: &str,
field_name: &str,
_global_type: &wasmi::GlobalDescriptor,
) -> std::result::Result {
Err(wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)))
}
fn resolve_table(
&self,
module_name: &str,
field_name: &str,
_table_type: &wasmi::TableDescriptor,
) -> std::result::Result {
Err(wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)))
}
}
/// Allocate new memory region
pub fn new_memory(initial: u32, maximum: Option) -> crate::error::Result {
let memory = Memory::Wasmi(MemoryWrapper::new(
MemoryInstance::alloc(Pages(initial as usize), maximum.map(|m| Pages(m as usize)))
.map_err(|error| Error::Sandbox(error.to_string()))?,
));
Ok(memory)
}
/// Wasmi provides direct access to its memory using slices.
///
/// This wrapper limits the scope where the slice can be taken to
#[derive(Debug, Clone)]
pub struct MemoryWrapper(wasmi::MemoryRef);
impl MemoryWrapper {
/// Take ownership of the memory region and return a wrapper object
fn new(memory: wasmi::MemoryRef) -> Self {
Self(memory)
}
}
impl MemoryTransfer for MemoryWrapper {
fn read(&self, source_addr: Pointer, size: usize) -> error::Result> {
self.0.with_direct_access(|source| {
let range = checked_range(source_addr.into(), size, source.len())
.ok_or_else(|| error::Error::Other("memory read is out of bounds".into()))?;
Ok(Vec::from(&source[range]))
})
}
fn read_into(&self, source_addr: Pointer, destination: &mut [u8]) -> error::Result<()> {
self.0.with_direct_access(|source| {
let range = checked_range(source_addr.into(), destination.len(), source.len())
.ok_or_else(|| error::Error::Other("memory read is out of bounds".into()))?;
destination.copy_from_slice(&source[range]);
Ok(())
})
}
fn write_from(&self, dest_addr: Pointer, source: &[u8]) -> error::Result<()> {
self.0.with_direct_access_mut(|destination| {
let range = checked_range(dest_addr.into(), source.len(), destination.len())
.ok_or_else(|| error::Error::Other("memory write is out of bounds".into()))?;
destination[range].copy_from_slice(source);
Ok(())
})
}
}
impl<'a> wasmi::Externals for GuestExternals<'a> {
fn invoke_index(
&mut self,
index: usize,
args: RuntimeArgs,
) -> std::result::Result