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

Error assertion left == right failed on Wasm module init in Rust-embedded Wasmtime #9714

Open
Tracked by #5032
epsylonix opened this issue Dec 3, 2024 · 6 comments
Assignees
Labels
bug Incorrect behavior in the current implementation that needs fixing wasm-proposal:gc Issues with the implementation of the gc wasm proposal

Comments

@epsylonix
Copy link

Test Case

I'm trying to run a Kotlin-produced Wasm module in Rust-embedded Wasmtime. Kotlin requires support for the Wasm GC proposal, which Wasmtime officially has since version 27.0.0. Unfortunately, when initializing the Kotlin-generated Wasm module, a Wasmtime assertion error occurs, seemingly related to the Wasm GC code.

I initially assumed some incompatibility between Wasmtime and Kotlin-produced Wasm but the same file can be executed successfully in the standalone mode with wasmtime -W function-references,gc.

The minimal Kotlin guest code that reproduces this issue looks like this:

fun main() {}

And this is the corresponding host code:

use wasmtime::*;
use wasi_common::sync::WasiCtxBuilder;

fn main() -> Result<()> {
    let mut config = Config::new();
    config
        .wasm_gc(true)
        .wasm_function_references(true);

    let engine = Engine::new(&mut config)?;
    let mut linker = Linker::new(&engine);
    wasi_common::sync::add_to_linker(&mut linker, |s| s)?;

    let wasi = WasiCtxBuilder::new()
        .inherit_stderr()
        .inherit_stdout()
        .build();
    let mut store = Store::new(&engine, wasi);

    let module = Module::from_file(&engine, "kotlin.wasm")?;
    linker.instantiate(&mut store, &module)?;

    Ok(())
}

This fails at linker.instantiate call with an error:

thread 'main' panicked at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/type_registry.rs:271:17:
assertion `left == right` failed
  left: WasmSubType { is_final: true, supertype: None, composite_type: WasmCompositeType { inner: Struct(WasmStructType { fields: [] }), shared: false } }
 right: WasmSubType { is_final: true, supertype: Some(Engine(VMSharedTypeIndex(19))), composite_type: WasmCompositeType { inner: Struct(WasmStructType { fields: [] }), shared: false } }

stack backtrace:
   0: rust_begin_unwind
             at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/std/src/panicking.rs:662:5
   1: core::panicking::panic_fmt
             at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/panicking.rs:74:14
   2: core::panicking::assert_failed_inner
   3: core::panicking::assert_failed
             at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/panicking.rs:367:5
   4: <wasmtime::runtime::type_registry::RegisteredType as core::cmp::PartialEq>::eq
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/type_registry.rs:271:17
   5: core::cmp::impls::<impl core::cmp::PartialEq<&B> for &A>::eq
             at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/cmp.rs:1661:13
   6: <Q as hashbrown::Equivalent<K>>::equivalent
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/hashbrown-0.14.5/src/lib.rs:172:9
   7: hashbrown::map::equivalent_key::{{closure}}
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/hashbrown-0.14.5/src/map.rs:229:14
   8: hashbrown::raw::inner::RawTable<T,A>::find_or_find_insert_slot::{{closure}}
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/hashbrown-0.14.5/src/raw/mod.rs:1425:68
   9: hashbrown::raw::inner::RawTableInner::find_or_find_insert_slot_inner
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/hashbrown-0.14.5/src/raw/mod.rs:1983:27
  10: hashbrown::raw::inner::RawTable<T,A>::find_or_find_insert_slot
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/hashbrown-0.14.5/src/raw/mod.rs:1423:19
  11: hashbrown::map::HashMap<K,V,S,A>::insert
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/hashbrown-0.14.5/src/map.rs:1754:15
  12: hashbrown::set::HashSet<T,S,A>::insert
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/hashbrown-0.14.5/src/set.rs:1115:9
  13: wasmtime::runtime::store::StoreOpaque::insert_gc_host_alloc_type
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/store.rs:1797:9
  14: wasmtime::runtime::gc::enabled::structref::StructRefPre::_new
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/gc/enabled/structref.rs:74:9
  15: wasmtime::runtime::vm::const_expr::ConstEvalContext::struct_new
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/vm/const_expr.rs:93:25
  16: wasmtime::runtime::vm::const_expr::ConstEvalContext::struct_new_default
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/vm/const_expr.rs:153:18
  17: wasmtime::runtime::vm::const_expr::ConstExprEvaluator::eval
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/vm/const_expr.rs:278:31
  18: wasmtime::runtime::vm::instance::allocator::initialize_globals
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/vm/instance/allocator.rs:779:13
  19: wasmtime::runtime::vm::instance::allocator::initialize_instance
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/vm/instance/allocator.rs:826:5
  20: wasmtime::runtime::vm::instance::InstanceHandle::initialize
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/vm/instance.rs:1541:9
  21: wasmtime::runtime::instance::Instance::new_raw
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/instance.rs:343:9
  22: wasmtime::runtime::instance::Instance::new_started_impl
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/instance.rs:206:33
  23: wasmtime::runtime::instance::Instance::new_started
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/instance.rs:194:9
  24: wasmtime::runtime::instance::InstancePre<T>::instantiate
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/instance.rs:876:18
  25: wasmtime::runtime::linker::Linker<T>::instantiate
             at ~/.cargo/registry/src/index.crates.io-6f17d22bba15001f/wasmtime-27.0.0/src/runtime/linker.rs:1112:9
  26: jobexecutor::main
             at ./src/main.rs:25:5
  27: core::ops::function::FnOnce::call_once
             at /rustc/f6e511eec7342f59a25f7c0534f1dbea00d01b14/library/core/src/ops/function.rs:250:5

