// This file is part of Substrate.
// Copyright (C) 2018-2022 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 module implements sandboxing support in the runtime.
//!
//! Sandboxing is backed by wasmi and wasmer, depending on the configuration.
#[cfg(feature = "wasmer-sandbox")]
mod wasmer_backend;
mod wasmi_backend;
use crate::{
error::{self, Result},
util,
};
use codec::Decode;
use sp_core::sandbox as sandbox_primitives;
use sp_wasm_interface::{FunctionContext, Pointer, WordSize};
use std::{collections::HashMap, rc::Rc};
#[cfg(feature = "wasmer-sandbox")]
use wasmer_backend::{
get_global as wasmer_get_global, instantiate as wasmer_instantiate, invoke as wasmer_invoke,
new_memory as wasmer_new_memory, Backend as WasmerBackend,
MemoryWrapper as WasmerMemoryWrapper,
};
use wasmi_backend::{
get_global as wasmi_get_global, instantiate as wasmi_instantiate, invoke as wasmi_invoke,
new_memory as wasmi_new_memory, MemoryWrapper as WasmiMemoryWrapper,
};
/// Index of a function inside the supervisor.
///
/// This is a typically an index in the default table of the supervisor, however
/// the exact meaning of this index is depends on the implementation of dispatch function.
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct SupervisorFuncIndex(usize);
impl From for usize {
fn from(index: SupervisorFuncIndex) -> Self {
index.0
}
}
/// Index of a function within guest index space.
///
/// This index is supposed to be used as index for `Externals`.
#[derive(Copy, Clone, Debug, PartialEq)]
struct GuestFuncIndex(usize);
/// This struct holds a mapping from guest index space to supervisor.
struct GuestToSupervisorFunctionMapping {
/// Position of elements in this vector are interpreted
/// as indices of guest functions and are mapped to
/// corresponding supervisor function indices.
funcs: Vec,
}
impl GuestToSupervisorFunctionMapping {
/// Create an empty function mapping
fn new() -> GuestToSupervisorFunctionMapping {
GuestToSupervisorFunctionMapping { funcs: Vec::new() }
}
/// Add a new supervisor function to the mapping.
/// Returns a newly assigned guest function index.
fn define(&mut self, supervisor_func: SupervisorFuncIndex) -> GuestFuncIndex {
let idx = self.funcs.len();
self.funcs.push(supervisor_func);
GuestFuncIndex(idx)
}
/// Find supervisor function index by its corresponding guest function index
fn func_by_guest_index(&self, guest_func_idx: GuestFuncIndex) -> Option {
self.funcs.get(guest_func_idx.0).cloned()
}
}
/// Holds sandbox function and memory imports and performs name resolution
struct Imports {
/// Maps qualified function name to its guest function index
func_map: HashMap<(Vec, Vec), GuestFuncIndex>,
/// Maps qualified field name to its memory reference
memories_map: HashMap<(Vec, Vec), Memory>,
}
impl Imports {
fn func_by_name(&self, module_name: &str, func_name: &str) -> Option {
self.func_map
.get(&(module_name.as_bytes().to_owned(), func_name.as_bytes().to_owned()))
.cloned()
}
fn memory_by_name(&self, module_name: &str, memory_name: &str) -> Option {
self.memories_map
.get(&(module_name.as_bytes().to_owned(), memory_name.as_bytes().to_owned()))
.cloned()
}
}
/// The sandbox context used to execute sandboxed functions.
pub trait SandboxContext {
/// Invoke a function in the supervisor environment.
///
/// This first invokes the dispatch thunk function, passing in the function index of the
/// desired function to call and serialized arguments. The thunk calls the desired function
/// with the deserialized arguments, then serializes the result into memory and returns
/// reference. The pointer to and length of the result in linear memory is encoded into an
/// `i64`, with the upper 32 bits representing the pointer and the lower 32 bits representing
/// the length.
///
/// # Errors
///
/// Returns `Err` if the dispatch_thunk function has an incorrect signature or traps during
/// execution.
fn invoke(
&mut self,
invoke_args_ptr: Pointer,
invoke_args_len: WordSize,
state: u32,
func_idx: SupervisorFuncIndex,
) -> Result;
/// Returns the supervisor context.
fn supervisor_context(&mut self) -> &mut dyn FunctionContext;
}
/// Implementation of [`Externals`] that allows execution of guest module with
/// [externals][`Externals`] that might refer functions defined by supervisor.
///
/// [`Externals`]: ../wasmi/trait.Externals.html
pub struct GuestExternals<'a> {
/// Instance of sandboxed module to be dispatched
sandbox_instance: &'a SandboxInstance,
/// External state passed to guest environment, see the `instantiate` function
state: u32,
}
/// Module instance in terms of selected backend
enum BackendInstance {
/// Wasmi module instance
Wasmi(wasmi::ModuleRef),
/// Wasmer module instance
#[cfg(feature = "wasmer-sandbox")]
Wasmer(wasmer::Instance),
}
/// Sandboxed instance of a wasm module.
///
/// It's primary purpose is to [`invoke`] exported functions on it.
///
/// All imports of this instance are specified at the creation time and
/// imports are implemented by the supervisor.
///
/// Hence, in order to invoke an exported function on a sandboxed module instance,
/// it's required to provide supervisor externals: it will be used to execute
/// code in the supervisor context.
///
/// This is generic over a supervisor function reference type.
///
/// [`invoke`]: #method.invoke
pub struct SandboxInstance {
backend_instance: BackendInstance,
guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping,
}
impl SandboxInstance {
/// Invoke an exported function by a name.
///
/// `supervisor_externals` is required to execute the implementations
/// of the syscalls that published to a sandboxed module instance.
///
/// The `state` parameter can be used to provide custom data for
/// these syscall implementations.
pub fn invoke(
&self,
export_name: &str,
args: &[sp_wasm_interface::Value],
state: u32,
sandbox_context: &mut dyn SandboxContext,
) -> std::result::Result