From d79d5b70a56e40ec91a4d6c21c36470faca90d8f Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Wed, 21 May 2025 16:16:28 +0200 Subject: [PATCH] LibWasm: Validate indirect calls at runtime This is required by the spec, and also unbreaks all of the call-indirect wpt tests. --- .../AbstractMachine/BytecodeInterpreter.cpp | 14 ++++++++++++-- .../LibWasm/AbstractMachine/BytecodeInterpreter.h | 7 ++++++- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp index b47cb05d790..509034b0feb 100644 --- a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp +++ b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp @@ -223,13 +223,16 @@ VectorType BytecodeInterpreter::pop_vector(Configuration& configuration) return bit_cast(configuration.value_stack().take_last().to()); } -void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address) +void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address, CallAddressSource source) { TRAP_IF_NOT(m_stack_info.size_free() >= Constants::minimum_stack_space_to_keep_free, "{}: {}", Constants::stack_exhaustion_message); auto instance = configuration.store().get(address); FunctionType const* type { nullptr }; instance->visit([&](auto const& function) { type = &function.type(); }); + if (source == CallAddressSource::IndirectCall) { + TRAP_IF_NOT(type->parameters().size() <= configuration.value_stack().size()); + } Vector args; args.ensure_capacity(type->parameters().size()); auto span = configuration.value_stack().span().slice_from_end(type->parameters().size()); @@ -538,8 +541,15 @@ ALWAYS_INLINE void BytecodeInterpreter::interpret_instruction(Configuration& con auto element = table_instance->elements()[index]; TRAP_IF_NOT(element.ref().has()); auto address = element.ref().get().address; + auto const& type_actual = configuration.store().get(address)->visit([](auto& f) -> decltype(auto) { return f.type(); }); + auto const& type_expected = configuration.frame().module().types()[args.type.value()]; + TRAP_IF_NOT(type_actual.parameters().size() == type_expected.parameters().size()); + TRAP_IF_NOT(type_actual.results().size() == type_expected.results().size()); + TRAP_IF_NOT(type_actual.parameters() == type_expected.parameters()); + TRAP_IF_NOT(type_actual.results() == type_expected.results()); + dbgln_if(WASM_TRACE_DEBUG, "call_indirect({} -> {})", index, address.value()); - call_address(configuration, address); + call_address(configuration, address, CallAddressSource::IndirectCall); return; } case Instructions::i32_load.value(): diff --git a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h index 225720cd693..b9f6007e6d2 100644 --- a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h +++ b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h @@ -47,6 +47,11 @@ struct BytecodeInterpreter : public Interpreter { BytecodeInterpreter& m_interpreter; }; + enum class CallAddressSource { + DirectCall, + IndirectCall, + }; + protected: void interpret_instruction(Configuration&, InstructionPointer&, Instruction const&); void branch_to_label(Configuration&, LabelIndex); @@ -71,7 +76,7 @@ protected: template typename SetSign, typename VectorType = Native128ByteVectorOf> VectorType pop_vector(Configuration&); void store_to_memory(Configuration&, Instruction::MemoryArgument const&, ReadonlyBytes data, u32 base); - void call_address(Configuration&, FunctionAddress); + void call_address(Configuration&, FunctionAddress, CallAddressSource = CallAddressSource::DirectCall); template void binary_numeric_operation(Configuration&, Args&&...);