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}