Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Corruption of WasmerEnv when using call indirect to host fn from Wasm with Singlepass #2329

Closed
thedavidmeister opened this issue May 21, 2021 · 5 comments
Labels
bug Something isn't working 📦 lib-compiler-singlepass About wasmer-compiler-singlepass
Milestone

Comments

@thedavidmeister
Copy link

Describe the bug

If a host function is being called from different crates in the guest then on the host side the env points to garbage, which returns None for memory and can simply panic for other fields.

Steps to reproduce

I made a repo - https://github.com/thedavidmeister/wasmer-env-memory

Expected behavior

Host consistently finds memory and other env fields.

Actual behavior

Host finds None and/or panics due to other env fields pointing to garbage.

Additional context

There is a workaround/fix which is adding #[inline(always)] to any guest function that:

  • calls a host function
  • expects to be used by other crates

https://github.com/thedavidmeister/wasmer-env-memory/blob/master/other/lib.rs#L1

@thedavidmeister thedavidmeister added the bug Something isn't working label May 21, 2021
@thedavidmeister
Copy link
Author

cc @MarkMcCaskey

@MarkMcCaskey MarkMcCaskey changed the title wasmer memory and other env properties goes missing across crate boundaries Corruption of WasmerEnv when using call indirect from Wasm with Singlepass May 21, 2021
@MarkMcCaskey
Copy link
Contributor

Thanks for the bug report! I did a deep dive on this and found out the cause. It seems to be a bug only affecting singlepass.

The WAT that causes the failure:

(module
  (type (;0;) (func (param i32) (result i32)))
  (type (;1;) (func))
  (type (;2;) (func (param i32 i32) (result i32)))
  (import "env" "__read_memory" (func $__read_memory (type 0)))
  (func $read_memory (type 1)
    (drop
      (call $_ZN5other8dispatch17h053cb34ef5d0d7b0E
        (i32.const 1)
        (i32.const 2)))
    (drop
      (call $__read_memory
        (i32.const 1))))
  (func $_ZN5other8dispatch17h053cb34ef5d0d7b0E (type 2) (param i32 i32) (result i32)
    (call_indirect (type 0)
      (local.get 1)
      (local.get 0)))
  (table (;0;) 2 2 funcref)
  (memory (;0;) 16)
  (global (;0;) (mut i32) (i32.const 1048576))
  (global (;1;) i32 (i32.const 1048576))
  (global (;2;) i32 (i32.const 1048576))
  (export "memory" (memory 0))
  (export "read_memory" (func $read_memory))
  (export "__data_end" (global 1))
  (export "__heap_base" (global 2))
  (elem (;0;) (i32.const 1) func $__read_memory))

The version with inlining that fixes this

(module
  (type (;0;) (func (param i32) (result i32)))
  (type (;1;) (func))
  (import "env" "__read_memory" (func $__read_memory (type 0)))
  (func $read_memory (type 1)
    (drop
      (call $__read_memory
        (i32.const 2)))
    (drop
      (call $__read_memory
        (i32.const 1))))
  (table (;0;) 1 1 funcref)
  (memory (;0;) 16)
  (global (;0;) (mut i32) (i32.const 1048576))
  (global (;1;) i32 (i32.const 1048576))
  (global (;2;) i32 (i32.const 1048576))
  (export "memory" (memory 0))
  (export "read_memory" (func $read_memory))
  (export "__data_end" (global 1))
  (export "__heap_base" (global 2)))

Additionally, running the Wasm through wasm-opt will inline the call and side-step this bug (I imagine compiling with full LTO also avoids it). Just as a side note, it's always good to ensure your Wasm is optimized before giving it to Wasmer, though in this case I'm glad you didn't so we could find this bug 😆 .

@MarkMcCaskey MarkMcCaskey changed the title Corruption of WasmerEnv when using call indirect from Wasm with Singlepass Corruption of WasmerEnv when using call indirect to host fn from Wasm with Singlepass May 21, 2021
@MarkMcCaskey MarkMcCaskey added the 📦 lib-compiler-singlepass About wasmer-compiler-singlepass label May 21, 2021
@olonho
Copy link

olonho commented Jul 28, 2021

Can be fixed along those lines:

diff --git a/lib/compiler-singlepass/src/codegen_x64.rs b/lib/compiler-singlepass/src/codegen_x64.rs
index bd02ddc2a..0444fcf82 100644
--- a/lib/compiler-singlepass/src/codegen_x64.rs
+++ b/lib/compiler-singlepass/src/codegen_x64.rs
@@ -5388,6 +5388,8 @@ impl<'a> FuncGen<'a> {
 
                 let vmcaller_checked_anyfunc_func_ptr =
                     self.vmoffsets.vmcaller_checked_anyfunc_func_ptr() as usize;
+                let vmcaller_checked_anyfunc_vmctx =
+                    self.vmoffsets.vmcaller_checked_anyfunc_vmctx() as usize;
 
                 self.emit_call_sysv(
                     |this| {
@@ -5403,6 +5405,11 @@ impl<'a> FuncGen<'a> {
                             this.trap_table
                                 .offset_to_code
                                 .insert(offset, TrapCode::StackOverflow);
+                            // Prepare context pointer, it isn't always vmctx.
+                            this.assembler.emit_mov(
+                                Size::S64,
+                                Location::Memory(GPR::RAX, vmcaller_checked_anyfunc_vmctx as i32),
+                                Machine::get_param_location(0));
                             this.assembler.emit_call_location(Location::Memory(
                                 GPR::RAX,
                                 vmcaller_checked_anyfunc_func_ptr as i32,

@syrusakbary
Copy link
Member

The fix looks correct. I've opened a PR to test it a bit more throughly

syrusakbary added a commit that referenced this issue Jul 29, 2021
@syrusakbary
Copy link
Member

This is now fixed in master: #2494. Closing the issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working 📦 lib-compiler-singlepass About wasmer-compiler-singlepass
Projects
None yet
Development

No branches or pull requests

5 participants