Versions and Environment

Wasmtime version or commit: 27.0.0

Operating system: macOS

Architecture: arm64

@epsylonix epsylonix added the bug Incorrect behavior in the current implementation that needs fixing label Dec 3, 2024
@alexcrichton alexcrichton added the wasm-proposal:gc Issues with the implementation of the gc wasm proposal label Dec 3, 2024
@epsylonix
Copy link
Author

epsylonix commented Dec 4, 2024

I've also encountered a similar issue in the standalone mode with when added any non-trivial code. For example, using Kotlin's kotlinx.serialization json and a small code example from docs: https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/basic-serialization.md#json-decoding

import kotlinx.serialization.json.Json

fun main() {
    val data = Json.decodeFromString<HashMap<String, String>>("""
        {"name":"test"}
    """)
    println(data)
}

@WasmExport
fun dummy() {}

Execution results in this error:

thread 'main' panicked at crates/wasmtime/src/runtime/vm/gc/func_ref.rs:81:13:
assertion failed: types.is_subtype(actual_ty, expected_ty)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[1]    11826 abort      ./wasmtime-v27.0.0/wasmtime -W function-references,gc

I tested it with WasmEdge and it this works there so I assume this is a Wasmtime issue, not Kotlin.

@fitzgen
Copy link
Member

fitzgen commented Dec 4, 2024

Thanks for filing an issue! Can you attach the .wasm or .wat instead of the Kotlin source? That will greatly help us debug and diagnose the issue. Thanks!

@epsylonix
Copy link
Author

epsylonix commented Dec 5, 2024

Thank you for looking into this!

Here are two wasm files for both issues I've mentined.

  1. The first one is an empty main case (fun main() {}) that can be executed by the standalone Wasmtime version but fails with assertion left == right failed when the module is initialized when Wasmtime is embedded in Rust. I'm new to Rust so it might be something I'm doing wrong here but the code is mostly taken from docs.
    assertion_failed__left_eq_right.wasm.zip

  2. The second one is the case with the serialization. It fails even in the standalone version of Wasmtime with assertion failed: types.is_subtype(actual_ty, expected_ty) error.
    assertion_failed__types_is_subtype.wasm.zip

@fitzgen
Copy link
Member

fitzgen commented Jan 23, 2025

I've used creduce to reduce the assertion `left == right` failure panic down to the following input:

