From 6e419db26c3750609fb4eabcb8157495ad8d3d16 Mon Sep 17 00:00:00 2001 From: Diego <96022404+dzfrias@users.noreply.github.com> Date: Thu, 13 Jun 2024 07:02:57 -0700 Subject: [PATCH] LibWasm: Tighten validation algorithm The big improvement included in this commit is stack height mismatch validation. There are other minor improvements included (related to the validation algorithm). The method of supporting stack polymorphism has changed to be more like the spec, which was necessary for confidently handling stack height mismatches. See: https://webassembly.github.io/spec/core/appendix/algorithm.html --- .../LibWasm/AbstractMachine/Validator.cpp | 315 ++++++------------ .../LibWasm/AbstractMachine/Validator.h | 104 +++--- 2 files changed, 155 insertions(+), 264 deletions(-) diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Validator.cpp b/Userland/Libraries/LibWasm/AbstractMachine/Validator.cpp index 37a0820bf64..6e56f7aa9c8 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Validator.cpp +++ b/Userland/Libraries/LibWasm/AbstractMachine/Validator.cpp @@ -233,6 +233,8 @@ ErrorOr Validator::validate(ElementSection const& section })); for (auto& expression : segment.init) { + if (expression.instructions().is_empty()) + continue; auto result = TRY(validate(expression, { segment.type })); if (!result.is_constant) return Errors::invalid("element initializer"sv); @@ -291,7 +293,9 @@ ErrorOr Validator::validate(CodeSection const& section) function_validator.m_frames.empend(function_type, FrameKind::Function, (size_t)0); - TRY(function_validator.validate(function.body(), function_type.results())); + auto results = TRY(function_validator.validate(function.body(), function_type.results())); + if (results.result_types.size() != function_type.results().size()) + return Errors::invalid("function result"sv, function_type.results(), results.result_types); } return {}; @@ -384,176 +388,151 @@ VALIDATE_INSTRUCTION(f64_const) // https://webassembly.github.io/spec/core/bikeshed/#-tmathsfhrefsyntax-unopmathitunop VALIDATE_INSTRUCTION(i32_clz) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I32) }); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i32_ctz) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I32) }); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i32_popcnt) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I32) }); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i64_clz) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I64) }); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_ctz) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I64) }); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_popcnt) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I64) }); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(f32_abs) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F32) }); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f32_neg) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F32) }); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f32_sqrt) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F32) }); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f32_ceil) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F32) }); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f32_floor) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F32) }); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f32_trunc) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F32) }); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f32_nearest) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F32) }); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f64_abs) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F32) }); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f64_neg) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F64) }); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f64_sqrt) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F64) }); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f64_ceil) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F64) }); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f64_floor) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F64) }); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f64_trunc) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F64) }); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f64_nearest) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::F64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::F64) }); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(i32_extend16_s) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I32) }); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i32_extend8_s) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I32)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I32) }); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i64_extend32_s) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I64) }); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_extend16_s) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I64) }); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_extend8_s) { - if (stack.is_empty() || !stack.last().is_of_kind(ValueType::I64)) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I64) }); + TRY(stack.take_and_put(ValueType::I64)); return {}; } @@ -1109,232 +1088,199 @@ VALIDATE_INSTRUCTION(f64_ge) // https://webassembly.github.io/spec/core/bikeshed/#-t_2mathsfhrefsyntax-cvtopmathitcvtopmathsf_t_1mathsf_hrefsyntax-sxmathitsx VALIDATE_INSTRUCTION(i32_wrap_i64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i64_extend_si32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_extend_ui32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i32_trunc_sf32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i32_trunc_uf32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i32_trunc_sf64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i32_trunc_uf64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i64_trunc_sf32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_trunc_uf32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_trunc_sf64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_trunc_uf64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i32_trunc_sat_f32_s) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i32_trunc_sat_f32_u) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i32_trunc_sat_f64_s) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i32_trunc_sat_f64_u) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i64_trunc_sat_f32_s) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_trunc_sat_f32_u) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_trunc_sat_f64_s) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(i64_trunc_sat_f64_u) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } VALIDATE_INSTRUCTION(f32_convert_si32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F32)); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f32_convert_ui32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F32)); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f32_convert_si64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F32)); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f32_convert_ui64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F32)); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f64_convert_si32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F64)); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f64_convert_ui32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F64)); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f64_convert_si64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F64)); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f64_convert_ui64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F64)); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f32_demote_f64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F32)); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f64_promote_f32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F64)); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(f32_reinterpret_i32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F32)); + TRY(stack.take_and_put(ValueType::F32)); return {}; } VALIDATE_INSTRUCTION(f64_reinterpret_i64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::F64)); + TRY(stack.take_and_put(ValueType::F64)); return {}; } VALIDATE_INSTRUCTION(i32_reinterpret_f32) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I32)); + TRY(stack.take_and_put(ValueType::I32)); return {}; } VALIDATE_INSTRUCTION(i64_reinterpret_f64) { - TRY(stack.take()); - stack.append(ValueType(ValueType::I64)); + TRY(stack.take_and_put(ValueType::I64)); return {}; } @@ -1352,7 +1298,7 @@ VALIDATE_INSTRUCTION(ref_is_null) if (stack.is_empty() || !stack.last().is_reference()) return Errors::invalid_stack_state(stack, Tuple { "reference" }); - stack.take_last(); + TRY(stack.take_last()); stack.append(ValueType(ValueType::I32)); return {}; } @@ -1373,26 +1319,21 @@ VALIDATE_INSTRUCTION(ref_func) // https://webassembly.github.io/spec/core/bikeshed/#parametric-instructions%E2%91%A2 VALIDATE_INSTRUCTION(drop) { - if (stack.is_empty()) - return Errors::invalid_stack_state(stack, Tuple { "any" }); - stack.take_last(); + TRY(stack.take_last()); return {}; } VALIDATE_INSTRUCTION(select) { - if (stack.size() < 3) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I32), "any", "any" }); + TRY(stack.take()); + auto arg0_type = TRY(stack.take_last()); + auto arg1_type = TRY(stack.take_last()); - auto index_type = stack.take_last(); - auto arg0_type = stack.take_last(); - auto& arg1_type = stack.last(); - if (!index_type.is_of_kind(ValueType::I32)) - return Errors::invalid("select index type"sv, ValueType(ValueType::I32), index_type); - - if (arg0_type != arg1_type) + if (arg0_type != arg1_type || arg0_type.concrete_type.is_reference() || arg1_type.concrete_type.is_reference()) return Errors::invalid("select argument types"sv, Vector { arg0_type, arg0_type }, Vector { arg0_type, arg1_type }); + stack.append(arg0_type.is_known ? arg0_type : arg1_type); + return {}; } @@ -1402,18 +1343,15 @@ VALIDATE_INSTRUCTION(select_typed) if (required_types.size() != 1) return Errors::invalid("select types"sv, "exactly one type"sv, required_types); - if (stack.size() < 3) - return Errors::invalid_stack_state(stack, Tuple { ValueType(ValueType::I32), required_types.first(), required_types.first() }); - - auto index_type = stack.take_last(); - auto arg0_type = stack.take_last(); - auto& arg1_type = stack.last(); - if (!index_type.is_of_kind(ValueType::I32)) - return Errors::invalid("select index type"sv, ValueType(ValueType::I32), index_type); + TRY(stack.take()); + auto arg0_type = TRY(stack.take_last()); + auto arg1_type = TRY(stack.take_last()); if (arg0_type != arg1_type || arg0_type != required_types.first()) return Errors::invalid("select argument types"sv, Vector { required_types.first(), required_types.first() }, Vector { arg0_type, arg1_type }); + stack.append(arg0_type.is_known ? arg0_type : arg1_type); + return {}; } @@ -1982,7 +1920,8 @@ VALIDATE_INSTRUCTION(nop) VALIDATE_INSTRUCTION(unreachable) { // https://webassembly.github.io/spec/core/bikeshed/#polymorphism - stack.append(StackEntry()); + m_frames.last().unreachable = true; + stack.resize(m_frames.last().initial_size); return {}; } @@ -1993,14 +1932,18 @@ VALIDATE_INSTRUCTION(structured_end) if (m_frames.is_empty()) return Errors::invalid("usage of structured end"sv); - auto last_frame = m_frames.take_last(); + auto& last_frame = m_frames.last(); auto& results = last_frame.type.results(); for (size_t i = 1; i <= results.size(); ++i) TRY(stack.take(results[results.size() - i])); + if (stack.size() != last_frame.initial_size) + return Errors::stack_height_mismatch(stack, last_frame.initial_size); + for (auto& result : results) stack.append(result); + m_frames.take_last(); return {}; } @@ -2014,14 +1957,18 @@ VALIDATE_INSTRUCTION(structured_else) if (m_frames.last().kind != FrameKind::If) return Errors::invalid("usage of structured else"sv); - auto frame = m_frames.take_last(); + auto& frame = m_frames.last(); auto& block_type = frame.type; auto& results = block_type.results(); for (size_t i = 1; i <= results.size(); ++i) TRY(stack.take(results[results.size() - i])); - m_frames.empend(block_type, FrameKind::Else, stack.actual_size()); + if (stack.size() != frame.initial_size) + return Errors::stack_height_mismatch(stack, frame.initial_size); + + frame.kind = FrameKind::Else; + frame.unreachable = false; for (auto& parameter : block_type.parameters()) stack.append(parameter); @@ -2037,7 +1984,7 @@ VALIDATE_INSTRUCTION(block) for (size_t i = 1; i <= parameters.size(); ++i) TRY(stack.take(parameters[parameters.size() - i])); - m_frames.empend(block_type, FrameKind::Block, stack.actual_size()); + m_frames.empend(block_type, FrameKind::Block, stack.size()); for (auto& parameter : parameters) stack.append(parameter); @@ -2053,7 +2000,7 @@ VALIDATE_INSTRUCTION(loop) for (size_t i = 1; i <= parameters.size(); ++i) TRY(stack.take(parameters[parameters.size() - i])); - m_frames.empend(block_type, FrameKind::Loop, stack.actual_size()); + m_frames.empend(block_type, FrameKind::Loop, stack.size()); for (auto& parameter : parameters) stack.append(parameter); @@ -2073,7 +2020,7 @@ VALIDATE_INSTRUCTION(if_) for (size_t i = 1; i <= parameters.size(); ++i) TRY(stack.take(parameters[parameters.size() - i])); - m_frames.empend(block_type, FrameKind::If, stack.actual_size()); + m_frames.empend(block_type, FrameKind::If, stack.size()); for (auto& parameter : parameters) stack.append(parameter); @@ -2089,7 +2036,8 @@ VALIDATE_INSTRUCTION(br) for (size_t i = 1; i <= type.size(); ++i) TRY(stack.take(type[type.size() - i])); - stack.append(StackEntry()); + m_frames.last().unreachable = true; + stack.resize(m_frames.last().initial_size); return {}; } @@ -2130,15 +2078,17 @@ VALIDATE_INSTRUCTION(br_table) auto& default_types = m_frames[(m_frames.size() - 1) - args.default_.value()].labels(); auto arity = default_types.size(); - auto stack_snapshot = stack; - auto stack_to_check = stack_snapshot; for (auto& label : args.labels) { auto& label_types = m_frames[(m_frames.size() - 1) - label.value()].labels(); if (label_types.size() != arity) return Errors::invalid("br_table label arity mismatch"sv); - for (size_t i = 0; i < arity; ++i) - TRY(stack_to_check.take(label_types[label_types.size() - i - 1])); - stack_to_check = stack_snapshot; + Vector popped {}; + for (size_t i = 0; i < arity; ++i) { + auto stack_entry = TRY(stack.take(label_types[label_types.size() - i - 1])); + popped.append(stack_entry); + } + for (auto popped_type : popped.in_reverse()) + stack.append(popped_type); } for (size_t i = 0; i < arity; ++i) { @@ -2146,7 +2096,8 @@ VALIDATE_INSTRUCTION(br_table) TRY((stack.take(expected))); } - stack.append(StackEntry()); + m_frames.last().unreachable = true; + stack.resize(m_frames.last().initial_size); return {}; } @@ -2157,7 +2108,8 @@ VALIDATE_INSTRUCTION(return_) for (size_t i = 0; i < return_types.size(); ++i) TRY((stack.take(return_types[return_types.size() - i - 1]))); - stack.append(StackEntry()); + m_frames.last().unreachable = true; + stack.resize(m_frames.last().initial_size); return {}; } @@ -3777,7 +3729,9 @@ ErrorOr Validator::validate(Instruction const& instructio ErrorOr Validator::validate(Expression const& expression, Vector const& result_types) { - Stack stack; + if (m_frames.is_empty()) + m_frames.empend(FunctionType { {}, result_types }, FrameKind::Function, (size_t)0); + auto stack = Stack(m_frames); bool is_constant_expression = true; for (auto& instruction : expression.instructions()) { @@ -3793,61 +3747,12 @@ ErrorOr Validator::validate(Ex for (auto& type : result_types) stack.append(type); + m_frames.take_last(); + VERIFY(m_frames.is_empty()); return ExpressionTypeResult { stack.release_vector(), is_constant_expression }; } -bool Validator::Stack::operator==(Stack const& other) const -{ - if (!m_did_insert_unknown_entry && !other.m_did_insert_unknown_entry) - return static_cast const&>(*this) == static_cast const&>(other); - - Optional own_last_unknown_entry_index_from_end, other_last_unknown_entry_index_from_end; - auto other_size = static_cast const&>(other).size(); - auto own_size = Vector::size(); - - for (size_t i = 0; i < own_size; ++i) { - if (other_size <= i) - break; - - auto own_entry = at(own_size - i - 1); - auto other_entry = other.at(other_size - i - 1); - if (!own_entry.is_known) { - own_last_unknown_entry_index_from_end = i; - break; - } - - if (!other_entry.is_known) { - other_last_unknown_entry_index_from_end = i; - break; - } - } - - if (!own_last_unknown_entry_index_from_end.has_value() && !other_last_unknown_entry_index_from_end.has_value()) { - if (static_cast const&>(other).is_empty() || Vector::is_empty()) - return true; - - dbgln("Equality check internal error between"); - dbgln("stack:"); - for (auto& entry : *this) - dbgln("- {}", entry.is_known ? Wasm::ValueType::kind_name(entry.concrete_type.kind()) : ""); - dbgln("and stack:"); - for (auto& entry : other) - dbgln("- {}", entry.is_known ? Wasm::ValueType::kind_name(entry.concrete_type.kind()) : ""); - - VERIFY_NOT_REACHED(); - } - - auto index_from_end = max(own_last_unknown_entry_index_from_end.value_or(0), other_last_unknown_entry_index_from_end.value_or(0)); - - for (size_t i = 0; i < index_from_end; ++i) { - if (at(own_size - i - 1) != other.at(other_size - i - 1)) - return false; - } - - return true; -} - ByteString Validator::Errors::find_instruction_name(SourceLocation const& location) { auto index = location.function_name().find('<'); diff --git a/Userland/Libraries/LibWasm/AbstractMachine/Validator.h b/Userland/Libraries/LibWasm/AbstractMachine/Validator.h index 72e16edf4a6..61e62a37e1f 100644 --- a/Userland/Libraries/LibWasm/AbstractMachine/Validator.h +++ b/Userland/Libraries/LibWasm/AbstractMachine/Validator.h @@ -132,6 +132,27 @@ public: return Errors::invalid("TableIndex"sv); } + enum class FrameKind { + Block, + Loop, + If, + Else, + Function, + }; + + struct Frame { + FunctionType type; + FrameKind kind; + size_t initial_size; + // Stack polymorphism is handled with this field + bool unreachable { false }; + + Vector const& labels() const + { + return kind != FrameKind::Loop ? type.results() : type.parameters(); + } + }; + // Instructions struct StackEntry { StackEntry(ValueType type) @@ -181,66 +202,58 @@ public: friend struct AK::Formatter; public: - // The unknown entry will never be popped off, so we can safely use the original `is_empty`. + Stack(Vector const& frames) + : m_frames(frames) + { + } + using Vector::is_empty; using Vector::last; using Vector::at; + using Vector::size; + using Vector::resize; - StackEntry take_last() + ErrorOr take_last() { - if (last().is_known) - return Vector::take_last(); - return last(); + if (size() == m_frames.last().initial_size && m_frames.last().unreachable) + return StackEntry(); + if (size() == m_frames.last().initial_size) + return Errors::invalid("stack state"sv, ""sv, ""sv); + return Vector::take_last(); } void append(StackEntry entry) { - if (!entry.is_known) - m_did_insert_unknown_entry = true; Vector::append(entry); } - ErrorOr take(ValueType type, SourceLocation location = SourceLocation::current()) + ErrorOr take(ValueType type, SourceLocation location = SourceLocation::current()) { - if (is_empty()) - return Errors::invalid("stack state"sv, type, ""sv, location); - - auto type_on_stack = take_last(); + auto type_on_stack = TRY(take_last()); if (type_on_stack != type) return Errors::invalid("stack state"sv, type, type_on_stack, location); - return {}; + return type_on_stack; } template ErrorOr take(SourceLocation location = SourceLocation::current()) { - ErrorOr result; - if (((result = take(Wasm::ValueType(kinds), location)).is_error(), ...)) { - return result; - } - return result; + for (auto kind : { kinds... }) + TRY(take(Wasm::ValueType(kind), location)); + return {}; } - template ErrorOr take_and_put(Wasm::ValueType::Kind kind, SourceLocation location = SourceLocation::current()) { - ErrorOr result; - if (((result = take(Wasm::ValueType(kinds), location)).is_error(), ...)) { - return result; - } + TRY(take(location)); append(Wasm::ValueType(kind)); - return result; + return {}; } - size_t actual_size() const { return Vector::size(); } - size_t size() const { return m_did_insert_unknown_entry ? static_cast(-1) : actual_size(); } - Vector release_vector() { return exchange(static_cast&>(*this), Vector {}); } - bool operator==(Stack const& other) const; - private: - bool m_did_insert_unknown_entry { false }; + Vector const& m_frames; }; struct ExpressionTypeResult { @@ -286,6 +299,7 @@ private: static ValidationError duplicate_export_name(StringView name) { return ByteString::formatted("Duplicate exported name '{}'", name); } static ValidationError multiple_start_sections() { return ByteString("Found multiple start sections"sv); } + static ValidationError stack_height_mismatch(Stack const& stack, size_t expected_height) { return ByteString::formatted("Stack height mismatch, got {} but expected length {}", stack, expected_height); } template static ValidationError out_of_bounds(StringView name, V value, T min, U max) { return ByteString::formatted("Value {} for {} is out of bounds ({},{})", value, name, min, max); } @@ -308,7 +322,7 @@ private: builder.append("], but found [ "sv); - auto actual_size = stack.actual_size(); + auto actual_size = stack.size(); for (size_t i = 1; i <= min(count, actual_size); ++i) { auto& entry = stack.at(actual_size - i); if (entry.is_known) { @@ -326,34 +340,6 @@ private: static ByteString find_instruction_name(SourceLocation const&); }; - struct BlockDetails { - size_t initial_stack_size { 0 }; - struct IfDetails { - Stack initial_stack; - }; - Variant details; - }; - - enum class FrameKind { - Block, - Loop, - If, - Else, - Function, - }; - - struct Frame { - FunctionType type; - FrameKind kind; - size_t init_size; - bool unreachable { false }; - - Vector const& labels() const - { - return kind != FrameKind::Loop ? type.results() : type.parameters(); - } - }; - Context m_context; Vector m_frames; COWVector m_globals_without_internal_globals;