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}