susy_wasmi/
lib.rs

1//! # susy-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 susy_wasmi;
52//! extern crate wabt;
53//!
54//! use susy_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 = susy_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//// alloc is required in no_std
100#![cfg_attr(not(feature = "std"), feature(alloc, alloc_prelude))]
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
117#[cfg(not(feature = "std"))]
118extern crate hashbrown;
119extern crate memory_units as memory_units_crate;
120extern crate susy_wasm;
121
122extern crate susy_wasmi_validation as validation;
123
124#[allow(unused_imports)]
125use alloc::prelude::v1::*;
126use core::fmt;
127#[cfg(feature = "std")]
128use std::error;
129
130#[cfg(not(feature = "std"))]
131extern crate libm;
132
133/// Error type which can be thrown by wasm code or by host environment.
134///
135/// Under some conditions, wasm execution may produce a `Trap`, which immediately aborts execution.
136/// Traps can't be handled by WebAssembly code, but are reported to the embedder.
137#[derive(Debug)]
138pub struct Trap {
139    kind: TrapKind,
140}
141
142impl Trap {
143    /// Create new trap.
144    pub fn new(kind: TrapKind) -> Trap {
145        Trap { kind }
146    }
147
148    /// Returns kind of this trap.
149    pub fn kind(&self) -> &TrapKind {
150        &self.kind
151    }
152}
153
154impl fmt::Display for Trap {
155    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
156        write!(f, "Trap: {:?}", self.kind)
157    }
158}
159
160#[cfg(feature = "std")]
161impl error::Error for Trap {
162    fn description(&self) -> &str {
163        "runtime trap"
164    }
165}
166
167/// Error type which can be thrown by wasm code or by host environment.
168///
169/// See [`Trap`] for details.
170///
171/// [`Trap`]: struct.Trap.html
172#[derive(Debug)]
173pub enum TrapKind {
174    /// Wasm code executed `unreachable` opcode.
175    ///
176    /// `unreachable` is a special opcode which always traps upon execution.
177    /// This opcode have a similar purpose as `ud2` in x86.
178    Unreachable,
179
180    /// Attempt to load or store at the address which
181    /// lies outside of bounds of the memory.
182    ///
183    /// Since addresses are interpreted as unsigned integers, out of bounds access
184    /// can't happen with negative addresses (i.e. they will always wrap).
185    MemoryAccessOutOfBounds,
186
187    /// Attempt to access table element at index which
188    /// lies outside of bounds.
189    ///
190    /// This typically can happen when `call_indirect` is executed
191    /// with index that lies out of bounds.
192    ///
193    /// Since indexes are interpreted as unsinged integers, out of bounds access
194    /// can't happen with negative indexes (i.e. they will always wrap).
195    TableAccessOutOfBounds,
196
197    /// Attempt to access table element which is uninitialized (i.e. `None`).
198    ///
199    /// This typically can happen when `call_indirect` is executed.
200    ElemUninitialized,
201
202    /// Attempt to divide by zero.
203    ///
204    /// This trap typically can happen if `div` or `rem` is executed with
205    /// zero as divider.
206    DivisionByZero,
207
208    /// Attempt to make a conversion to an int failed.
209    ///
210    /// This can happen when:
211    ///
212    /// - trying to do signed division (or get the remainder) -2<sup>N-1</sup> over -1. This is
213    ///   because the result +2<sup>N-1</sup> isn't representable as a N-bit signed integer.
214    /// - trying to truncate NaNs, infinity, or value for which the result is out of range into an integer.
215    InvalidConversionToInt,
216
217    /// Stack overflow.
218    ///
219    /// This is likely caused by some infinite or very deep recursion.
220    /// Extensive inlining might also be the cause of stack overflow.
221    StackOverflow,
222
223    /// Attempt to invoke a function with mismatching signature.
224    ///
225    /// This can happen if [`FuncInstance`] was invoked
226    /// with mismatching [signature][`Signature`].
227    ///
228    /// This can always happen with indirect calls. `call_indirect` instruction always
229    /// specifies the expected signature of function. If `call_indirect` is executed
230    /// with index that points on function with signature different that is
231    /// expected by this `call_indirect`, this trap is raised.
232    ///
233    /// [`Signature`]: struct.Signature.html
234    UnexpectedSignature,
235
236    /// Error specified by the host.
237    ///
238    /// Typically returned from an implementation of [`Externals`].
239    ///
240    /// [`Externals`]: trait.Externals.html
241    Host(Box<host::HostError>),
242}
243
244impl TrapKind {
245    /// Whether this trap is specified by the host.
246    pub fn is_host(&self) -> bool {
247        match self {
248            &TrapKind::Host(_) => true,
249            _ => false,
250        }
251    }
252}
253
254/// Internal interpreter error.
255#[derive(Debug)]
256pub enum Error {
257    /// Module validation error. Might occur only at load time.
258    Validation(String),
259    /// Error while instantiating a module. Might occur when provided
260    /// with incorrect exports (i.e. linkage failure).
261    Instantiation(String),
262    /// Function-level error.
263    Function(String),
264    /// Table-level error.
265    Table(String),
266    /// Memory-level error.
267    Memory(String),
268    /// Global-level error.
269    Global(String),
270    /// Value-level error.
271    Value(String),
272    /// Trap.
273    Trap(Trap),
274    /// Custom embedder error.
275    Host(Box<host::HostError>),
276}
277
278impl Error {
279    /// Returns [`HostError`] if this `Error` represents some host error.
280    ///
281    /// I.e. if this error have variant [`Host`] or [`Trap`][`Trap`] with [host][`TrapKind::Host`] error.
282    ///
283    /// [`HostError`]: trait.HostError.html
284    /// [`Host`]: enum.Error.html#variant.Host
285    /// [`Trap`]: enum.Error.html#variant.Trap
286    /// [`TrapKind::Host`]: enum.TrapKind.html#variant.Host
287    pub fn as_host_error(&self) -> Option<&host::HostError> {
288        match *self {
289            Error::Host(ref host_err) => Some(&**host_err),
290            Error::Trap(ref trap) => match *trap.kind() {
291                TrapKind::Host(ref host_err) => Some(&**host_err),
292                _ => None,
293            },
294            _ => None,
295        }
296    }
297}
298
299impl Into<String> for Error {
300    fn into(self) -> String {
301        match self {
302            Error::Validation(s) => s,
303            Error::Instantiation(s) => s,
304            Error::Function(s) => s,
305            Error::Table(s) => s,
306            Error::Memory(s) => s,
307            Error::Global(s) => s,
308            Error::Value(s) => s,
309            Error::Trap(s) => format!("trap: {:?}", s),
310            Error::Host(e) => format!("user: {}", e),
311        }
312    }
313}
314
315impl fmt::Display for Error {
316    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
317        match *self {
318            Error::Validation(ref s) => write!(f, "Validation: {}", s),
319            Error::Instantiation(ref s) => write!(f, "Instantiation: {}", s),
320            Error::Function(ref s) => write!(f, "Function: {}", s),
321            Error::Table(ref s) => write!(f, "Table: {}", s),
322            Error::Memory(ref s) => write!(f, "Memory: {}", s),
323            Error::Global(ref s) => write!(f, "Global: {}", s),
324            Error::Value(ref s) => write!(f, "Value: {}", s),
325            Error::Trap(ref s) => write!(f, "Trap: {:?}", s),
326            Error::Host(ref e) => write!(f, "User: {}", e),
327        }
328    }
329}
330
331#[cfg(feature = "std")]
332impl error::Error for Error {
333    fn description(&self) -> &str {
334        match *self {
335            Error::Validation(ref s) => s,
336            Error::Instantiation(ref s) => s,
337            Error::Function(ref s) => s,
338            Error::Table(ref s) => s,
339            Error::Memory(ref s) => s,
340            Error::Global(ref s) => s,
341            Error::Value(ref s) => s,
342            Error::Trap(_) => "Trap",
343            Error::Host(_) => "Host error",
344        }
345    }
346}
347
348impl<U> From<U> for Error
349where
350    U: host::HostError + Sized,
351{
352    fn from(e: U) -> Self {
353        Error::Host(Box::new(e))
354    }
355}
356
357impl<U> From<U> for Trap
358where
359    U: host::HostError + Sized,
360{
361    fn from(e: U) -> Self {
362        Trap::new(TrapKind::Host(Box::new(e)))
363    }
364}
365
366impl From<Trap> for Error {
367    fn from(e: Trap) -> Error {
368        Error::Trap(e)
369    }
370}
371
372impl From<TrapKind> for Trap {
373    fn from(e: TrapKind) -> Trap {
374        Trap::new(e)
375    }
376}
377
378impl From<validation::Error> for Error {
379    fn from(e: validation::Error) -> Error {
380        Error::Validation(e.to_string())
381    }
382}
383
384mod func;
385mod global;
386mod host;
387mod imports;
388mod isa;
389mod memory;
390mod module;
391pub mod nan_preserving_float;
392mod prepare;
393mod runner;
394mod table;
395mod types;
396mod value;
397
398#[cfg(test)]
399mod tests;
400
401pub use self::func::{FuncInstance, FuncInvocation, FuncRef, ResumableError};
402pub use self::global::{GlobalInstance, GlobalRef};
403pub use self::host::{Externals, HostError, NopExternals, RuntimeArgs};
404pub use self::imports::{ImportResolver, ImportsBuilder, ModuleImportResolver};
405pub use self::memory::{MemoryInstance, MemoryRef, LINEAR_MEMORY_PAGE_SIZE};
406pub use self::module::{ExternVal, ModuleInstance, ModuleRef, NotStartedModuleRef};
407pub use self::table::{TableInstance, TableRef};
408pub use self::types::{GlobalDescriptor, MemoryDescriptor, Signature, TableDescriptor, ValueType};
409pub use self::value::{Error as ValueError, FromRuntimeValue, LittleEndianConvert, RuntimeValue};
410
411/// WebAssembly-specific sizes and units.
412pub mod memory_units {
413    pub use memory_units_crate::wasm32::*;
414    pub use memory_units_crate::{size_of, ByteSize, Bytes, RoundUpTo};
415}
416
417/// Deserialized module prepared for instantiation.
418pub struct Module {
419    code_map: Vec<isa::Instructions>,
420    module: susy_wasm::elements::Module,
421}
422
423impl Module {
424    /// Create `Module` from `susy_wasm::elements::Module`.
425    ///
426    /// This function will load, validate and prepare a `susy_wasm`'s `Module`.
427    ///
428    /// # Errors
429    ///
430    /// Returns `Err` if provided `Module` is not valid.
431    ///
432    /// # Examples
433    ///
434    /// ```rust
435    /// extern crate susy_wasm;
436    /// extern crate susy_wasmi;
437    ///
438    /// use susy_wasm::builder;
439    /// use susy_wasm::elements;
440    ///
441    /// fn main() {
442    ///     let susy_module =
443    ///         builder::module()
444    ///             .function()
445    ///                 .signature().with_param(elements::ValueType::I32).build()
446    ///                 .body().build()
447    ///             .build()
448    ///         .build();
449    ///
450    ///     let module = susy_wasmi::Module::from_susy_wasm_module(susy_module)
451    ///         .expect("susy-wasm builder generated invalid module!");
452    ///
453    ///     // Instantiate `module`, etc...
454    /// }
455    /// ```
456    pub fn from_susy_wasm_module(module: susy_wasm::elements::Module) -> Result<Module, Error> {
457        let prepare::CompiledModule { code_map, module } = prepare::compile_module(module)?;
458
459        Ok(Module { code_map, module })
460    }
461
462    /// Fail if the module contains any floating-point operations
463    ///
464    /// # Errors
465    ///
466    /// Returns `Err` if provided `Module` is not valid.
467    ///
468    /// # Examples
469    ///
470    /// ```rust
471    /// # extern crate susy_wasmi;
472    /// # extern crate wabt;
473    ///
474    /// let wasm_binary: Vec<u8> =
475    ///     wabt::wat2wasm(
476    ///         r#"
477    ///         (module
478    ///          (func $add (param $lhs i32) (param $rhs i32) (result i32)
479    ///                get_local $lhs
480    ///                get_local $rhs
481    ///                i32.add))
482    ///         "#,
483    ///     )
484    ///     .expect("failed to parse wat");
485    ///
486    /// // Load wasm binary and prepare it for instantiation.
487    /// let module = susy_wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
488    /// assert!(module.deny_floating_point().is_ok());
489    ///
490    /// let wasm_binary: Vec<u8> =
491    ///     wabt::wat2wasm(
492    ///         r#"
493    ///         (module
494    ///          (func $add (param $lhs f32) (param $rhs f32) (result f32)
495    ///                get_local $lhs
496    ///                get_local $rhs
497    ///                f32.add))
498    ///         "#,
499    ///     )
500    ///     .expect("failed to parse wat");
501    ///
502    /// let module = susy_wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
503    /// assert!(module.deny_floating_point().is_err());
504    ///
505    /// let wasm_binary: Vec<u8> =
506    ///     wabt::wat2wasm(
507    ///         r#"
508    ///         (module
509    ///          (func $add (param $lhs f32) (param $rhs f32) (result f32)
510    ///                get_local $lhs))
511    ///         "#,
512    ///     )
513    ///     .expect("failed to parse wat");
514    ///
515    /// let module = susy_wasmi::Module::from_buffer(&wasm_binary).expect("Parsing failed");
516    /// assert!(module.deny_floating_point().is_err());
517    /// ```
518    pub fn deny_floating_point(&self) -> Result<(), Error> {
519        prepare::deny_floating_point(&self.module).map_err(Into::into)
520    }
521
522    /// Create `Module` from a given buffer.
523    ///
524    /// This function will deserialize wasm module from a given module,
525    /// validate and prepare it for instantiation.
526    ///
527    /// # Errors
528    ///
529    /// Returns `Err` if wasm binary in provided `buffer` is not valid wasm binary.
530    ///
531    /// # Examples
532    ///
533    /// ```rust
534    /// extern crate susy_wasmi;
535    ///
536    /// fn main() {
537    ///     let module =
538    ///         susy_wasmi::Module::from_buffer(
539    ///             // Minimal module:
540    ///             //   \0asm - magic
541    ///             //    0x01 - version (in little-endian)
542    ///             &[0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]
543    ///         ).expect("Failed to load minimal module");
544    ///
545    ///     // Instantiate `module`, etc...
546    /// }
547    /// ```
548    pub fn from_buffer<B: AsRef<[u8]>>(buffer: B) -> Result<Module, Error> {
549        let module = susy_wasm::elements::deserialize_buffer(buffer.as_ref())
550            .map_err(|e: susy_wasm::elements::Error| Error::Validation(e.to_string()))?;
551        Module::from_susy_wasm_module(module)
552    }
553
554    pub(crate) fn module(&self) -> &susy_wasm::elements::Module {
555        &self.module
556    }
557
558    pub(crate) fn code(&self) -> &Vec<isa::Instructions> {
559        &self.code_map
560    }
561}