casper_wasmi/lib.rs
1//! # wasmi
2//!
3//! This library allows WebAssembly modules to be loaded in binary format and their functions invoked.
4//!
5//! # Introduction
6//!
7//! WebAssembly (wasm) is a safe, portable and compact format that is designed for efficient execution.
8//!
9//! Wasm code is distributed in the form of modules that contains definitions of:
10//!
11//! - functions,
12//! - global variables,
13//! - linear memory instances and
14//! - tables.
15//!
16//! Each of these definitions can be imported and exported.
17//!
18//! In addition to these definitions, modules can define initialization data for their memory or tables. This initialization data can take the
19//! form of segments, copied to given offsets. They can also define a `start` function that is automatically executed when the module is loaded.
20//!
21//! ## Loading and Validation
22//!
23//! Before execution, a module must be validated. This process checks that the module is well-formed
24//! and makes only allowed operations.
25//!
26//! A valid module can't access memory outside its sandbox, can't cause stack underflows
27//! and can only call functions with correct signatures.
28//!
29//! ## Instantiation
30//!
31//! In order to execute code from a wasm module, it must be instantiated.
32//! Instantiation includes the following steps:
33//!
34//! 1. Creating an empty module instance.
35//! 2. Resolving the definition instances for each declared import in the module.
36//! 3. Instantiating definitions declared in the module (e.g. allocate global variables, allocate linear memory, etc.).
37//! 4. Initializing memory and table contents by copying segments into them.
38//! 5. Executing the `start` function, if any.
39//!
40//! After these steps, the module instance is ready to execute functions.
41//!
42//! ## Execution
43//!
44//! It only is allowed to call functions which are exported by the module.
45//! Functions can either return a result or trap (e.g. there can't be linking error in the middle of the function execution).
46//! This property is ensured by the validation process.
47//!
48//! # Examples
49//!
50//! ```rust
51//! extern crate casper_wasmi;
52//! extern crate wat;
53//!
54//! use casper_wasmi::{ModuleInstance, ImportsBuilder, NopExternals, RuntimeValue};
55//!
56//! fn main() {
57//! // Parse WAT (WebAssembly Text format) into wasm bytecode.
58//! let wasm_binary: Vec<u8> =
59//! wat::parse_str(
60//! r#"
61//! (module
62//! (func (export "test") (result i32)
63//! i32.const 1337
64//! )
65//! )
66//! "#,
67//! )
68//! .expect("failed to parse wat");
69//!
70//! // Load wasm binary and prepare it for instantiation.
71//! let module = casper_wasmi::Module::from_buffer(&wasm_binary)
72//! .expect("failed to load wasm");
73//!
74//! // Instantiate a module with empty imports and
75//! // assert that there is no `start` function.
76//! let instance =
77//! ModuleInstance::new(
78//! &module,
79//! &ImportsBuilder::default()
80//! )
81//! .expect("failed to instantiate wasm module")
82//! .assert_no_start();
83//!
84//! // Finally, invoke the exported function "test" with no parameters
85//! // and empty external function executor.
86//! assert_eq!(
87//! instance.invoke_export(
88//! "test",
89//! &[],
90//! &mut NopExternals,
91//! ).expect("failed to execute export"),
92//! Some(RuntimeValue::I32(1337)),
93//! );
94//! }
95//! ```
96
97#![warn(missing_docs)]
98#![cfg_attr(not(feature = "std"), no_std)]
99#![allow(clippy::len_without_is_empty)]
100
101#[cfg(not(feature = "std"))]
102#[macro_use]
103extern crate alloc;
104#[cfg(feature = "std")]
105extern crate std as alloc;
106
107use alloc::{
108 boxed::Box,
109 string::{String, ToString},
110 vec::Vec,
111};
112use core::fmt;
113#[cfg(feature = "std")]
114use std::error;
115
116/// Internal interpreter error.
117#[derive(Debug)]
118pub enum Error {
119 /// Module validation error. Might occur only at load time.
120 Validation(String),
121 /// Error while instantiating a module. Might occur when provided
122 /// with incorrect exports (i.e. linkage failure).
123 Instantiation(String),
124 /// Function-level error.
125 Function(String),
126 /// Table-level error.
127 Table(String),
128 /// Memory-level error.
129 Memory(String),
130 /// Global-level error.
131 Global(String),
132 /// Value-level error.
133 Value(String),
134 /// Trap.
135 Trap(Trap),
136 /// Custom embedder error.
137 Host(Box<dyn HostError>),
138}
139
140impl Error {
141 /// Creates a new host error.
142 pub fn host<T>(host_error: T) -> Self
143 where
144 T: HostError + Sized,
145 {
146 Self::Host(Box::new(host_error))
147 }
148
149 /// Returns a reference to a [`HostError`] if this `Error` represents some host error.
150 ///
151 /// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error.
152 ///
153 /// [`HostError`]: trait.HostError.html
154 /// [`Host`]: enum.Error.html#variant.Host
155 /// [`Trap`]: enum.Error.html#variant.Trap
156 /// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
157 pub fn as_host_error(&self) -> Option<&dyn HostError> {
158 match self {
159 Self::Host(host_error) => Some(&**host_error),
160 Self::Trap(Trap::Host(host_error)) => Some(&**host_error),
161 _ => None,
162 }
163 }
164
165 /// Returns [`HostError`] if this `Error` represents some host error.
166 ///
167 /// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error.
168 ///
169 /// [`HostError`]: trait.HostError.html
170 /// [`Host`]: enum.Error.html#variant.Host
171 /// [`Trap`]: enum.Error.html#variant.Trap
172 /// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
173 pub fn into_host_error(self) -> Option<Box<dyn HostError>> {
174 match self {
175 Error::Host(host_error) => Some(host_error),
176 Self::Trap(Trap::Host(host_error)) => Some(host_error),
177 _ => None,
178 }
179 }
180
181 /// Returns [`HostError`] if this `Error` represents some host error, otherwise returns the original error.
182 ///
183 /// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error.
184 ///
185 /// [`HostError`]: trait.HostError.html
186 /// [`Host`]: enum.Error.html#variant.Host
187 /// [`Trap`]: enum.Error.html#variant.Trap
188 /// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
189 pub fn try_into_host_error(self) -> Result<Box<dyn HostError>, Self> {
190 match self {
191 Error::Host(host_error) => Ok(host_error),
192 Self::Trap(Trap::Host(host_error)) => Ok(host_error),
193 other => Err(other),
194 }
195 }
196}
197
198impl From<Error> for String {
199 fn from(error: Error) -> Self {
200 match error {
201 Error::Validation(message) => message,
202 Error::Instantiation(message) => message,
203 Error::Function(message) => message,
204 Error::Table(message) => message,
205 Error::Memory(message) => message,
206 Error::Global(message) => message,
207 Error::Value(message) => message,
208 Error::Trap(trap) => format!("trap: {trap:?}"),
209 Error::Host(error) => format!("user: {error}"),
210 }
211 }
212}
213
214impl fmt::Display for Error {
215 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
216 match *self {
217 Error::Validation(ref s) => write!(f, "Validation: {}", s),
218 Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s),
219 Error::Function(ref s) => write!(f, "Function: {}", s),
220 Error::Table(ref s) => write!(f, "Table: {}", s),
221 Error::Memory(ref s) => write!(f, "Memory: {}", s),
222 Error::Global(ref s) => write!(f, "Global: {}", s),
223 Error::Value(ref s) => write!(f, "Value: {}", s),
224 Error::Trap(ref s) => write!(f, "Trap: {:?}", s),
225 Error::Host(ref e) => write!(f, "User: {}", e),
226 }
227 }
228}
229
230#[cfg(feature = "std")]
231impl error::Error for Error {
232 fn description(&self) -> &str {
233 match *self {
234 Error::Validation(ref s) => s,
235 Error::Instantiation(ref s) => s,
236 Error::Function(ref s) => s,
237 Error::Table(ref s) => s,
238 Error::Memory(ref s) => s,
239 Error::Global(ref s) => s,
240 Error::Value(ref s) => s,
241 Error::Trap(_) => "Trap",
242 Error::Host(_) => "Host error",
243 }
244 }
245}
246
247impl From<Trap> for Error {
248 fn from(e: Trap) -> Error {
249 Error::Trap(e)
250 }
251}
252
253impl From<validation::Error> for Error {
254 fn from(e: validation::Error) -> Error {
255 Error::Validation(e.to_string())
256 }
257}
258
259mod func;
260mod global;
261mod host;
262mod imports;
263mod isa;
264mod memory;
265mod module;
266mod prepare;
267mod pwasm;
268mod runner;
269mod table;
270mod types;
271
272pub use self::{
273 func::{FuncInstance, FuncInvocation, FuncRef, ResumableError},
274 global::{GlobalInstance, GlobalRef},
275 host::{Externals, NopExternals, RuntimeArgs},
276 imports::{ImportResolver, ImportsBuilder, ModuleImportResolver},
277 memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE},
278 module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef},
279 runner::{StackRecycler, DEFAULT_CALL_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT},
280 table::{TableInstance, TableRef},
281 types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor},
282};
283#[doc(inline)]
284pub use casper_wasmi_core::Value as RuntimeValue;
285pub use casper_wasmi_core::{
286 memory_units,
287 FromValue,
288 HostError,
289 LittleEndianConvert,
290 Trap,
291 TrapCode,
292 ValueType,
293};
294
295/// Mirrors the old value module.
296pub(crate) mod value {
297 pub use casper_wasmi_core::{
298 ArithmeticOps,
299 ExtendInto,
300 Float,
301 FromValue,
302 Integer,
303 LittleEndianConvert,
304 TransmuteInto,
305 TryTruncateInto,
306 WrapInto,
307 };
308}
309
310/// Floating point types that preserve NaN values.
311pub mod nan_preserving_float {
312 pub use casper_wasmi_core::{F32, F64};
313}
314
315/// Deserialized module prepared for instantiation.
316pub struct Module {
317 code_map: Vec<isa::Instructions>,
318 module: casper_wasm::elements::Module,
319}
320
321impl Module {
322 /// Create `Module` from `casper_wasm::elements::Module`.
323 ///
324 /// This function will load, validate and prepare a `casper_wasm`'s `Module`.
325 ///
326 /// # Errors
327 ///
328 /// Returns `Err` if provided `Module` is not valid.
329 ///
330 /// # Examples
331 ///
332 /// ```rust
333 /// extern crate casper_wasm;
334 /// extern crate casper_wasmi;
335 ///
336 /// use casper_wasm::builder;
337 /// use casper_wasm::elements;
338 ///
339 /// fn main() {
340 /// let parity_module =
341 /// builder::module()
342 /// .function()
343 /// .signature().with_param(elements::ValueType::I32).build()
344 /// .body().build()
345 /// .build()
346 /// .build();
347 ///
348 /// let module = casper_wasmi::Module::from_casper_wasm_module(parity_module)
349 /// .expect("casper-wasm builder generated invalid module!");
350 ///
351 /// // Instantiate `module`, etc...
352 /// }
353 /// ```
354 #[deprecated(note = "Please use `Module::from_casper_wasm_module` instead")]
355 #[inline]
356 pub fn from_parity_wasm_module(module: casper_wasm::elements::Module) -> Result<Module, Error> {
357 Self::from_casper_wasm_module(module)
358 }
359
360 /// Create `Module` from `casper_wasm::elements::Module`.
361 ///
362 /// This function will load, validate and prepare a `casper_wasm`'s `Module`.
363 ///
364 /// # Errors
365 ///
366 /// Returns `Err` if provided `Module` is not valid.
367 ///
368 /// # Examples
369 ///
370 /// ```rust
371 /// extern crate casper_wasm;
372 /// extern crate casper_wasmi;
373 ///
374 /// use casper_wasm::builder;
375 /// use casper_wasm::elements;
376 ///
377 /// fn main() {
378 /// let parity_module =
379 /// builder::module()
380 /// .function()
381 /// .signature().with_param(elements::ValueType::I32).build()
382 /// .body().build()
383 /// .build()
384 /// .build();
385 ///
386 /// let module = casper_wasmi::Module::from_casper_wasm_module(parity_module)
387 /// .expect("casper-wasm builder generated invalid module!");
388 ///
389 /// // Instantiate `module`, etc...
390 /// }
391 /// ```
392 pub fn from_casper_wasm_module(module: casper_wasm::elements::Module) -> Result<Module, Error> {
393 let prepare::CompiledModule { code_map, module } = prepare::compile_module(module)?;
394
395 Ok(Module { code_map, module })
396 }
397
398 /// Fail if the module contains any floating-point operations
399 ///
400 /// # Errors
401 ///
402 /// Returns `Err` if provided `Module` is not valid.
403 ///
404 /// # Examples
405 ///
406 /// ```rust
407 /// # extern crate casper_wasmi;
408 /// # extern crate wat;
409 ///
410 /// let wasm_binary: Vec<u8> =
411 /// wat::parse_str(
412 /// r#"
413 /// (module
414 /// (func $add (param $lhs i32) (param $rhs i32) (result i32)
415 /// local.get $lhs
416 /// local.get $rhs
417 /// i32.add))
418 /// "#,
419 /// )
420 /// .expect("failed to parse wat");
421 ///
422 /// // Load wasm binary and prepare it for instantiation.
423 /// let module = casper_wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
424 /// assert!(module.deny_floating_point().is_ok());
425 ///
426 /// let wasm_binary: Vec<u8> =
427 /// wat::parse_str(
428 /// r#"
429 /// (module
430 /// (func $add (param $lhs f32) (param $rhs f32) (result f32)
431 /// local.get $lhs
432 /// local.get $rhs
433 /// f32.add))
434 /// "#,
435 /// )
436 /// .expect("failed to parse wat");
437 ///
438 /// let module = casper_wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
439 /// assert!(module.deny_floating_point().is_err());
440 ///
441 /// let wasm_binary: Vec<u8> =
442 /// wat::parse_str(
443 /// r#"
444 /// (module
445 /// (func $add (param $lhs f32) (param $rhs f32) (result f32)
446 /// local.get $lhs))
447 /// "#,
448 /// )
449 /// .expect("failed to parse wat");
450 ///
451 /// let module = casper_wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
452 /// assert!(module.deny_floating_point().is_err());
453 /// ```
454 pub fn deny_floating_point(&self) -> Result<(), Error> {
455 prepare::deny_floating_point(&self.module).map_err(Into::into)
456 }
457
458 /// Create `Module` from a given buffer.
459 ///
460 /// This function will deserialize wasm module from a given module,
461 /// validate and prepare it for instantiation.
462 ///
463 /// # Errors
464 ///
465 /// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary.
466 ///
467 /// # Examples
468 ///
469 /// ```rust
470 /// extern crate casper_wasmi;
471 ///
472 /// fn main() {
473 /// let module =
474 /// casper_wasmi::Module::from_buffer(
475 /// // Minimal module:
476 /// // \0asm - magic
477 /// // 0x01 - version (in little-endian)
478 /// &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]
479 /// ).expect("Failed to load minimal module");
480 ///
481 /// // Instantiate `module`, etc...
482 /// }
483 /// ```
484 pub fn from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Module, Error> {
485 let module = casper_wasm::elements::deserialize_buffer(buffer.as_ref())
486 .map_err(|e: casper_wasm::elements::Error| Error::Validation(e.to_string()))?;
487 Module::from_casper_wasm_module(module)
488 }
489
490 pub(crate) fn module(&self) -> &casper_wasm::elements::Module {
491 &self.module
492 }
493
494 pub(crate) fn code(&self) -> &Vec<isa::Instructions> {
495 &self.code_map
496 }
497}