LibWasm+LibWeb: Throw a js stack-overflow error if wasm stack overflows

Follows the spec.
This commit is contained in:
Ali Mohammad Pur 2025-05-21 16:15:34 +02:00 committed by Tim Ledbetter
commit 39b637a446
Notes: github-actions[bot] 2025-05-22 06:37:26 +00:00
4 changed files with 20 additions and 4 deletions

View file

@ -22,9 +22,9 @@ using namespace AK::SIMD;
namespace Wasm { namespace Wasm {
#define TRAP_IF_NOT(x) \ #define TRAP_IF_NOT(x, ...) \
do { \ 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__); \ dbgln_if(WASM_TRACE_DEBUG, "Trapped because {} failed, at line {}", #x, __LINE__); \
return; \ return; \
} \ } \
@ -225,7 +225,7 @@ VectorType BytecodeInterpreter::pop_vector(Configuration& configuration)
void BytecodeInterpreter::call_address(Configuration& configuration, FunctionAddress address) 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); auto instance = configuration.store().get(address);
FunctionType const* type { nullptr }; FunctionType const* type { nullptr };

View file

@ -89,6 +89,14 @@ protected:
return !m_trap.has<Empty>(); return !m_trap.has<Empty>();
} }
template<typename... Rest>
ALWAYS_INLINE bool trap_if_not(bool value, StringView reason, CheckedFormatString<StringView, Rest...> format, Rest const&... args)
{
if (!value)
m_trap = Trap { ByteString::formatted(move(format), reason, args...) };
return !m_trap.has<Empty>();
}
Variant<Trap, Empty> m_trap; Variant<Trap, Empty> m_trap;
StackInfo const& m_stack_info; StackInfo const& m_stack_info;
}; };

View file

@ -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_vector_size = 500 * MiB;
static constexpr auto max_allowed_function_locals_per_type = 42069; // Note: VERY arbitrary. 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;
} }

View file

@ -474,7 +474,12 @@ JS::NativeFunction* create_native_function(JS::VM& vm, Wasm::FunctionAddress add
if (result.is_trap()) { if (result.is_trap()) {
if (auto ptr = result.trap().data.get_pointer<Wasm::ExternallyManagedTrap>()) if (auto ptr = result.trap().data.get_pointer<Wasm::ExternallyManagedTrap>())
return ptr->unsafe_external_object_as<JS::Completion>(); return ptr->unsafe_external_object_as<JS::Completion>();
return vm.throw_completion<RuntimeError>(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", result.trap().format()))); auto& trap = result.trap().data.get<ByteString>();
// 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::InternalError>(JS::ErrorType::CallStackSizeExceeded);
return vm.throw_completion<RuntimeError>(TRY_OR_THROW_OOM(vm, String::formatted("Wasm execution trapped (WIP): {}", trap)));
} }
if (result.values().is_empty()) if (result.values().is_empty())