// This file is part of Substrate.
// Copyright (C) 2018-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 .
//! This module implements sandboxing support in the runtime.
//!
//! Sandboxing is baked by wasmi at the moment. In future, however, we would like to add/switch to
//! a compiled execution engine.
use crate::error::{Result, Error};
use std::{collections::HashMap, rc::Rc};
use codec::{Decode, Encode};
use sp_core::sandbox as sandbox_primitives;
use wasmi::{
Externals, ImportResolver, MemoryInstance, MemoryRef, Module, ModuleInstance,
ModuleRef, RuntimeArgs, RuntimeValue, Trap, TrapKind, memory_units::Pages,
};
use sp_wasm_interface::{FunctionContext, Pointer, WordSize};
/// 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 with 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 {
funcs: Vec,
}
impl GuestToSupervisorFunctionMapping {
fn new() -> GuestToSupervisorFunctionMapping {
GuestToSupervisorFunctionMapping { funcs: Vec::new() }
}
fn define(&mut self, supervisor_func: SupervisorFuncIndex) -> GuestFuncIndex {
let idx = self.funcs.len();
self.funcs.push(supervisor_func);
GuestFuncIndex(idx)
}
fn func_by_guest_index(&self, guest_func_idx: GuestFuncIndex) -> Option {
self.funcs.get(guest_func_idx.0).cloned()
}
}
struct Imports {
func_map: HashMap<(Vec, Vec), GuestFuncIndex>,
memories_map: HashMap<(Vec, Vec), MemoryRef>,
}
impl ImportResolver for Imports {
fn resolve_func(
&self,
module_name: &str,
field_name: &str,
signature: &::wasmi::Signature,
) -> std::result::Result {
let key = (
module_name.as_bytes().to_owned(),
field_name.as_bytes().to_owned(),
);
let idx = *self.func_map.get(&key).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 key = (
module_name.as_bytes().to_vec(),
field_name.as_bytes().to_vec(),
);
let mem = self.memories_map
.get(&key)
.ok_or_else(|| {
wasmi::Error::Instantiation(format!(
"Export {}:{} not found",
module_name, field_name
))
})?
.clone();
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
)))
}
}
/// This trait encapsulates sandboxing capabilities.
///
/// Note that this functions are only called in the `supervisor` context.
pub trait SandboxCapabilities: FunctionContext {
/// Represents a function reference into the supervisor environment.
type SupervisorFuncRef;
/// 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,
dispatch_thunk: &Self::SupervisorFuncRef,
invoke_args_ptr: Pointer,
invoke_args_len: WordSize,
state: u32,
func_idx: SupervisorFuncIndex,
) -> Result;
}
/// 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, FE: SandboxCapabilities + 'a> {
supervisor_externals: &'a mut FE,
sandbox_instance: &'a SandboxInstance,
state: u32,
}
fn trap(msg: &'static str) -> Trap {
TrapKind::Host(Box::new(Error::Other(msg.into()))).into()
}
fn deserialize_result(serialized_result: &[u8]) -> std::result::Result