LibWasm: Validate indirect calls at runtime

This is required by the spec, and also unbreaks all of the call-indirect
wpt tests.
This commit is contained in:
Ali Mohammad Pur 2025-05-21 16:16:28 +02:00 committed by Tim Ledbetter
parent 39b637a446
commit d79d5b70a5
Notes: github-actions[bot] 2025-05-22 06:37:18 +00:00
2 changed files with 18 additions and 3 deletions

View file

@ -223,13 +223,16 @@ VectorType BytecodeInterpreter::pop_vector(Configuration& configuration)
return bit_cast<VectorType>(configuration.value_stack().take_last().to<u128>());
}
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<Value> 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<Reference::Func>());
auto address = element.ref().get<Reference::Func>().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():

View file

@ -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 M, template<typename> typename SetSign, typename VectorType = Native128ByteVectorOf<M, SetSign>>
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<typename PopTypeLHS, typename PushType, typename Operator, typename PopTypeRHS = PopTypeLHS, typename... Args>
void binary_numeric_operation(Configuration&, Args&&...);