diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp index 2da24f00227..ecc0bfd5551 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Configuration.cpp @@ -59,6 +59,8 @@ Result Configuration::execute() { Interpreter interpreter; interpreter.interpret(*this); + if (interpreter.did_trap()) + return Trap {}; Vector> results; for (size_t i = 0; i < m_current_frame->arity(); ++i) diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp index 518f896d17b..a1d39387ec3 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Interpreter.cpp @@ -14,6 +14,12 @@ namespace Wasm { +#define TRAP_IF_NOT(x) \ + do { \ + if (trap_if_not(x)) \ + return; \ + } while (false) + void Interpreter::interpret(Configuration& configuration) { auto& instructions = configuration.frame()->expression().instructions(); @@ -23,22 +29,21 @@ void Interpreter::interpret(Configuration& configuration) while (current_ip_value < max_ip_value) { auto& instruction = instructions[current_ip_value.value()]; interpret(configuration, current_ip_value, instruction); + if (m_do_trap) + return; ++current_ip_value; } } void Interpreter::branch_to_label(Configuration& configuration, LabelIndex index) { + dbgln_if(WASM_TRACE_DEBUG, "Branch to label with index {}...", index.value()); auto label = configuration.nth_label(index.value()); - VERIFY(label.has_value()); - NonnullOwnPtrVector results; - // Pop results in order - for (size_t i = 0; i < label->arity(); ++i) - results.append(move(configuration.stack().pop().get>())); + TRAP_IF_NOT(label.has_value()); + dbgln_if(WASM_TRACE_DEBUG, "...which is actually IP {}, and has {} result(s)", label->continuation().value(), label->arity()); + auto results = pop_values(configuration, label->arity()); size_t drop_count = index.value() + 1; - if (label->continuation() < configuration.ip()) - --drop_count; for (; !configuration.stack().is_empty();) { auto entry = configuration.stack().pop(); @@ -59,12 +64,19 @@ ReadonlyBytes Interpreter::load_from_memory(Configuration& configuration, const { auto& address = configuration.frame()->module().memories().first(); auto memory = configuration.store().get(address); - VERIFY(memory); + if (!memory) { + m_do_trap = true; + return {}; + } auto& arg = instruction.arguments().get(); auto base = configuration.stack().pop().get>()->to(); - VERIFY(base.has_value()); + if (!base.has_value()) { + m_do_trap = true; + return {}; + } auto instance_address = base.value() + static_cast(arg.offset); if (instance_address < 0 || static_cast(instance_address + size) > memory->size()) { + m_do_trap = true; dbgln("LibWasm: Memory access out of bounds (expected 0 > {} and {} > {})", instance_address, instance_address + size, memory->size()); return {}; } @@ -76,12 +88,13 @@ void Interpreter::store_to_memory(Configuration& configuration, const Instructio { auto& address = configuration.frame()->module().memories().first(); auto memory = configuration.store().get(address); - VERIFY(memory); + TRAP_IF_NOT(memory); auto& arg = instruction.arguments().get(); auto base = configuration.stack().pop().get>()->to(); - VERIFY(base.has_value()); + TRAP_IF_NOT(base.has_value()); auto instance_address = base.value() + static_cast(arg.offset); if (instance_address < 0 || static_cast(instance_address + data.size()) > memory->size()) { + m_do_trap = true; dbgln("LibWasm: Memory access out of bounds (expected 0 > {} and {} > {})", instance_address, instance_address + data.size(), memory->size()); return; } @@ -92,10 +105,10 @@ void Interpreter::store_to_memory(Configuration& configuration, const Instructio void Interpreter::call_address(Configuration& configuration, FunctionAddress address) { auto instance = configuration.store().get(address); - VERIFY(instance); + TRAP_IF_NOT(instance); const FunctionType* type { nullptr }; instance->visit([&](const auto& function) { type = &function.type(); }); - VERIFY(type); + TRAP_IF_NOT(type); Vector args; args.ensure_capacity(type->parameters().size()); for (size_t i = 0; i < type->parameters().size(); ++i) { @@ -104,40 +117,60 @@ void Interpreter::call_address(Configuration& configuration, FunctionAddress add Configuration function_configuration { configuration.store() }; function_configuration.depth() = configuration.depth() + 1; auto result = function_configuration.call(address, move(args)); - if (result.is_trap()) - TODO(); + if (result.is_trap()) { + m_do_trap = true; + return; + } for (auto& entry : result.values()) configuration.stack().push(make(move(entry))); } -#define BINARY_NUMERIC_OPERATION(type, operator, ...) \ +#define BINARY_NUMERIC_OPERATION(type, operator, cast, ...) \ do { \ auto rhs = configuration.stack().pop().get>()->to(); \ auto lhs = configuration.stack().pop().get>()->to(); \ - VERIFY(lhs.has_value()); \ - VERIFY(rhs.has_value()); \ + TRAP_IF_NOT(lhs.has_value()); \ + TRAP_IF_NOT(rhs.has_value()); \ + __VA_ARGS__; \ auto result = lhs.value() operator rhs.value(); \ dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = {}", lhs.value(), #operator, rhs.value(), result); \ - configuration.stack().push(make(__VA_ARGS__(result))); \ + configuration.stack().push(make(cast(result))); \ return; \ } while (false) -#define BINARY_PREFIX_NUMERIC_OPERATION(type, operation, ...) \ +#define OVF_CHECKED_BINARY_NUMERIC_OPERATION(type, operator, cast, ...) \ + do { \ + auto rhs = configuration.stack().pop().get>()->to(); \ + auto ulhs = configuration.stack().pop().get>()->to(); \ + TRAP_IF_NOT(ulhs.has_value()); \ + TRAP_IF_NOT(rhs.has_value()); \ + dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = ??", ulhs.value(), #operator, rhs.value()); \ + __VA_ARGS__; \ + Checked lhs = ulhs.value(); \ + lhs operator##= rhs.value(); \ + TRAP_IF_NOT(!lhs.has_overflow()); \ + auto result = lhs.value(); \ + dbgln_if(WASM_TRACE_DEBUG, "{} {} {} = {}", ulhs.value(), #operator, rhs.value(), result); \ + configuration.stack().push(make(cast(result))); \ + return; \ + } while (false) + +#define BINARY_PREFIX_NUMERIC_OPERATION(type, operation, cast, ...) \ do { \ auto rhs = configuration.stack().pop().get>()->to(); \ auto lhs = configuration.stack().pop().get>()->to(); \ - VERIFY(lhs.has_value()); \ - VERIFY(rhs.has_value()); \ + TRAP_IF_NOT(lhs.has_value()); \ + TRAP_IF_NOT(rhs.has_value()); \ auto result = operation(lhs.value(), rhs.value()); \ dbgln_if(WASM_TRACE_DEBUG, "{}({} {}) = {}", #operation, lhs.value(), rhs.value(), result); \ - configuration.stack().push(make(__VA_ARGS__(result))); \ + configuration.stack().push(make(cast(result))); \ return; \ } while (false) #define UNARY_MAP(pop_type, operation, ...) \ do { \ auto value = configuration.stack().pop().get>()->to(); \ - VERIFY(value.has_value()); \ + TRAP_IF_NOT(value.has_value()); \ auto result = operation(value.value()); \ dbgln_if(WASM_TRACE_DEBUG, "map({}) {} = {}", #operation, value.value(), result); \ configuration.stack().push(make(__VA_ARGS__(result))); \ @@ -150,7 +183,7 @@ void Interpreter::call_address(Configuration& configuration, FunctionAddress add #define LOAD_AND_PUSH(read_type, push_type) \ do { \ auto slice = load_from_memory(configuration, instruction, sizeof(read_type)); \ - VERIFY(slice.size() == sizeof(read_type)); \ + TRAP_IF_NOT(slice.size() == sizeof(read_type)); \ if constexpr (sizeof(read_type) == 1) \ configuration.stack().push(make(static_cast(slice[0]))); \ else \ @@ -230,6 +263,20 @@ struct ConvertToRaw { } }; +Vector> Interpreter::pop_values(Configuration& configuration, size_t count) +{ + Vector> results; + // Pop results in order + for (size_t i = 0; i < count; ++i) { + auto top_of_stack = configuration.stack().pop(); + if (auto value = top_of_stack.get_pointer>()) + results.append(move(*value)); + else + trap_if_not(value); + } + return results; +} + void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip, const Instruction& instruction) { dbgln_if(WASM_TRACE_DEBUG, "Executing instruction {} at ip {}", instruction_name(instruction.opcode()), ip.value()); @@ -237,7 +284,8 @@ void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip configuration.dump_stack(); switch (instruction.opcode().value()) { case Instructions::unreachable.value(): - VERIFY_NOT_REACHED(); // FIXME: This is definitely not right :) + m_do_trap = true; + return; case Instructions::nop.value(): return; case Instructions::local_get.value(): @@ -284,7 +332,7 @@ void Interpreter::interpret(Configuration& configuration, InstructionPointer& ip auto entry = configuration.stack().pop(); auto value = entry.get>()->to(); - VERIFY(value.has_value()); + TRAP_IF_NOT(value.has_value()); configuration.stack().push(make