Files
wasmer/lib/api
bors[bot] 43b3d4b0cd Merge #1822
1822: fix(vm) Fix memory leak of `InstanceHandle` r=syrusakbary a=Hywan

# Description

In `wasmer_engine::Artifact::instantiate`, the engine artifact
allocates a new instance by calling
`wasmer_vm::InstanceHandle::allocate_instance`. To make it short, this
function calculates the [`Layout`] of an `wasmer_vm::Instance`, and
allocates space for it by calling [`alloc`]. This last part is
important because it means we are responsible to deallocate it with
[`dealloc`].

The pointer to this `wasmer_vm::Instance` is stored in the
`wasmer_vm::InstanceHandle` structure. That's the handle that is
returned by the engine artifact `Artifact::instantiate` method.

This instance handle is then stored in `wasmer::Instance` (through the
intervention of `wasmer::Module`).

How is it freed? It wasn't. That's the leak. [`dealloc`] was never
called.

How to free it? We must call [`dealloc`]. There is even a
`wasmer_vm::InstanceHandle::deallocate` helper to do that
properly. Neat!

When to free it? That's the tricky part. A `wasmer::Instance` can be
clonable. To do so, `wasmer_vm::InstanceHandle` must be clonable
too. There was a `Clone` implementation, that was constructing a new
`wasmer_vm:InstanceHandle` by using the same pointer to
`wasmer_vm::Instance`. That's annoying because it's the best way to
get many pointers that point to the same `wasmer_vm::Instance` in the
nature, and it's difficult to track them.

This patch changes the paradigm. There is only one and unique
`wasmer_vm::InstanceHandle` per `wasmer::Instance`, including its
clones. The handle is now stored inside a
`Arc<Mutex<wasmer_vm::InstanceHandle>>`. Consequently, when a
`wasmer::Instance` is cloned, it uses the same
`wasmer_vm::InstanceHandle`, not a clone of it.

Bonus: `wasmer::Instance` continues to be `Send` + `Sync`.

So. Let's back to our question. When to free
`wasmer_vm::InstanceHandle`? Response: When `wasmer::Instance` is
dropped. Right? There is a unique path from `wasmer::Instance`, to
`wasmer_vm::InstanceHandle`, to `wasmer_vm::Instance` now. So we just
need to call `wasmer_vm::InstanceHandle::dealloc` in a specific `Drop`
implementation for `wasmer_vm::InstanceHandle`, and the Rust borrow
checker does the rest.

Yes. … No. There is another use case: It is possible to create a
`wasmer_vm::InstanceHandle` with `InstanceHandle::from_vmctx`. Indeed,
a `wasmer_vm::VMContext` also stores a pointer to
`wasmer_vm::Instance`. In this, we consider `wasmer_vm::VMContext`
owns the instance pointer, somehow, and is responsible to free it
properly.

Consequently, we need another flag inside `wasmer_vm::InstanceHandle`
to know whether this structure owns the pointer to
`wasmer_vm::Instance` or not.

So. Let's back to our question. When to free
`wasmer_vm::InstanceHandle`? Response: Inside the `Drop`
implementation of `wasmer_vm::InstanceHandle` with its `Self::dealloc`
method if and only if the handle owns the pointer to
`wasmer_vm::Instance`.

Testing with Valgrind shows that the leak has been removed.

[`Layout`]: https://doc.rust-lang.org/std/alloc/struct.Layout.html
[`alloc`]: https://doc.rust-lang.org/std/alloc/fn.alloc.html
[`dealloc`]: https://doc.rust-lang.org/std/alloc/fn.dealloc.html

# Review

- [ ] Add a short description of the the change to the CHANGELOG.md file


Co-authored-by: Ivan Enderlin <ivan@mnt.io>
2020-11-19 18:14:04 +00:00
..
2020-11-19 18:14:04 +00:00
2020-10-27 22:13:57 +00:00
2020-11-06 11:50:07 -08:00
2020-09-15 23:54:38 +03:00

wasmer Build Status Join Wasmer Slack MIT License

Wasmer is the most popular WebAssembly runtime for Rust (...and also the fastest!). It supports JIT (Just in Time) and AOT (Ahead of time) compilation as well as pluggable compilers suited to your needs.

It's designed to be safe and secure, and runnable in any kind of environment.

Usage

Add to your Cargo.toml

[dependencies]
wasmer = "1.0.0-alpha"
use wasmer::{Store, Module, Instance, Value, imports};

fn main() -> anyhow::Result<()> {
    let module_wat = r#"
    (module
    (type $t0 (func (param i32) (result i32)))
    (func $add_one (export "add_one") (type $t0) (param $p0 i32) (result i32)
        get_local $p0
        i32.const 1
        i32.add))
    "#;

    let store = Store::default();
    let module = Module::new(&store, &module_wat);
    // The module doesn't import anything, so we create an empty import object.
    let import_object = imports! {};
    let instance = Instance::new(&module, &import_object)?;

    let add_one = instance.exports.get_function("add_one")?;
    let result = add_one.call([Value::I32(42)])?;
    assert_eq!(result[0], Value::I32(43));

    Ok(())
}

Features

Wasmer is not only fast, but also designed to be highly customizable:

  • Pluggable Engines: do you have a fancy dlopen implementation? This is for you!
  • Pluggable Compilers: you want to emit code with DynASM or other compiler? We got you!
  • Headless mode: that means that no compilers will be required to run a serialized Module (via Module::deserialize()).
  • Cross-compilation: You can pre-compile a module and serialize it to then run it in other platform (via Module::serialize()).

Config flags

Wasmer has the following configuration flags:

  • wat (enabled by default): It allows to read WebAssembly files in their text format. This feature is normally used only in development environments
  • Compilers (mutually exclusive):
    • singlepass: it will use wasmer-compiler-singlepass as the default compiler (ideal for blockchains).
    • cranelift: it will use wasmer-compiler-cranelift as the default compiler (ideal for development).
    • llvm: it will use wasmer-compiler-llvm as the default compiler (ideal for production).

Wasmer ships by default with the cranelift compiler as its great for development proposes. However, we strongly encourage to use the llvm backend in production as it performs about 50% faster, achieving near-native speeds.

Note: if you want to use multiple compilers at the same time, it's also possible! You will need to import them directly via each of the compiler crates.


Made with ❤️ by the Wasmer team, for the community