wasm-c-api icon indicating copy to clipboard operation
wasm-c-api copied to clipboard

sigsegv when trying to access exported memory

Open devsnek opened this issue 5 years ago • 2 comments

  own<Trap> trap;
  auto instance = Instance::make(store.get(), module.get(), imports, &trap);
  if (trap) {
    PrintTrap(trap);
    return 1;
  }

  auto memory = instance.get()->exports()[0]->memory();
  assert(memory->kind() == wasm::EXTERN_MEMORY); // SIGSEGV here

backtrace:

(lldb) bt
* thread #1, name = 'weed8', stop reason = signal SIGSEGV: invalid address (fault address: 0x0)
  * frame #0: 0x0000555556859158 weed8`v8::internal::FullObjectSlot::operator*() const at slots-inl.h:32:64
    frame #1: 0x000055555686e3bd weed8`v8::internal::Handle<v8::internal::JSReceiver> const v8::internal::Handle<v8::internal::JSReceiver>::cast<v8::internal::JSReceiver>(v8::internal::Handle<v8::internal::JSReceiver>) at handles-inl.h:28:11
    frame #2: 0x000055555684fd50 weed8`wasm::RefImpl<wasm::Extern, v8::internal::JSReceiver>::v8_object() const at c-api.cc:761:48
    frame #3: 0x00005555568423ed weed8`wasm::Extern::kind() const at c-api.cc:1116:46
    frame #4: 0x0000555556838bac weed8`weed8::run(char const*) at weed8.cc:134:3

module is

(module
  (import "wasi_unstable" "fd_write" (func $fd_write (param i32) (param i32) (param i32) (result i32)))

  (memory (export "memory") 1)

  (data (i32.const 16)
    "Hello, World"
  )

  (func $start
    (i32.store (i32.const 0) (i32.const 12)) ;; length of data at 16
    (i32.store (i32.const 4) (i32.const 16)) ;; pointer to data at 16
    (call $fd_write (i32.const 1) (i32.const 0) (i32.const 1))
    (drop)
  )

  (export "start" (func $start))
)

devsnek avatar Oct 31 '19 06:10 devsnek

Thanks for the bug report. From the stack trace it looks like you are using V8's native implementation of the API. In that case, please file this bug with V8's bug tracker.

rossberg avatar Oct 31 '19 08:10 rossberg

Nope, not a V8 bug; it's a footgun in the API design. instance.get()->exports() gives you an ownvec<Extern>, so right after you do [0]-> on it the ownvec, being a temporary value, goes out of scope and destroys all the Externs it contained.

As long as the API doesn't change (and, to be fair, I don't have a good suggestion for how to avoid this footgun there), the client-side solution is to explicitly store the ownvec in a variable with sufficient lifetime, for example:

ownvec<Extern> exports = instance.get()->exports();
auto memory = exports[0]->memory();

jakobkummerow avatar Oct 31 '19 09:10 jakobkummerow