diff --git a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp index 6bf45a844ae..b47cb05d790 100644 --- a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp +++ b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp @@ -22,9 +22,9 @@ using namespace AK::SIMD; namespace Wasm { -#define TRAP_IF_NOT(x) \ +#define TRAP_IF_NOT(x, ...) \ do { \ - if (trap_if_not(x, #x##sv)) { \ + if (trap_if_not(x, #x##sv __VA_OPT__(, ) __VA_ARGS__)) { \ dbgln_if(WASM_TRACE_DEBUG, "Trapped because {} failed, at line {}", #x, __LINE__); \ return; \ } \ @@ -225,7 +225,7 @@ VectorType BytecodeInterpreter::pop_vector(Configuration& configuration) void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address) { - TRAP_IF_NOT(m_stack_info.size_free() >= Constants::minimum_stack_space_to_keep_free); + 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 }; diff --git a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h index fcb9127ab47..225720cd693 100644 --- a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h +++ b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.h @@ -89,6 +89,14 @@ protected: return !m_trap.has(); } + template + ALWAYS_INLINE bool trap_if_not(bool value, StringView reason, CheckedFormatString format, Rest const&... args) + { + if (!value) + m_trap = Trap { ByteString::formatted(move(format), reason, args...) }; + return !m_trap.has(); + } + Variant m_trap; StackInfo const& m_stack_info; }; diff --git a/Libraries/LibWasm/Constants.h b/Libraries/LibWasm/Constants.h index 3323a7ec26b..95e261472b7 100644 --- a/Libraries/LibWasm/Constants.h +++ b/Libraries/LibWasm/Constants.h @@ -44,4 +44,7 @@ static constexpr auto max_allowed_executed_instructions_per_call = 256 * 1024 * static constexpr auto max_allowed_vector_size = 500 * MiB; static constexpr auto max_allowed_function_locals_per_type = 42069; // Note: VERY arbitrary. +// Messages used by the host +static constexpr auto stack_exhaustion_message = "STACK-EXHAUSTION"sv; + } diff --git a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp index c2b3d8bdead..73c2e8b8a27 100644 --- a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp +++ b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp @@ -474,7 +474,12 @@ JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress add if (result.is_trap()) { if (auto ptr = result.trap().data.get_pointer()) return ptr->unsafe_external_object_as(); - return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", result.trap().format()))); + auto& trap = result.trap().data.get(); + // https://webassembly.github.io/spec/js-api/#stack-overflow + // 6.1. Whenever a stack overflow occurs in WebAssembly code, the same class of exception is thrown as for a stack overflow in JavaScript. + if (trap.ends_with(Wasm::Constants::stack_exhaustion_message)) + return vm.throw_completion(JS::ErrorType::CallStackSizeExceeded); + return vm.throw_completion(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", trap))); } if (result.values().is_empty())