From 9840b37665b56819fba04eeca1141fe036cb74ad Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 10 Jul 2026 16:41:33 -0700 Subject: [PATCH] Fix typecheck of host fixed-length-lists In addition to checking the element type, the length of the list needs to be checked as well. --- .../src/runtime/component/func/typed.rs | 15 +++--- .../all/component_model/fixed_length_list.rs | 46 +++++++++++++++++++ 2 files changed, 55 insertions(+), 6 deletions(-) diff --git a/crates/wasmtime/src/runtime/component/func/typed.rs b/crates/wasmtime/src/runtime/component/func/typed.rs index d8ab37bb9878..9cac53d770cd 100644 --- a/crates/wasmtime/src/runtime/component/func/typed.rs +++ b/crates/wasmtime/src/runtime/component/func/typed.rs @@ -2980,16 +2980,19 @@ where { type Lower = [T::Lower; N]; - #[allow( - clippy::cast_possible_truncation, - reason = "there is no fallible const conversion, yet" - )] const ABI: CanonicalAbiInfo = CanonicalAbiInfo::fixed_length_list_static(&T::ABI, N); fn typecheck(ty: &InterfaceType, types: &InstanceType<'_>) -> Result<()> { match ty { - InterfaceType::FixedLengthList(t) => T::typecheck(&types.types[*t].element, types), - other => bail!("expected `list<_, N>` found `{}`", desc(other)), + InterfaceType::FixedLengthList(t) => { + let list = &types.types[*t]; + match usize::try_from(list.size) { + Ok(n) if n == N => {} + _ => bail!("expected `list<_, {}>` found `list<_, {N}>`", list.size), + } + T::typecheck(&list.element, types) + } + other => bail!("expected `list<_, {N}>` found `{}`", desc(other)), } } } diff --git a/tests/all/component_model/fixed_length_list.rs b/tests/all/component_model/fixed_length_list.rs index cbcc624662e1..4a680b951776 100644 --- a/tests/all/component_model/fixed_length_list.rs +++ b/tests/all/component_model/fixed_length_list.rs @@ -205,3 +205,49 @@ package test:fixed-length-lists { assert_eq!(result, ([[1, 2], [3, 4]], [[-1, -2], [-3, -4]])); Ok(()) } + +#[test] +fn fixed_length_list_length_mismatch_rejected() -> Result<()> { + let wat = r#" +(component + (core module $m + (func (export "run") + (param i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32 i32) + (result i32) + local.get 15) + (memory (export "memory") 1) + ) + (core instance $i (instantiate $m)) + (type $lst (list u32 16)) + (type $ft (func (param "x" $lst) (result u32))) + (alias core export $i "run" (core func $run)) + (func (export "run") (type $ft) + (canon lift (core func $run) (memory $i "memory"))) +) +"#; + let mut config = Config::new(); + config.wasm_component_model_fixed_length_lists(true); + let engine = Engine::new(&config)?; + let component = Component::new(&engine, wat)?; + let mut store = Store::new(&engine, ()); + let linker = Linker::new(&engine); + let instance = linker.instantiate(&mut store, &component)?; + + assert!( + instance + .get_typed_func::<([u32; 2],), (u32,)>(&mut store, "run") + .is_err() + ); + assert!( + instance + .get_typed_func::<([u32; 32],), (u32,)>(&mut store, "run") + .is_err(), + ); + + let func = instance.get_typed_func::<([u32; 16],), (u32,)>(&mut store, "run")?; + let mut input = [0u32; 16]; + input[15] = 0x1234_5678; + let (out,) = func.call(&mut store, (input,))?; + assert_eq!(out, 0x1234_5678); + Ok(()) +}