(module
  (type (;0;) (func))
  (rec
    (type (;1;) (struct))
    (type (;2;) (struct))
    (type (;3;) (struct))
    (type (;4;) (struct))
    (type (;5;) (struct))
    (type (;6;) (struct))
    (type (;7;) (struct))
    (type (;8;) (struct))
    (type (;9;) (struct))
    (type (;10;) (struct))
    (type (;11;) (struct))
    (type (;12;) (struct))
    (type (;13;) (array (mut (ref 2))))
    (type (;14;) (array i16))
    (type (;15;) (struct (field (ref 2)) (field (ref 1))))
  )
  (global (;0;) (ref 15) struct.new_default 2 struct.new_default 1 struct.new 15)
)

@fitzgen
Copy link
Member

fitzgen commented Jan 23, 2025

Further reduced by hand:

(module
  (rec
    (type $b (struct))
    (type $a (struct (field (ref $b))))
  )
  (global (;0;) (ref $a)
    struct.new_default $b
    struct.new $a
  )
)

fitzgen added a commit to fitzgen/wasmtime that referenced this issue Jan 23, 2025
We were incorrectly checking whether they were in the same rec
group (i.e. whether the two `RegisteredType`s had the same `RecGroupEntry`)
rather than whether they were actually the same type or not, even if they were
in the same rec group.

This fixes one of two issues reported in bytecodealliance#9714
@fitzgen
Copy link
Member

fitzgen commented Jan 23, 2025

Fix for the assertion `left == right` failure bug in #10091

github-merge-queue bot pushed a commit that referenced this issue Jan 23, 2025
We were incorrectly checking whether they were in the same rec
group (i.e. whether the two `RegisteredType`s had the same `RecGroupEntry`)
rather than whether they were actually the same type or not, even if they were
in the same rec group.

This fixes one of two issues reported in #9714
fitzgen added a commit to fitzgen/wasmtime that referenced this issue Feb 12, 2025
It is possible for two `WasmSubType`s to be equal to each other, as far as
`derive(PartialEq)` is concerned, but still different from each other if they
are in different rec groups or even if they are at different indices within the
same rec group. The assertion mistakenly did not permit either of these,
however.

Fixes bytecodealliance#9714
fitzgen added a commit to fitzgen/wasmtime that referenced this issue Feb 12, 2025
It is possible for two `WasmSubType`s to be equal to each other, as far as
`derive(PartialEq)` is concerned, but still different from each other if they
are in different rec groups or even if they are at different indices within the
same rec group. The assertion mistakenly did not permit either of these,
however.

Fixes bytecodealliance#9714
github-merge-queue bot pushed a commit that referenced this issue Feb 14, 2025
…e usage in ExternType::from_wasmtime (#10223)

* Fix assertion in `PartialEq` for `RegisteredType` again

It is possible for two `WasmSubType`s to be equal to each other, as far as
`derive(PartialEq)` is concerned, but still different from each other if they
are in different rec groups or even if they are at different indices within the
same rec group. The assertion mistakenly did not permit either of these,
however.

Fixes #9714

* Canonicalize all types for runtime usage when creating `wasmtime::{Module,Component}`s

Rather than canonicalizing them on demand in functions like
`{Func,Global,Table}Type::from_wasmtime` and other places. Instead, we do it in
one place, up front, so that it is very unlikely we miss anything. Doing this
involves changing some things from `ModuleInternedTypeIndex`es to
`EngineOrModuleTypeIndex`es in `wasmtime_environ`, which means that a bunch of
uses of those things need to unwrap the appropriate kind of type index at usage
sites (e.g. compilation uses will unwrap `ModuleInternedTypeIndex`es, runtime
uses usage will unwrap `VMSharedTypeIndex`es). And it additionally required
implementing the `TypeTrace` trait for a handful of things to unlock the
provided `canonicalize_for_runtime_usage` trait method for those things.

All this machinery is required to avoid an assertion failure in the regression
test introduced in the previous commit, which was triggered because we were
failing to canonicalize type indices inside `ExternType`s for runtime usage on
some code paths. We shouldn't have to play that kind of whack-a-mole in the
future, thanks to this new approach.

* Fix a warning in no-default-features builds

* Fix another warning in weird cfg builds
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Incorrect behavior in the current implementation that needs fixing wasm-proposal:gc Issues with the implementation of the gc wasm proposal
Projects
None yet
Development

No branches or pull requests

3 participants