Stream: git-wasmtime

Topic: wasmtime / issue #11102 Panic with host-defined tables/gl...


view this post on Zulip Wasmtime GitHub notifications bot (Jun 23 2025 at 16:13):

alexcrichton opened issue #11102:

In at least Wasmtime 24+, possibly more versions too (haven't checked) this test will panic:

<details>

<summary>Table test</summary>

    let engine = Engine::default();
    let mut store = Store::new(&engine, ());

    let ty = FuncType::new(&engine, [], []);

    let t = Table::new(
        &mut store,
        TableType::new(RefType::new(true, HeapType::ConcreteFunc(ty)), 1, None),
        Ref::Func(None),
    )?;

    {
        let _ty2 = FuncType::new(&engine, [ValType::I32], [ValType::I32]);
        let ty = t.ty(&store);
        let fty = ty.element().heap_type().unwrap_concrete_func();
        assert!(fty.params().len() == 0);
        assert!(fty.results().len() == 0);
    }

    let ty = t.ty(&store);
    let fty = ty.element().heap_type().unwrap_concrete_func();
    assert!(fty.params().len() == 0);
    assert!(fty.results().len() == 0);

</details>

In Wasmtime 34 this test also panics (very similar to the above, just for globals).

<details>

<summary>Global test</summary>

    let engine = Engine::default();
    let mut store = Store::new(&engine, ());

    let ty = FuncType::new(&engine, [], []);

    let g = Global::new(
        &mut store,
        GlobalType::new(
            RefType::new(true, HeapType::ConcreteFunc(ty)).into(),
            Mutability::Const,
        ),
        Val::FuncRef(None),
    )?;

    {
        let _ty2 = FuncType::new(&engine, [ValType::I32], [ValType::I32]);
        let ty = g.ty(&store);
        let fty = ty.content().unwrap_ref().heap_type().unwrap_concrete_func();
        assert!(fty.params().len() == 0);
        assert!(fty.results().len() == 0);
    }

    let ty = g.ty(&store);
    let fty = ty.content().unwrap_ref().heap_type().unwrap_concrete_func();
    assert!(fty.params().len() == 0);
    assert!(fty.results().len() == 0);

</details>

The root of these issues is that a host-defined global/table are created with a concrete reference type but this reference type isn't kept alive by the host item meaning that after it's created the VMSharedTypeIndex that the item refers to is available for reuse. This can cause internal assertions to trip about "this type should be registered" as it isn't. Additionally, however, this can also cause the type of a table or global to "change" over time as the index is re-used.

This behavior is close to being a security issue and/or a CVE, but after discussion with @fitzgen we have concluded that while scary this isn't actually a security issue. While the type of a table or a global can change in this regard there's a few properties which avoid this being a security issue:

This means that panicking is only possible with host-side code and additionally the type confusion can't end up causing an value to be reinterpreted as the wrong type (except for null).

@fitzgen and I have concluded that this is not a security issue as a result. Definitely an issue to fix and one which we'll be making patch releases for (including older supported branches), however. In any case though I wanted to open a dedicated issue for this to have a longer-form description of the problem as well as the rationale/consequences. The summary is basically that this is a bug we feel needs fixing and is "close" to being a security issue, but we were unable to see a way where this could be exploitable by any means so we're going to make this public and issue patch releases but not issue advisories or such.

view this post on Zulip Wasmtime GitHub notifications bot (Jun 23 2025 at 16:13):

alexcrichton added the fuzz-bug label to Issue #11102.

view this post on Zulip Wasmtime GitHub notifications bot (Jun 24 2025 at 15:14):

fitzgen closed issue #11102:

In at least Wasmtime 24+, possibly more versions too (haven't checked) this test will panic:

<details>

<summary>Table test</summary>

    let engine = Engine::default();
    let mut store = Store::new(&engine, ());

    let ty = FuncType::new(&engine, [], []);

    let t = Table::new(
        &mut store,
        TableType::new(RefType::new(true, HeapType::ConcreteFunc(ty)), 1, None),
        Ref::Func(None),
    )?;

    {
        let _ty2 = FuncType::new(&engine, [ValType::I32], [ValType::I32]);
        let ty = t.ty(&store);
        let fty = ty.element().heap_type().unwrap_concrete_func();
        assert!(fty.params().len() == 0);
        assert!(fty.results().len() == 0);
    }

    let ty = t.ty(&store);
    let fty = ty.element().heap_type().unwrap_concrete_func();
    assert!(fty.params().len() == 0);
    assert!(fty.results().len() == 0);

</details>

In Wasmtime 34 this test also panics (very similar to the above, just for globals).

<details>

<summary>Global test</summary>

    let engine = Engine::default();
    let mut store = Store::new(&engine, ());

    let ty = FuncType::new(&engine, [], []);

    let g = Global::new(
        &mut store,
        GlobalType::new(
            RefType::new(true, HeapType::ConcreteFunc(ty)).into(),
            Mutability::Const,
        ),
        Val::FuncRef(None),
    )?;

    {
        let _ty2 = FuncType::new(&engine, [ValType::I32], [ValType::I32]);
        let ty = g.ty(&store);
        let fty = ty.content().unwrap_ref().heap_type().unwrap_concrete_func();
        assert!(fty.params().len() == 0);
        assert!(fty.results().len() == 0);
    }

    let ty = g.ty(&store);
    let fty = ty.content().unwrap_ref().heap_type().unwrap_concrete_func();
    assert!(fty.params().len() == 0);
    assert!(fty.results().len() == 0);

</details>

The root of these issues is that a host-defined global/table are created with a concrete reference type but this reference type isn't kept alive by the host item meaning that after it's created the VMSharedTypeIndex that the item refers to is available for reuse. This can cause internal assertions to trip about "this type should be registered" as it isn't. Additionally, however, this can also cause the type of a table or global to "change" over time as the index is re-used.

This behavior is close to being a security issue and/or a CVE, but after discussion with @fitzgen we have concluded that while scary this isn't actually a security issue. While the type of a table or a global can change in this regard there's a few properties which avoid this being a security issue:

This means that panicking is only possible with host-side code and additionally the type confusion can't end up causing an value to be reinterpreted as the wrong type (except for null).

@fitzgen and I have concluded that this is not a security issue as a result. Definitely an issue to fix and one which we'll be making patch releases for (including older supported branches), however. In any case though I wanted to open a dedicated issue for this to have a longer-form description of the problem as well as the rationale/consequences. The summary is basically that this is a bug we feel needs fixing and is "close" to being a security issue, but we were unable to see a way where this could be exploitable by any means so we're going to make this public and issue patch releases but not issue advisories or such.


Last updated: Dec 06 2025 at 07:03 UTC