patract_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 patract_wasmi;
52//! extern crate wabt;
53//!
54//! use patract_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//!         wabt::wat2wasm(
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 = patract_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#![allow(clippy::new_ret_no_self)]
101
102#[cfg(not(feature = "std"))]
103#[macro_use]
104extern crate alloc;
105#[cfg(feature = "std")]
106extern crate std as alloc;
107
108#[cfg(feature = "std")]
109#[macro_use]
110extern crate core;
111
112#[cfg(test)]
113extern crate assert_matches;
114#[cfg(test)]
115extern crate wabt;
116
117use alloc::{
118    boxed::Box,
119    string::{String, ToString},
120    vec::Vec,
121};
122use core::fmt;
123#[cfg(feature = "std")]
124use std::error;
125
126#[cfg(not(feature = "std"))]
127extern crate libm;
128
129extern crate num_rational;
130extern crate num_traits;
131
132/// Error type which can be thrown by wasm code or by host environment.
133///
134/// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution.
135/// Traps can't be handled by WebAssembly code, but are reported to the embedder.
136#[derive(Debug)]
137pub struct Trap {
138    kind: TrapKind,
139    wasm_trace: Vec<String>,
140}
141
142impl Trap {
143    /// Create new trap.
144    pub fn new(kind: TrapKind) -> Trap {
145        Trap {
146            kind,
147            wasm_trace: vec![],
148        }
149    }
150
151    /// Embed a new context into wasm trace
152    pub fn wasm_trace_with(mut self, info: Option<&(usize, String)>) -> Trap {
153        if let Some(info) = info {
154            self.wasm_trace.push(format!(
155                "{:#}[{}]",
156                rustc_demangle::demangle(&info.1),
157                info.0
158            ));
159        }
160        self
161    }
162
163    /// Embed a new context into wasm trace
164    pub fn wasm_trace_with_kind(kind: TrapKind, info: Option<&(usize, String)>) -> Trap {
165        let mut trap: Self = kind.into();
166        if let Some(info) = info {
167            trap.wasm_trace.push(format!(
168                "{:#}[{}]",
169                rustc_demangle::demangle(&info.1),
170                info.0
171            ));
172        }
173        trap
174    }
175
176    /// Returns wasm trace
177    pub fn wasm_trace(&self) -> &Vec<String> {
178        &self.wasm_trace
179    }
180
181    /// Set wasm trace
182    pub fn set_wasm_trace(mut self, trace: Vec<String>) -> Self {
183        self.wasm_trace = trace;
184        self
185    }
186
187    /// Returns kind of this trap.
188    pub fn kind(&self) -> &TrapKind {
189        &self.kind
190    }
191
192    /// Converts into kind of this trap.
193    pub fn into_kind(self) -> TrapKind {
194        self.kind
195    }
196}
197
198impl fmt::Display for Trap {
199    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
200        write!(
201            f,
202            "Trap: {:?} \n \
203             WASM Trace: {:?}",
204            self.kind, self.wasm_trace,
205        )
206    }
207}
208
209#[cfg(feature = "std")]
210impl error::Error for Trap {
211    fn description(&self) -> &str {
212        "runtime trap"
213    }
214}
215
216/// Error type which can be thrown by wasm code or by host environment.
217///
218/// See [`Trap`] for details.
219///
220/// [`Trap`]: struct.Trap.html
221#[derive(Debug)]
222pub enum TrapKind {
223    /// Wasm code executed `unreachable` opcode.
224    ///
225    /// `unreachable` is a special opcode which always traps upon execution.
226    /// This opcode have a similar purpose as `ud2` in x86.
227    Unreachable,
228
229    /// Attempt to load or store at the address which
230    /// lies outside of bounds of the memory.
231    ///
232    /// Since addresses are interpreted as unsigned integers, out of bounds access
233    /// can't happen with negative addresses (i.e. they will always wrap).
234    MemoryAccessOutOfBounds,
235
236    /// Attempt to access table element at index which
237    /// lies outside of bounds.
238    ///
239    /// This typically can happen when `call_indirect` is executed
240    /// with index that lies out of bounds.
241    ///
242    /// Since indexes are interpreted as unsinged integers, out of bounds access
243    /// can't happen with negative indexes (i.e. they will always wrap).
244    TableAccessOutOfBounds,
245
246    /// Attempt to access table element which is uninitialized (i.e. `None`).
247    ///
248    /// This typically can happen when `call_indirect` is executed.
249    ElemUninitialized,
250
251    /// Attempt to divide by zero.
252    ///
253    /// This trap typically can happen if `div` or `rem` is executed with
254    /// zero as divider.
255    DivisionByZero,
256
257    /// Attempt to make a conversion to an int failed.
258    ///
259    /// This can happen when:
260    ///
261    /// - trying to do signed division (or get the remainder) -2<sup>N-1</sup> over -1. This is
262    ///   because the result +2<sup>N-1</sup> isn't representable as a N-bit signed integer.
263    /// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer.
264    InvalidConversionToInt,
265
266    /// Stack overflow.
267    ///
268    /// This is likely caused by some infinite or very deep recursion.
269    /// Extensive inlining might also be the cause of stack overflow.
270    StackOverflow,
271
272    /// Attempt to invoke a function with mismatching signature.
273    ///
274    /// This can happen if [`FuncInstance`] was invoked
275    /// with mismatching [signature][`Signature`].
276    ///
277    /// This can always happen with indirect calls. `call_indirect` instruction always
278    /// specifies the expected signature of function. If `call_indirect` is executed
279    /// with index that points on function with signature different that is
280    /// expected by this `call_indirect`, this trap is raised.
281    ///
282    /// [`Signature`]: struct.Signature.html
283    UnexpectedSignature,
284
285    /// Error specified by the host.
286    ///
287    /// Typically returned from an implementation of [`Externals`].
288    ///
289    /// [`Externals`]: trait.Externals.html
290    Host(Box<dyn host::HostError>),
291}
292
293impl TrapKind {
294    /// Whether this trap is specified by the host.
295    pub fn is_host(&self) -> bool {
296        matches!(self, TrapKind::Host(_))
297    }
298}
299
300/// Internal interpreter error.
301#[derive(Debug)]
302pub enum Error {
303    /// Module validation error. Might occur only at load time.
304    Validation(String),
305    /// Error while instantiating a module. Might occur when provided
306    /// with incorrect exports (i.e. linkage failure).
307    Instantiation(String),
308    /// Function-level error.
309    Function(String),
310    /// Table-level error.
311    Table(String),
312    /// Memory-level error.
313    Memory(String),
314    /// Global-level error.
315    Global(String),
316    /// Value-level error.
317    Value(String),
318    /// Trap.
319    Trap(Trap),
320    /// Custom embedder error.
321    Host(Box<dyn host::HostError>),
322}
323
324impl Error {
325    /// Returns a reference to a [`HostError`] if this `Error` represents some host error.
326    ///
327    /// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error.
328    ///
329    /// [`HostError`]: trait.HostError.html
330    /// [`Host`]: enum.Error.html#variant.Host
331    /// [`Trap`]: enum.Error.html#variant.Trap
332    /// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
333    pub fn as_host_error(&self) -> Option<&dyn host::HostError> {
334        match self {
335            Error::Host(host_err) => Some(&**host_err),
336            Error::Trap(Trap {
337                kind: TrapKind::Host(host_err),
338                ..
339            }) => Some(&**host_err),
340            _ => None,
341        }
342    }
343
344    /// Returns [`HostError`] if this `Error` represents some host error.
345    ///
346    /// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error.
347    ///
348    /// [`HostError`]: trait.HostError.html
349    /// [`Host`]: enum.Error.html#variant.Host
350    /// [`Trap`]: enum.Error.html#variant.Trap
351    /// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
352    pub fn into_host_error(self) -> Option<Box<dyn host::HostError>> {
353        match self {
354            Error::Host(host_err) => Some(host_err),
355            Error::Trap(Trap {
356                kind: TrapKind::Host(host_err),
357                ..
358            }) => Some(host_err),
359            _ => None,
360        }
361    }
362
363    /// Returns [`HostError`] if this `Error` represents some host error, otherwise returns the original error.
364    ///
365    /// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error.
366    ///
367    /// [`HostError`]: trait.HostError.html
368    /// [`Host`]: enum.Error.html#variant.Host
369    /// [`Trap`]: enum.Error.html#variant.Trap
370    /// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
371    pub fn try_into_host_error(self) -> Result<Box<dyn host::HostError>, Self> {
372        match self {
373            Error::Host(host_err) => Ok(host_err),
374            Error::Trap(Trap {
375                kind: TrapKind::Host(host_err),
376                ..
377            }) => Ok(host_err),
378            other => Err(other),
379        }
380    }
381}
382
383#[allow(clippy::from_over_into)]
384impl Into<String> for Error {
385    fn into(self) -> String {
386        match self {
387            Error::Validation(s) => s,
388            Error::Instantiation(s) => s,
389            Error::Function(s) => s,
390            Error::Table(s) => s,
391            Error::Memory(s) => s,
392            Error::Global(s) => s,
393            Error::Value(s) => s,
394            Error::Trap(s) => format!("trap: {:?}", s),
395            Error::Host(e) => format!("user: {}", e),
396        }
397    }
398}
399
400impl fmt::Display for Error {
401    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
402        match *self {
403            Error::Validation(ref s) => write!(f, "Validation: {}", s),
404            Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s),
405            Error::Function(ref s) => write!(f, "Function: {}", s),
406            Error::Table(ref s) => write!(f, "Table: {}", s),
407            Error::Memory(ref s) => write!(f, "Memory: {}", s),
408            Error::Global(ref s) => write!(f, "Global: {}", s),
409            Error::Value(ref s) => write!(f, "Value: {}", s),
410            Error::Trap(ref s) => write!(f, "Trap: {:?}", s),
411            Error::Host(ref e) => write!(f, "User: {}", e),
412        }
413    }
414}
415
416#[cfg(feature = "std")]
417impl error::Error for Error {
418    fn description(&self) -> &str {
419        match *self {
420            Error::Validation(ref s) => s,
421            Error::Instantiation(ref s) => s,
422            Error::Function(ref s) => s,
423            Error::Table(ref s) => s,
424            Error::Memory(ref s) => s,
425            Error::Global(ref s) => s,
426            Error::Value(ref s) => s,
427            Error::Trap(_) => "Trap",
428            Error::Host(_) => "Host error",
429        }
430    }
431}
432
433impl<U> From<U> for Error
434where
435    U: host::HostError + Sized,
436{
437    fn from(e: U) -> Self {
438        Error::Host(Box::new(e))
439    }
440}
441
442impl<U> From<U> for Trap
443where
444    U: host::HostError + Sized,
445{
446    fn from(e: U) -> Self {
447        Trap::new(TrapKind::Host(Box::new(e)))
448    }
449}
450
451impl From<Trap> for Error {
452    fn from(e: Trap) -> Error {
453        Error::Trap(e)
454    }
455}
456
457impl From<TrapKind> for Trap {
458    fn from(e: TrapKind) -> Trap {
459        Trap::new(e)
460    }
461}
462
463impl From<validation::Error> for Error {
464    fn from(e: validation::Error) -> Error {
465        Error::Validation(e.to_string())
466    }
467}
468
469mod func;
470mod global;
471mod host;
472mod imports;
473mod isa;
474mod memory;
475mod module;
476pub mod nan_preserving_float;
477mod prepare;
478mod runner;
479mod table;
480mod types;
481mod value;
482
483#[cfg(test)]
484mod tests;
485
486pub use self::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError};
487pub use self::global::{GlobalInstance, GlobalRef};
488pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs};
489pub use self::imports::{ImportResolver, ImportsBuilder, ModuleImportResolver};
490pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
491pub use self::module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef};
492pub use self::runner::{StackRecycler, DEFAULT_CALL_STACK_LIMIT, DEFAULT_VALUE_STACK_LIMIT};
493pub use self::table::{TableInstance, TableRef};
494pub use self::types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType};
495pub use self::value::{Error as ValueError, FromRuntimeValue, LittleEndianConvert, RuntimeValue};
496
497/// WebAssembly-specific sizes and units.
498pub mod memory_units {
499    pub use memory_units::wasm32::*;
500    pub use memory_units::{size_of, ByteSize, Bytes, RoundUpTo};
501}
502
503/// Deserialized module prepared for instantiation.
504pub struct Module {
505    code_map: Vec<isa::Instructions>,
506    module: parity_wasm::elements::Module,
507}
508
509impl Module {
510    /// Create `Module` from `parity_wasm::elements::Module`.
511    ///
512    /// This function will load, validate and prepare a `parity_wasm`'s `Module`.
513    ///
514    /// # Errors
515    ///
516    /// Returns `Err` if provided `Module` is not valid.
517    ///
518    /// # Examples
519    ///
520    /// ```rust
521    /// extern crate parity_wasm;
522    /// extern crate patract_wasmi;
523    ///
524    /// use parity_wasm::builder;
525    /// use parity_wasm::elements;
526    ///
527    /// fn main() {
528    ///     let parity_module =
529    ///         builder::module()
530    ///             .function()
531    ///                 .signature().with_param(elements::ValueType::I32).build()
532    ///                 .body().build()
533    ///             .build()
534    ///         .build();
535    ///
536    ///     let module = patract_wasmi::Module::from_parity_wasm_module(parity_module)
537    ///         .expect("parity-wasm builder generated invalid module!");
538    ///
539    ///     // Instantiate `module`, etc...
540    /// }
541    /// ```
542    pub fn from_parity_wasm_module(module: parity_wasm::elements::Module) -> Result<Module, Error> {
543        let prepare::CompiledModule { code_map, module } = prepare::compile_module(module)?;
544        Ok(Module { code_map, module })
545    }
546
547    /// Fail if the module contains any floating-point operations
548    ///
549    /// # Errors
550    ///
551    /// Returns `Err` if provided `Module` is not valid.
552    ///
553    /// # Examples
554    ///
555    /// ```rust
556    /// # extern crate patract_wasmi;
557    /// # extern crate wabt;
558    ///
559    /// let wasm_binary: Vec<u8> =
560    ///     wabt::wat2wasm(
561    ///         r#"
562    ///         (module
563    ///          (func $add (param $lhs i32) (param $rhs i32) (result i32)
564    ///                get_local $lhs
565    ///                get_local $rhs
566    ///                i32.add))
567    ///         "#,
568    ///     )
569    ///     .expect("failed to parse wat");
570    ///
571    /// // Load wasm binary and prepare it for instantiation.
572    /// let module = patract_wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
573    /// assert!(module.deny_floating_point().is_ok());
574    ///
575    /// let wasm_binary: Vec<u8> =
576    ///     wabt::wat2wasm(
577    ///         r#"
578    ///         (module
579    ///          (func $add (param $lhs f32) (param $rhs f32) (result f32)
580    ///                get_local $lhs
581    ///                get_local $rhs
582    ///                f32.add))
583    ///         "#,
584    ///     )
585    ///     .expect("failed to parse wat");
586    ///
587    /// let module = patract_wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
588    /// assert!(module.deny_floating_point().is_err());
589    ///
590    /// let wasm_binary: Vec<u8> =
591    ///     wabt::wat2wasm(
592    ///         r#"
593    ///         (module
594    ///          (func $add (param $lhs f32) (param $rhs f32) (result f32)
595    ///                get_local $lhs))
596    ///         "#,
597    ///     )
598    ///     .expect("failed to parse wat");
599    ///
600    /// let module = patract_wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
601    /// assert!(module.deny_floating_point().is_err());
602    /// ```
603    pub fn deny_floating_point(&self) -> Result<(), Error> {
604        prepare::deny_floating_point(&self.module).map_err(Into::into)
605    }
606
607    /// Create `Module` from a given buffer.
608    ///
609    /// This function will deserialize wasm module from a given module,
610    /// validate and prepare it for instantiation.
611    ///
612    /// # Errors
613    ///
614    /// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary.
615    ///
616    /// # Examples
617    ///
618    /// ```rust
619    /// extern crate patract_wasmi;
620    ///
621    /// fn main() {
622    ///     let module =
623    ///         patract_wasmi::Module::from_buffer(
624    ///             // Minimal module:
625    ///             //   \0asm - magic
626    ///             //    0x01 - version (in little-endian)
627    ///             &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]
628    ///         ).expect("Failed to load minimal module");
629    ///
630    ///     // Instantiate `module`, etc...
631    /// }
632    /// ```
633    pub fn from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Module, Error> {
634        let module = parity_wasm::elements::deserialize_buffer(buffer.as_ref())
635            .map_err(|e: parity_wasm::elements::Error| Error::Validation(e.to_string()))?;
636        Module::from_parity_wasm_module(module)
637    }
638
639    /// Try to parse name section in place.
640    ///
641    /// Corresponding custom section with proper header will convert to name sections
642    /// If some of them will fail to be decoded, Err variant is returned with the list of
643    /// (index, Error) tuples of failed sections.
644    pub fn parse_names(mut self) -> Result<Module, Error> {
645        self.module = self
646            .module
647            .parse_names()
648            .map_err(|_| Error::Instantiation("Failed to parse name sections".into()))?;
649        Ok(self)
650    }
651
652    /// Same as `parse_names`, but without error returns.
653    pub fn try_parse_names(mut self) -> Module {
654        self.module = match self.module.parse_names() {
655            Ok(module) => module,
656            Err((_, module)) => module,
657        };
658
659        self
660    }
661
662    pub(crate) fn name_map(&self) -> Option<&parity_wasm::elements::NameMap> {
663        Some(self.module().names_section()?.functions()?.names())
664    }
665
666    pub(crate) fn module(&self) -> &parity_wasm::elements::Module {
667        &self.module
668    }
669
670    pub(crate) fn code(&self) -> &Vec<isa::Instructions> {
671        &self.code_map
672    }
673}