gear_sandbox/
lib.rs

1// This file is part of Gear.
2
3// Copyright (C) Gear Technologies Inc.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19//! This crate provides means to instantiate and execute wasm modules.
20//!
21//! It works even when the user of this library executes from
22//! inside the wasm VM. In this case the same VM is used for execution
23//! of both the sandbox owner and the sandboxed module, without compromising security
24//! and without the performance penalty of full wasm emulation inside wasm.
25//!
26//! This is achieved by using bindings to the wasm VM, which are published by the host API.
27//! This API is thin and consists of only a handful functions. It contains functions for
28//! instantiating modules and executing them, but doesn't contain functions for inspecting the
29//! module structure. The user of this library is supposed to read the wasm module.
30//!
31//! When this crate is used in the `std` environment all these functions are implemented by directly
32//! calling the wasm VM.
33//!
34//! Examples of possible use-cases for this library are not limited to the following:
35//!
36//! - implementing program runtimes that use Wasm for contract code
37//! - executing a wasm substrate runtime inside of a wasm parachain
38
39#![allow(clippy::needless_borrows_for_generic_args)]
40#![cfg_attr(not(feature = "std"), no_std)]
41
42extern crate alloc;
43
44#[cfg(feature = "std")]
45pub mod embedded_executor;
46#[cfg(feature = "std")]
47pub use self::embedded_executor as default_executor;
48
49#[cfg(not(feature = "std"))]
50#[cfg(target_arch = "wasm32")]
51pub mod host_executor;
52#[cfg(not(feature = "std"))]
53#[cfg(target_arch = "wasm32")]
54pub use self::host_executor as default_executor;
55
56pub use gear_sandbox_env as env;
57pub use gear_sandbox_env::HostError;
58
59pub use sp_wasm_interface_common::{IntoValue, ReturnValue, TryFromValue, Value};
60
61use alloc::string::String;
62use sp_core::RuntimeDebug;
63use sp_std::prelude::*;
64use sp_wasm_interface_common::HostPointer;
65
66/// Error that can occur while using this crate.
67#[derive(RuntimeDebug)]
68pub enum Error {
69    /// Module is not valid, couldn't be instantiated.
70    Module,
71
72    /// Access to a memory or table was made with an address or an index which is out of bounds.
73    ///
74    /// Note that if wasm module makes an out-of-bounds access then trap will occur.
75    OutOfBounds,
76
77    /// Trying to grow memory by more than maximum limit.
78    MemoryGrow,
79
80    /// Failed to invoke the start function or an exported function for some reason.
81    Execution,
82}
83
84impl From<Error> for HostError {
85    fn from(_e: Error) -> HostError {
86        HostError
87    }
88}
89
90/// Function pointer for specifying functions by the
91/// supervisor in [`EnvironmentDefinitionBuilder`].
92///
93/// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html
94pub type HostFuncType<T> =
95    fn(&mut default_executor::Caller<'_, T>, &[Value]) -> Result<env::WasmReturnValue, HostError>;
96
97/// Sandbox store.
98pub trait SandboxStore: AsContextExt {
99    /// Create a new sandbox store.
100    fn new(state: Self::State) -> Self;
101}
102
103/// Sandbox context.
104pub trait AsContextExt: default_executor::AsContext {
105    /// Context state.
106    type State;
107
108    /// Return mutable reference to state.
109    fn data_mut(&mut self) -> &mut Self::State;
110}
111
112/// Reference to a sandboxed linear memory, that
113/// will be used by the guest module.
114///
115/// The memory can't be directly accessed by supervisor, but only
116/// through designated functions [`get`](SandboxMemory::read) and [`set`](SandboxMemory::write).
117pub trait SandboxMemory<T>: Sized + Clone {
118    /// Construct a new linear memory instance.
119    ///
120    /// The memory allocated with initial number of pages specified by `initial`.
121    /// Minimal possible value for `initial` is 0 and maximum possible is `65536`.
122    /// (Since maximum addressable memory is 2<sup>32</sup> = 4GiB = 65536 * 64KiB).
123    ///
124    /// It is possible to limit maximum number of pages this memory instance can have by specifying
125    /// `maximum`. If not specified, this memory instance would be able to allocate up to 4GiB.
126    ///
127    /// Allocated memory is always zeroed.
128    fn new(
129        store: &mut default_executor::Store<T>,
130        initial: u32,
131        maximum: Option<u32>,
132    ) -> Result<Self, Error>;
133
134    /// Read a memory area at the address `ptr` with the size of the provided slice `buf`.
135    ///
136    /// Returns `Err` if the range is out-of-bounds.
137    fn read<Context>(&self, ctx: &Context, ptr: u32, buf: &mut [u8]) -> Result<(), Error>
138    where
139        Context: AsContextExt<State = T>;
140
141    /// Write a memory area at the address `ptr` with contents of the provided slice `buf`.
142    ///
143    /// Returns `Err` if the range is out-of-bounds.
144    fn write<Context>(&self, ctx: &mut Context, ptr: u32, value: &[u8]) -> Result<(), Error>
145    where
146        Context: AsContextExt<State = T>;
147
148    /// Grow memory with provided number of pages.
149    ///
150    /// Returns `Err` if attempted to allocate more memory than permitted by the limit.
151    fn grow<Context>(&self, ctx: &mut Context, pages: u32) -> Result<u32, Error>
152    where
153        Context: AsContextExt<State = T>;
154
155    /// Returns current memory size.
156    ///
157    /// Maximum memory size cannot exceed 65536 pages or 4GiB.
158    fn size<Context>(&self, ctx: &Context) -> u32
159    where
160        Context: AsContextExt<State = T>;
161
162    /// Returns pointer to the begin of wasm mem buffer
163    /// # Safety
164    /// Pointer is intended to use by `mprotect` function.
165    unsafe fn get_buff<Context>(&self, ctx: &Context) -> HostPointer
166    where
167        Context: AsContextExt<State = T>;
168}
169
170/// Struct that can be used for defining an environment for a sandboxed module.
171///
172/// The sandboxed module can access only the entities which were defined and passed
173/// to the module at the instantiation time.
174pub trait SandboxEnvironmentBuilder<State, Memory>: Sized {
175    /// Construct a new `EnvironmentDefinitionBuilder`.
176    fn new() -> Self;
177
178    /// Register a host function in this environment definition.
179    ///
180    /// NOTE that there is no constraints on type of this function. An instance
181    /// can import function passed here with any signature it wants. It can even import
182    /// the same function (i.e. with same `module` and `field`) several times. It's up to
183    /// the user code to check or constrain the types of signatures.
184    fn add_host_func<N1, N2>(&mut self, module: N1, field: N2, f: HostFuncType<State>)
185    where
186        N1: Into<String>,
187        N2: Into<String>;
188
189    /// Register a memory in this environment definition.
190    fn add_memory<N1, N2>(&mut self, module: N1, field: N2, mem: Memory)
191    where
192        N1: Into<String>,
193        N2: Into<String>;
194}
195
196/// Error that can occur while using this crate.
197#[derive(RuntimeDebug)]
198pub enum GlobalsSetError {
199    /// A global variable is not found.
200    NotFound,
201
202    /// A global variable is immutable or has a different type.
203    Other,
204}
205
206/// Sandboxed instance of a wasm module.
207///
208/// This instance can be used for invoking exported functions.
209pub trait SandboxInstance<State>: Sized {
210    /// The memory type used for this sandbox.
211    type Memory: SandboxMemory<State>;
212
213    /// The environment builder used to construct this sandbox.
214    type EnvironmentBuilder: SandboxEnvironmentBuilder<State, Self::Memory>;
215
216    /// Instantiate a module with the given [`EnvironmentDefinitionBuilder`]. It will
217    /// run the `start` function (if it is present in the module) with the given `state`.
218    ///
219    /// Returns `Err(Error::Module)` if this module can't be instantiated with the given
220    /// environment. If execution of `start` function generated a trap, then `Err(Error::Execution)`
221    /// will be returned.
222    ///
223    /// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html
224    fn new(
225        store: &mut default_executor::Store<State>,
226        code: &[u8],
227        env_def_builder: &Self::EnvironmentBuilder,
228    ) -> Result<Self, Error>;
229
230    /// Invoke an exported function with the given name.
231    ///
232    /// # Errors
233    ///
234    /// Returns `Err(Error::Execution)` if:
235    ///
236    /// - An export function name isn't a proper utf8 byte sequence,
237    /// - This module doesn't have an exported function with the given name,
238    /// - If types of the arguments passed to the function doesn't match function signature then
239    ///   trap occurs (as if the exported function was called via call_indirect),
240    /// - Trap occurred at the execution time.
241    fn invoke(
242        &mut self,
243        store: &mut default_executor::Store<State>,
244        name: &str,
245        args: &[Value],
246    ) -> Result<ReturnValue, Error>;
247
248    /// Get the value from a global with the given `name`.
249    ///
250    /// Returns `Some(_)` if the global could be found.
251    fn get_global_val(
252        &self,
253        store: &mut default_executor::Store<State>,
254        name: &str,
255    ) -> Option<Value>;
256
257    /// Set the value of a global with the given `name`.
258    fn set_global_val(
259        &self,
260        store: &mut default_executor::Store<State>,
261        name: &str,
262        value: Value,
263    ) -> Result<(), GlobalsSetError>;
264
265    /// Get raw pointer to the executor host sandbox instance.
266    fn get_instance_ptr(&self) -> HostPointer;
267}