tp_sandbox/lib.rs
1// This file is part of Tetcore.
2
3// Copyright (C) 2018-2021 Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! This crate provides means to instantiate and execute wasm modules.
19//!
20//! It works even when the user of this library executes from
21//! inside the wasm VM. In this case the same VM is used for execution
22//! of both the sandbox owner and the sandboxed module, without compromising security
23//! and without the performance penalty of full wasm emulation inside wasm.
24//!
25//! This is achieved by using bindings to the wasm VM, which are published by the host API.
26//! This API is thin and consists of only a handful functions. It contains functions for instantiating
27//! modules and executing them, but doesn't contain functions for inspecting the module
28//! structure. The user of this library is supposed to read the wasm module.
29//!
30//! When this crate is used in the `std` environment all these functions are implemented by directly
31//! calling the wasm VM.
32//!
33//! Examples of possible use-cases for this library are not limited to the following:
34//!
35//! - implementing smart-contract runtimes that use wasm for contract code
36//! - executing a wasm tetcore runtime inside of a wasm parachain
37
38#![warn(missing_docs)]
39#![cfg_attr(not(feature = "std"), no_std)]
40
41use tetcore_std::prelude::*;
42
43pub use tet_core::sandbox::HostError;
44pub use tetcore_wasm_interface::{Value, ReturnValue};
45
46mod imp {
47 #[cfg(feature = "std")]
48 include!("../with_std.rs");
49
50 #[cfg(not(feature = "std"))]
51 include!("../without_std.rs");
52}
53
54/// Error that can occur while using this crate.
55#[derive(tet_core::RuntimeDebug)]
56pub enum Error {
57 /// Module is not valid, couldn't be instantiated.
58 Module,
59
60 /// Access to a memory or table was made with an address or an index which is out of bounds.
61 ///
62 /// Note that if wasm module makes an out-of-bounds access then trap will occur.
63 OutOfBounds,
64
65 /// Failed to invoke the start function or an exported function for some reason.
66 Execution,
67}
68
69impl From<Error> for HostError {
70 fn from(_e: Error) -> HostError {
71 HostError
72 }
73}
74
75/// Function pointer for specifying functions by the
76/// supervisor in [`EnvironmentDefinitionBuilder`].
77///
78/// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html
79pub type HostFuncType<T> = fn(&mut T, &[Value]) -> Result<ReturnValue, HostError>;
80
81/// Reference to a sandboxed linear memory, that
82/// will be used by the guest module.
83///
84/// The memory can't be directly accessed by supervisor, but only
85/// through designated functions [`get`](Memory::get) and [`set`](Memory::set).
86#[derive(Clone)]
87pub struct Memory {
88 inner: imp::Memory,
89}
90
91impl Memory {
92 /// Construct a new linear memory instance.
93 ///
94 /// The memory allocated with initial number of pages specified by `initial`.
95 /// Minimal possible value for `initial` is 0 and maximum possible is `65536`.
96 /// (Since maximum addressable memory is 2<sup>32</sup> = 4GiB = 65536 * 64KiB).
97 ///
98 /// It is possible to limit maximum number of pages this memory instance can have by specifying
99 /// `maximum`. If not specified, this memory instance would be able to allocate up to 4GiB.
100 ///
101 /// Allocated memory is always zeroed.
102 pub fn new(initial: u32, maximum: Option<u32>) -> Result<Memory, Error> {
103 Ok(Memory {
104 inner: imp::Memory::new(initial, maximum)?,
105 })
106 }
107
108 /// Read a memory area at the address `ptr` with the size of the provided slice `buf`.
109 ///
110 /// Returns `Err` if the range is out-of-bounds.
111 pub fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> {
112 self.inner.get(ptr, buf)
113 }
114
115 /// Write a memory area at the address `ptr` with contents of the provided slice `buf`.
116 ///
117 /// Returns `Err` if the range is out-of-bounds.
118 pub fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> {
119 self.inner.set(ptr, value)
120 }
121}
122
123/// Struct that can be used for defining an environment for a sandboxed module.
124///
125/// The sandboxed module can access only the entities which were defined and passed
126/// to the module at the instantiation time.
127pub struct EnvironmentDefinitionBuilder<T> {
128 inner: imp::EnvironmentDefinitionBuilder<T>,
129}
130
131impl<T> EnvironmentDefinitionBuilder<T> {
132 /// Construct a new `EnvironmentDefinitionBuilder`.
133 pub fn new() -> EnvironmentDefinitionBuilder<T> {
134 EnvironmentDefinitionBuilder {
135 inner: imp::EnvironmentDefinitionBuilder::new(),
136 }
137 }
138
139 /// Register a host function in this environment definition.
140 ///
141 /// NOTE that there is no constraints on type of this function. An instance
142 /// can import function passed here with any signature it wants. It can even import
143 /// the same function (i.e. with same `module` and `field`) several times. It's up to
144 /// the user code to check or constrain the types of signatures.
145 pub fn add_host_func<N1, N2>(&mut self, module: N1, field: N2, f: HostFuncType<T>)
146 where
147 N1: Into<Vec<u8>>,
148 N2: Into<Vec<u8>>,
149 {
150 self.inner.add_host_func(module, field, f);
151 }
152
153 /// Register a memory in this environment definition.
154 pub fn add_memory<N1, N2>(&mut self, module: N1, field: N2, mem: Memory)
155 where
156 N1: Into<Vec<u8>>,
157 N2: Into<Vec<u8>>,
158 {
159 self.inner.add_memory(module, field, mem.inner);
160 }
161}
162
163/// Sandboxed instance of a wasm module.
164///
165/// This instance can be used for invoking exported functions.
166pub struct Instance<T> {
167 inner: imp::Instance<T>,
168}
169
170impl<T> Instance<T> {
171 /// Instantiate a module with the given [`EnvironmentDefinitionBuilder`]. It will
172 /// run the `start` function (if it is present in the module) with the given `state`.
173 ///
174 /// Returns `Err(Error::Module)` if this module can't be instantiated with the given
175 /// environment. If execution of `start` function generated a trap, then `Err(Error::Execution)` will
176 /// be returned.
177 ///
178 /// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html
179 pub fn new(code: &[u8], env_def_builder: &EnvironmentDefinitionBuilder<T>, state: &mut T)
180 -> Result<Instance<T>, Error>
181 {
182 Ok(Instance {
183 inner: imp::Instance::new(code, &env_def_builder.inner, state)?,
184 })
185 }
186
187 /// Invoke an exported function with the given name.
188 ///
189 /// # Errors
190 ///
191 /// Returns `Err(Error::Execution)` if:
192 ///
193 /// - An export function name isn't a proper utf8 byte sequence,
194 /// - This module doesn't have an exported function with the given name,
195 /// - If types of the arguments passed to the function doesn't match function signature
196 /// then trap occurs (as if the exported function was called via call_indirect),
197 /// - Trap occurred at the execution time.
198 pub fn invoke(
199 &mut self,
200 name: &str,
201 args: &[Value],
202 state: &mut T,
203 ) -> Result<ReturnValue, Error> {
204 self.inner.invoke(name, args, state)
205 }
206
207 /// Get the value from a global with the given `name`.
208 ///
209 /// Returns `Some(_)` if the global could be found.
210 pub fn get_global_val(&self, name: &str) -> Option<Value> {
211 self.inner.get_global_val(name)
212 }
213}