Stream: jco

Topic: ✔ Resource without drop crash


view this post on Zulip Slava Kuzmich (Apr 25 2024 at 11:31):

Hi, while testing wit-bindgen for Kotlin guests, I noticed that jco transpiled program fails to construct imported resource when the corresponding [resource-drop] function is not imported (it is sometimes removed by the compiler DCE when calls to it are proven unreachable).

Looking through the code, jco transpile doesn't initialise handleTables for such resource in trampoline_bindgen.rs. Then on runtime, when calling imported resource constructor, generated JS function ...

function resourceTransferOwn(handle, fromRid, toRid) {
  const { rep } = rscTableRemove(handleTables[fromRid], handle);
  return rscTableCreateOwn(handleTables[toRid], rep);
}

... evaluates handleTables[toRid] to undefined, causing the construction to crash.

Forcing "[resource-drop]..." import to stay alive solves the issue. Is this is a requirement for binding generators to keep resource-drops imports alive or a jco transpile issue?

JavaScript tooling for working with WebAssembly Components - bytecodealliance/jco

view this post on Zulip Guy Bedford (Apr 25 2024 at 17:46):

@Slava Kuzmich this sounds like a Jco bug to me, are you able to share the binary that is causing the issue? Does the resource have no functions at all then that it's table isn't being initialized by its functions?

view this post on Zulip Slava Kuzmich (Apr 25 2024 at 20:56):

@Guy Bedford I've attached binaries for both cases: with and without "[resource-drop]x" (never actually called). Testing setup requires swapping one core module in the final artefact (due to WasmGC). I've included these core files and README with repro steps.

Resource is

  resource x {
    constructor(a: s32);
    get-a: func() -> s32;
    set-a: func(a: s32);
    add: static func(x: x, a: s32) -> x;
  }

But only constructor and get-a are actually called.
jco_repro.zip

view this post on Zulip Slava Kuzmich (Apr 25 2024 at 21:07):

I've traced that ensure_resource_table is called for this resource with RID 18 only in "ok" case and only when handling Trampoline::ResourceDrop

view this post on Zulip Guy Bedford (Apr 26 2024 at 20:02):

@Slava Kuzmich thanks for sharing this - I tried running the replication but it doesn't seem to match the expectation. In the fail case I get a GC error, even when copying the core Wasm, while in the ok case I get a Wasm core erro, and when running --tracing in Jco transpile I can't see any other calls at all, or any resource calls. The core Wasms might not be lining up properly in the replication I think for the GC copy.

Can you confirm which version of Jco you are using? I'm just releasing Jco 1.2.0, it would be great to ensure the replication is fully working on that one.

view this post on Zulip Slava Kuzmich (Apr 29 2024 at 10:12):

Thanks for looking into it! I've checked with jco 1.2.1, the problem reproduced in the same way:

file:///Users/skuzmich/Downloads/jco_repro/fail/out/kco-wasm-wasi.mjs:81
  const free = table[0] & ~T_FLAG;
                    ^

TypeError: Cannot read properties of undefined (reading '0')
    at rscTableCreateOwn (file:///Users/skuzmich/Downloads/jco_repro/fail/out/kco-wasm-wasi.mjs:81:21)
    at resourceTransferOwn (file:///Users/skuzmich/Downloads/jco_repro/fail/out/kco-wasm-wasi.mjs:114:10)
    at wasm://wasm/437a0c32:wasm-function[10]:0x1c3
    at <kco>.Jsiface.constructorX (wasm://wasm/<kco>-00082e3e:wasm-function[536]:0xb6be)
    at <kco>.TestExportsImpl.test (wasm://wasm/<kco>-00082e3e:wasm-function[515]:0xb07e)
    at <kco>.__wasm_export_test (wasm://wasm/<kco>-00082e3e:wasm-function[607]:0xe230)
    at Object.test (file:///Users/skuzmich/Downloads/jco_repro/fail/out/kco-wasm-wasi.mjs:4080:35)
    at [eval]:1:54

What kind of GC errors do you get? Note that Node.js v22.0.0 is needed, as previous versions don't have the final GC support.

The core Wasms might not be lining up properly in the replication I think for the GC copy.

It lines up for me. Note that both ok and fail cases have separate respective core files. Also, jco needs to run with --base-cutoff 0, otherwise core files could be named differently.

view this post on Zulip Guy Bedford (Apr 29 2024 at 20:50):

when I follow the steps exactly I get this in the failing case:

view this post on Zulip Guy Bedford (Apr 29 2024 at 20:50):

wasm://wasm/952a0726:1


RuntimeError: unreachable
    at wasm://wasm/952a0726:wasm-function[30]:0x517
    at Object.test (file:///Users/gbedford/Projects/jco/out/kco-wasm-wasi.js:4173:35)
    at file:///Users/gbedford/Projects/jco/test.mjs:3:6

Node.js v22.0.0

view this post on Zulip Guy Bedford (Apr 29 2024 at 20:50):

where the unreachable is the call at

function test() {
  exports9['cm:example/test#test']();
}

view this post on Zulip Guy Bedford (Apr 29 2024 at 20:51):

no other calls happen

view this post on Zulip Guy Bedford (Apr 29 2024 at 20:52):

ohh wait I finally got it to work, yeah the base64 cutoff and Node.js versions were quite specific

view this post on Zulip Guy Bedford (Apr 29 2024 at 20:52):

looking into it now

view this post on Zulip Guy Bedford (Apr 29 2024 at 21:21):

So on further thought here I think the crash is actually valid

view this post on Zulip Guy Bedford (Apr 29 2024 at 21:21):

If you have a resource that is never dropped, that's not valid

view this post on Zulip Guy Bedford (Apr 29 2024 at 21:21):

Now certainly, there should be a better error

view this post on Zulip Guy Bedford (Apr 29 2024 at 21:22):

but from a component model perspective, creating a resource and never disposing it seems odd

view this post on Zulip Guy Bedford (Apr 29 2024 at 21:28):

I've posted a fix in https://github.com/bytecodealliance/jco/pull/430, but I can't land it without properly understanding what is going on here

This adds support for uninitialized resource table transfer. In rare edge cases where the target table is not otherwise referenced by any bindgen code and the resource is never dropped, it is possi...

view this post on Zulip Guy Bedford (Apr 29 2024 at 21:28):

specifically, what is the composition scenario here that results in an own transfer but not a drop?

view this post on Zulip Guy Bedford (Apr 30 2024 at 00:46):

After further thought, if this component works in Wasmtime it has to work in Jco, so I've gone ahead and published the fix in jco@1.2.2.

view this post on Zulip Slava Kuzmich (Apr 30 2024 at 07:03):

Thanks a lot for the fix!

In the attached example resource is leaked by accident.
I haven't found any indication that leaking resource is not valid in the spec. Also, there could be programs that don't leak resources, but also never drop. For example, constructing a resource and processing it in an infinite loop:

var resource = new R();
while (true) {
    resource.tick();
}

We can't call the drop here, as the resource is needed "forever".

view this post on Zulip Notification Bot (Apr 30 2024 at 07:03):

Slava Kuzmich has marked this topic as resolved.

view this post on Zulip Guy Bedford (Apr 30 2024 at 16:01):

thanks for clarifying, yes a singleton resource is similar to the component lifetime itself, and there may be use cases there


Last updated: Dec 23 2024 at 12:05 UTC