From d0eda1cbe1a5a69667427e7f49ed5794f6cfb922 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Sat, 8 Nov 2025 01:10:27 +0100 Subject: [PATCH 1/6] LibWasm: Better handle oversized table allocation requests --- Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp | 11 +++++++---- Libraries/LibWasm/Constants.h | 1 + 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp b/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp index 0fe96028e285..6bc28d38fdb3 100644 --- a/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp +++ b/Libraries/LibWasm/AbstractMachine/AbstractMachine.cpp @@ -34,6 +34,9 @@ Optional Store::allocate(HostFunction&& function) Optional Store::allocate(TableType const& type) { + if (type.limits().min() > Constants::max_allowed_table_size) + return {}; + TableAddress address { m_tables.size() }; Vector elements; elements.ensure_capacity(type.limits().min()); @@ -460,14 +463,14 @@ Optional AbstractMachine::allocate_all_initial_phase(Module for (auto& table : module.table_section().tables()) { auto table_address = m_store.allocate(table.type()); - VERIFY(table_address.has_value()); - module_instance.tables().append(*table_address); + if (table_address.has_value()) + module_instance.tables().append(*table_address); } for (auto& memory : module.memory_section().memories()) { auto memory_address = m_store.allocate(memory.type()); - VERIFY(memory_address.has_value()); - module_instance.memories().append(*memory_address); + if (memory_address.has_value()) + module_instance.memories().append(*memory_address); } size_t index = 0; diff --git a/Libraries/LibWasm/Constants.h b/Libraries/LibWasm/Constants.h index 653668cea371..78a1b4924eec 100644 --- a/Libraries/LibWasm/Constants.h +++ b/Libraries/LibWasm/Constants.h @@ -57,6 +57,7 @@ static constexpr auto page_size = 64 * KiB; static constexpr auto minimum_stack_space_to_keep_free = 256 * KiB; // Note: Value is arbitrary and chosen by testing with ASAN static constexpr auto max_allowed_executed_instructions_per_call = 256 * 1024 * 1024; static constexpr auto max_allowed_vector_size = 500 * MiB; +static constexpr auto max_allowed_table_size = 1024 * 1024; static constexpr auto max_allowed_function_locals_per_type = 42069; // Note: VERY arbitrary. // Messages used by the host From 20cc8a7ca2002a59754e0ed56f5d1c4b062a648f Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Thu, 4 Dec 2025 03:35:33 +0100 Subject: [PATCH 2/6] LibWeb: Make errors for missing wasm import errors slightly nicer --- Libraries/LibWeb/WebAssembly/WebAssembly.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp index a3d6298fcd32..9c3f539e0818 100644 --- a/Libraries/LibWeb/WebAssembly/WebAssembly.cpp +++ b/Libraries/LibWeb/WebAssembly/WebAssembly.cpp @@ -253,7 +253,7 @@ JS::ThrowCompletionOr> instantiate_module(JS // 3.4.1. If IsCallable(v) is false, throw a LinkError exception. if (!import_.is_function()) - return vm.throw_completion(JS::ErrorType::NotAFunction, import_); + return vm.throw_completion(JS::ErrorType::IsNotAEvaluatedFrom, import_, "function"_string, MUST(String::formatted("[wasm import object][\"{}\"]", import_name.name))); auto& function = import_.as_function(); // 3.4.2. If v has a [[FunctionAddress]] internal slot, and therefore is an Exported Function, From 2d1d36972b77ac79114821f6bd375676f0456510 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Thu, 4 Dec 2025 02:04:37 +0100 Subject: [PATCH 3/6] Meta: Pull wasm-tools in CI instead of wabt We'll be using wasm-tools for Wasm testsuite generation in the next few commits. --- .github/actions/setup/action.yml | 15 +++++++++------ Documentation/AdvancedBuildInstructions.md | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/actions/setup/action.yml b/.github/actions/setup/action.yml index 3c2302aa7430..784097d17e60 100644 --- a/.github/actions/setup/action.yml +++ b/.github/actions/setup/action.yml @@ -112,19 +112,22 @@ runs: brew update brew install autoconf autoconf-archive automake bash ccache coreutils libtool llvm@20 nasm ninja pkg-config qt unzip - - name: 'Install wabt' + - name: 'Install wasm-tools' shell: bash run: | + VERSION=1.243.0 if ${{ inputs.os == 'Linux' }} ; then - curl -f -L -o wabt-1.0.35.tar.gz https://github.com/WebAssembly/wabt/releases/download/1.0.35/wabt-1.0.35-ubuntu-20.04.tar.gz + NAME="wasm-tools-$VERSION-x86_64-linux" else - curl -f -L -o wabt-1.0.35.tar.gz https://github.com/WebAssembly/wabt/releases/download/1.0.35/wabt-1.0.35-macos-14.tar.gz + NAME="wasm-tools-$VERSION-aarch64-macos" fi - tar -xzf ./wabt-1.0.35.tar.gz - rm ./wabt-1.0.35.tar.gz + curl -f -L -o "${NAME}.tar.gz" "https://github.com/bytecodealliance/wasm-tools/releases/download/v${VERSION}/${NAME}.tar.gz" - echo "${{ github.workspace }}/wabt-1.0.35/bin" >> $GITHUB_PATH + tar -xzf "./${NAME}.tar.gz" + rm "./${NAME}.tar.gz" + + echo "${{ github.workspace }}/${NAME}" >> $GITHUB_PATH - name: 'Set required environment variables' if: ${{ inputs.os == 'Linux' && inputs.arch == 'arm64' }} diff --git a/Documentation/AdvancedBuildInstructions.md b/Documentation/AdvancedBuildInstructions.md index bccf4fbca58d..8b17d62fa4b0 100644 --- a/Documentation/AdvancedBuildInstructions.md +++ b/Documentation/AdvancedBuildInstructions.md @@ -26,7 +26,7 @@ There are some optional features that can be enabled during compilation that are - `ENABLE_ALL_THE_DEBUG_MACROS`: used for checking whether debug code compiles on CI. This should not be set normally, as it clutters the console output and makes the system run very slowly. Instead, enable only the needed debug macros, as described below. - `ENABLE_COMPILETIME_FORMAT_CHECK`: checks for the validity of `std::format`-style format string during compilation. Enabled by default. - `LAGOM_TOOLS_ONLY`: Skips building libraries, utiltis and tests for [Lagom](../Meta/Lagom/ReadMe.md). Mostly only useful for cross-compilation. -- `INCLUDE_WASM_SPEC_TESTS`: downloads and includes the WebAssembly spec testsuite tests. In order to use this option, you will need to install `prettier` and `wabt`. wabt version 1.0.35 or higher is required to pre-process the WebAssembly spec testsuite. +- `INCLUDE_WASM_SPEC_TESTS`: downloads and includes the WebAssembly spec testsuite tests. In order to use this option, you will need to install `prettier` and `wasm-tools`. - `INCLUDE_FLAC_SPEC_TESTS`: downloads and includes the xiph.org FLAC test suite. - `LADYBIRD_CACHE_DIR`: sets the location of a shared cache of downloaded files. Should not need to be set manually unless managing a distribution package. - `ENABLE_NETWORK_DOWNLOADS`: allows downloading files from the internet during the build. Default on, turning off enables offline builds. For offline builds, the structure of the LADYBIRD_CACHE_DIR must be set up the way that the build expects. From 56eac9a7d79c4a308d87a524c81d7bd114c2a1c0 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Thu, 4 Dec 2025 02:14:35 +0100 Subject: [PATCH 4/6] LibWasm: Implement (n)madd/vetor dot arguments the right way Previously we were reading the arguments in an incorrect order, and placing the result in the wrong slot. This also removes the hacky implementation of accumulative relaxed dot, and just implements it directly as a new operator. --- .../AbstractMachine/BytecodeInterpreter.cpp | 50 +++++++++---------- Libraries/LibWasm/AbstractMachine/Operators.h | 28 +++++++++++ 2 files changed, 52 insertions(+), 26 deletions(-) diff --git a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp index 867ae7a15198..a8f3d14fd6e3 100644 --- a/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp +++ b/Libraries/LibWasm/AbstractMachine/BytecodeInterpreter.cpp @@ -3688,41 +3688,41 @@ ALIAS_INSTRUCTION(i32x4_relaxed_trunc_f64x2_u_zero, i32x4_trunc_sat_f64x2_u_zero HANDLE_INSTRUCTION(f32x4_relaxed_madd) { - auto a = configuration.take_source(0, addresses.sources).to(); - auto b = configuration.take_source(1, addresses.sources).to(); - auto& c_slot = configuration.source_value(2, addresses.sources); - auto c = c_slot.to(); - c_slot = Value { Operators::VectorMultiplyAdd<4> {}(a, b, c) }; + auto c = configuration.take_source(0, addresses.sources).template to(); + auto a = configuration.take_source(1, addresses.sources).template to(); + auto& b_slot = configuration.source_value(2, addresses.sources); + auto b = b_slot.template to(); + b_slot = Value { Operators::VectorMultiplyAdd<4> {}(a, b, c) }; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f32x4_relaxed_nmadd) { - auto a = configuration.take_source(0, addresses.sources).to(); - auto b = configuration.take_source(1, addresses.sources).to(); - auto& c_slot = configuration.source_value(2, addresses.sources); - auto c = c_slot.to(); - c_slot = Value { Operators::VectorMultiplySub<4> {}(a, b, c) }; + auto c = configuration.take_source(0, addresses.sources).template to(); + auto a = configuration.take_source(1, addresses.sources).template to(); + auto& b_slot = configuration.source_value(2, addresses.sources); + auto b = b_slot.template to(); + b_slot = Value { Operators::VectorMultiplySub<4> {}(a, b, c) }; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_relaxed_madd) { - auto a = configuration.take_source(0, addresses.sources).to(); - auto b = configuration.take_source(1, addresses.sources).to(); - auto& c_slot = configuration.source_value(2, addresses.sources); - auto c = c_slot.to(); - c_slot = Value { Operators::VectorMultiplyAdd<2> {}(a, b, c) }; + auto c = configuration.take_source(0, addresses.sources).template to(); + auto a = configuration.take_source(1, addresses.sources).template to(); + auto& b_slot = configuration.source_value(2, addresses.sources); + auto b = b_slot.template to(); + b_slot = Value { Operators::VectorMultiplyAdd<2> {}(a, b, c) }; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } HANDLE_INSTRUCTION(f64x2_relaxed_nmadd) { - auto a = configuration.take_source(0, addresses.sources).to(); - auto b = configuration.take_source(1, addresses.sources).to(); - auto& c_slot = configuration.source_value(2, addresses.sources); - auto c = c_slot.to(); - c_slot = Value { Operators::VectorMultiplySub<2> {}(a, b, c) }; + auto c = configuration.take_source(0, addresses.sources).template to(); + auto a = configuration.take_source(1, addresses.sources).template to(); + auto& b_slot = configuration.source_value(2, addresses.sources); + auto b = b_slot.template to(); + b_slot = Value { Operators::VectorMultiplySub<2> {}(a, b, c) }; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } @@ -3745,12 +3745,10 @@ HANDLE_INSTRUCTION(i16x8_relaxed_dot_i8x16_i7x16_s) HANDLE_INSTRUCTION(i32x4_relaxed_dot_i8x16_i7x16_add_s) { - // do i16x8 dot first, then fold back down to i32, then do the final component add. - auto rhs = configuration.take_source(0, addresses.sources).to(); - auto lhs = configuration.take_source(1, addresses.sources).to(); // bounds checked by verifier. - auto result = Operators::VectorDotProduct<4, Operators::VectorIntegerExtOpPairwise<4, Operators::Add>> {}(lhs, rhs); - auto& c_slot = configuration.source_value(2, addresses.sources); - c_slot = Value { Operators::VectorIntegerBinaryOp<4, Operators::Add, MakeSigned> {}(result, c_slot.to()) }; + auto acc = configuration.take_source(0, addresses.sources).template to(); + auto rhs = configuration.take_source(1, addresses.sources).template to(); // bounds checked by verifier. + auto& lhs_slot = configuration.source_value(2, addresses.sources); + lhs_slot = Value { Operators::VectorRelaxedDotI8I7AddS {}(lhs_slot.template to(), rhs, acc) }; TAILCALL return continue_(HANDLER_PARAMS(DECOMPOSE_PARAMS_NAME_ONLY)); } diff --git a/Libraries/LibWasm/AbstractMachine/Operators.h b/Libraries/LibWasm/AbstractMachine/Operators.h index 4593cd98f963..d798e9eab943 100644 --- a/Libraries/LibWasm/AbstractMachine/Operators.h +++ b/Libraries/LibWasm/AbstractMachine/Operators.h @@ -791,6 +791,34 @@ struct VectorDotProduct { static StringView name() { return "dot"sv; } }; +struct VectorRelaxedDotI8I7AddS { + auto operator()(u128 lhs, u128 rhs, u128 acc) const + { + using VectorInput = Native128ByteVectorOf; + using VectorResult = Native128ByteVectorOf; + using VectorAcc = Native128ByteVectorOf; + + auto v1 = bit_cast(lhs); + auto v2 = bit_cast(rhs); + auto accumulator = bit_cast(acc); + VectorResult result {}; + + // Each i32 lane is the sum of 4 i8*i8 products, plus accumulator + for (size_t lane = 0; lane < 4; ++lane) { + i32 sum = 0; + for (size_t i = 0; i < 4; ++i) { + auto const idx = lane * 4 + i; + sum += static_cast(v1[idx]) * static_cast(v2[idx]); + } + result[lane] = sum + accumulator[lane]; + } + + return bit_cast(result); + } + + static StringView name() { return "i32x4.relaxed_dot_i8x16_i7x16_add_s"sv; } +}; + template struct VectorNarrow { auto operator()(u128 lhs, u128 rhs) const From c8101e63770e04a5a75a23d79c94cdb7de22a7df Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Thu, 4 Dec 2025 02:20:37 +0100 Subject: [PATCH 5/6] Meta: Generate Wasm tests using wasm-tools instead of wabt --- Meta/CMake/wasm_spec_tests.cmake | 2 +- Meta/generate-libwasm-spec-test.py | 245 ++++++++++++++++++++++++----- Meta/wasm_unimplemented_tests.txt | 204 ++++++++++++++++++++++++ Tests/LibWasm/test-wasm.cpp | 10 ++ 4 files changed, 420 insertions(+), 41 deletions(-) create mode 100644 Meta/wasm_unimplemented_tests.txt diff --git a/Meta/CMake/wasm_spec_tests.cmake b/Meta/CMake/wasm_spec_tests.cmake index 9cbd435e6abc..b6872c704614 100644 --- a/Meta/CMake/wasm_spec_tests.cmake +++ b/Meta/CMake/wasm_spec_tests.cmake @@ -15,7 +15,7 @@ if(INCLUDE_WASM_SPEC_TESTS) set(SKIP_PRETTIER true) endif() - find_program(WAT2WASM wat2wasm REQUIRED) + find_program(WASMTOOLS wasm-tools REQUIRED) find_program(PRETTIER prettier OPTIONAL) if (NOT SKIP_PRETTIER AND PRETTIER EQUAL "PRETTIER-NOTFOUND") message(FATAL_ERROR "Prettier required to format Wasm spec tests! Install prettier or set WASM_SPEC_TEST_SKIP_FORMATTING to ON") diff --git a/Meta/generate-libwasm-spec-test.py b/Meta/generate-libwasm-spec-test.py index 254236ca591c..037175c00f1d 100644 --- a/Meta/generate-libwasm-spec-test.py +++ b/Meta/generate-libwasm-spec-test.py @@ -7,8 +7,21 @@ from pathlib import Path from typing import Any from typing import Literal +from typing import Optional from typing import Union +TEST_MODULES_TO_SKIP: set[str] = set() +TESTS_TO_SKIP: set[str] = set() + + +with open(Path(__file__).parent / "wasm_unimplemented_tests.txt", "r") as f: + all_skipped_tests = (x.strip() for x in f.readlines() if not x.startswith("#")) + for test in all_skipped_tests: + if test.startswith("module "): + TEST_MODULES_TO_SKIP.add(test[len("module ") :] + ".wasm") + elif test.startswith("test "): + TESTS_TO_SKIP.add(test[len("test ") :]) + class ParseException(Exception): pass @@ -30,7 +43,18 @@ class WasmVector: num_bits: int -WasmValue = Union[WasmPrimitiveValue, WasmVector] +@dataclass +class WasmGCValue: + kind: str + value: Optional[str] = None + + +@dataclass +class EitherOf: + options: list["WasmValue"] + + +WasmValue = Union[WasmPrimitiveValue, WasmVector, WasmGCValue, EitherOf] @dataclass @@ -77,6 +101,12 @@ class AssertTrap: action: Action +@dataclass +class AssertException: + line: int + action: Action + + @dataclass class ActionCommand: line: int @@ -86,7 +116,7 @@ class ActionCommand: @dataclass class AssertInvalid: line: int - filename: str + filename: Path message: str @@ -96,6 +126,7 @@ class AssertInvalid: AssertTrap, ActionCommand, AssertInvalid, + AssertException, Register, ] @@ -116,7 +147,24 @@ class GeneratedVector: num_bits: int -GeneratedValue = Union[str, ArithmeticNan, CanonicalNan, GeneratedVector] +@dataclass +class GeneratedEitherOf: + options: list["GeneratedValue"] + + +@dataclass +class GeneratedAnyFuncRef: + pass + + +GeneratedValue = Union[ + str, + ArithmeticNan, + CanonicalNan, + GeneratedVector, + GeneratedEitherOf, + GeneratedAnyFuncRef, +] @dataclass @@ -134,13 +182,33 @@ class Context: def parse_value(arg: dict[str, str]) -> WasmValue: type_ = arg["type"] match type_: - case "i32" | "i64" | "f32" | "f64" | "externref" | "funcref": + case "i32" | "i64" | "f32" | "f64": return WasmPrimitiveValue(type_, arg["value"]) + case "externref" | "funcref": + return WasmPrimitiveValue(type_, arg["value"] if "value" in arg else None) + case "refnull": + return WasmPrimitiveValue("externref", "null") + case "nullfuncref": + return WasmPrimitiveValue("funcref", "null") case "v128": if not isinstance(arg["value"], list): raise ParseException("Got unknown type for Wasm value") num_bits = int(arg["lane_type"][1:]) return WasmVector(arg["value"], num_bits) + case ( + "arrayref" + | "structref" + | "eqref" + | "anyref" + | "i31ref" + | "exnref" + | "nullref" + | "nullexnref" + | "nullexternref" + ): + return WasmGCValue(type_, arg.get("value")) + case "either": + return EitherOf([parse_value(opt) for opt in arg["values"]]) case _: raise ParseException(f"Unknown value type: {type_}") @@ -159,14 +227,27 @@ def parse_action(action: dict[str, Any]) -> Action: raise ParseException(f"Action not implemented: {action['type']}") +def module_binary_filename(raw_cmd: dict[str, str]) -> Path: + return Path(raw_cmd["filename"] if raw_cmd.get("module_type") != "text" else raw_cmd["binary_filename"]) + + def parse(raw: dict[str, Any]) -> WastDescription: commands: list[Command] = [] + defined_modules: dict[str, Path] = {} for raw_cmd in raw["commands"]: line = raw_cmd["line"] cmd: Command match raw_cmd["type"]: case "module": - cmd = ModuleCommand(line, Path(raw_cmd["filename"]), raw_cmd.get("name")) + cmd = ModuleCommand(line, module_binary_filename(raw_cmd), raw_cmd.get("name")) + case "module_definition": + if "name" in raw_cmd: + defined_modules[raw_cmd["name"]] = module_binary_filename(raw_cmd) + continue + else: + cmd = ModuleCommand(line, module_binary_filename(raw_cmd), None) + case "module_instance": + cmd = ModuleCommand(line, defined_modules[raw_cmd["module"]], raw_cmd.get("instance")) case "action": cmd = ActionCommand(line, parse_action(raw_cmd["action"])) case "register": @@ -175,14 +256,16 @@ def parse(raw: dict[str, Any]) -> WastDescription: cmd = AssertReturn( line, parse_action(raw_cmd["action"]), - parse_value(raw_cmd["expected"][0]) if len(raw_cmd["expected"]) == 1 else None, + (parse_value(raw_cmd["expected"][0]) if len(raw_cmd["expected"]) == 1 else None), ) case "assert_trap" | "assert_exhaustion": cmd = AssertTrap(line, raw_cmd["text"], parse_action(raw_cmd["action"])) case "assert_invalid" | "assert_malformed" | "assert_uninstantiable" | "assert_unlinkable": if raw_cmd.get("module_type") == "text": continue - cmd = AssertInvalid(line, raw_cmd["filename"], raw_cmd["text"]) + cmd = AssertInvalid(line, module_binary_filename(raw_cmd), raw_cmd["text"]) + case "assert_exception": + cmd = AssertException(line, parse_action(raw_cmd["action"])) case _: raise ParseException(f"Unknown command type: {raw_cmd['type']}") commands.append(cmd) @@ -197,7 +280,15 @@ def escape(s: str) -> str: def make_description(input_path: Path, name: str, out_path: Path) -> WastDescription: out_json_path = out_path / f"{name}.json" result = subprocess.run( - ["wast2json", "--enable-all", input_path, f"--output={out_json_path}", "--no-check"], + [ + "wasm-tools", + "json-from-wast", + input_path, + "-o", + out_json_path, + "--wasm-dir", + str(out_path), + ], ) result.check_returncode() with open(out_json_path, "r") as f: @@ -205,9 +296,18 @@ def make_description(input_path: Path, name: str, out_path: Path) -> WastDescrip return parse(description) +def to_vector_element(value: str, bits: int, addition: str) -> str: + if value.isdigit(): + return value + addition + if value.startswith("-") and value[1:].isdigit(): + unsigned_value = (1 << bits) + int(value) + return str(unsigned_value) + addition + return f'"{value}"' + + def gen_vector(vec: WasmVector, *, array=False) -> str: addition = "n" if vec.num_bits == 64 else "" - vals = ", ".join(v + addition if v.isdigit() else f'"{v}"' for v in vec.lanes) + vals = ", ".join(to_vector_element(v, vec.num_bits, addition) for v in vec.lanes) if not array: type_ = "BigUint64Array" if vec.num_bits == 64 else f"Uint{vec.num_bits}Array" return f"new {type_}([{vals}])" @@ -215,9 +315,15 @@ def gen_vector(vec: WasmVector, *, array=False) -> str: def gen_value_arg(value: WasmValue) -> str: + if isinstance(value, WasmGCValue): + return "null" + if isinstance(value, WasmVector): return gen_vector(value) + if isinstance(value, EitherOf): + raise AssertionError("EitherOf should not appear here") + def unsigned_to_signed(uint: int, bits: int) -> int: max_value = 2**bits if uint >= 2 ** (bits - 1): @@ -242,7 +348,7 @@ def float_to_str(bits: int, *, double=False) -> str: f = int_to_float64_bitcast(bits) if double else int_to_float_bitcast(bits) return str(f) - if value.value.startswith("nan"): + if value.value is not None and value.value.startswith("nan"): raise GenerateException("Should not get indeterminate nan value as an argument") if value.value == "inf": return "Infinity" @@ -268,6 +374,12 @@ def gen_value_result(value: WasmValue) -> GeneratedValue: if isinstance(value, WasmVector): return GeneratedVector(gen_vector(value, array=True), value.num_bits) + if isinstance(value, EitherOf): + return GeneratedEitherOf([gen_value_result(option) for option in value.options]) + + if value.kind == "funcref" and value.value is None: + return GeneratedAnyFuncRef() + if (value.kind == "f32" or value.kind == "f64") and value.value.startswith("nan"): num_bits = int(value.kind[1:]) match value.value: @@ -284,6 +396,18 @@ def gen_args(args: list[WasmValue]) -> str: return ",".join(gen_value_arg(arg) for arg in args) +def gen_test_command_for_module(file_name): + if str(file_name) in TEST_MODULES_TO_SKIP: + return "_test.skip" + return "_test" + + +def gen_test_command_for_invoke(module_name): + if module_name in TESTS_TO_SKIP: + return "_test.skip" + return "_test" + + def gen_module_command(command: ModuleCommand, ctx: Context): if ctx.has_unclosed: print("});") @@ -295,7 +419,7 @@ def gen_module_command(command: ModuleCommand, ctx: Context): content = readBinaryWasmFile("Fixtures/SpecTests/{command.file_name}"); module = parseWebAssemblyModule(content, globalImportObject); }} catch (e) {{ -_test("parse", () => expect().fail(e)); +{gen_test_command_for_module(command.file_name)}("parse (line {command.line})", () => expect().fail(e)); _test = test.skip; _test.skip = test.skip; }} @@ -316,12 +440,12 @@ def gen_invalid(invalid: AssertInvalid, ctx: Context): if ctx.has_unclosed: print("});") ctx.has_unclosed = False - stem = Path(invalid.filename).stem + stem = invalid.filename.stem print( f""" describe("{stem}", () => {{ let _test = test; -_test("parse of {stem} (line {invalid.line})", () => {{ +{gen_test_command_for_module(invalid.filename)}("parse of {stem} (line {invalid.line})", () => {{ content = readBinaryWasmFile("Fixtures/SpecTests/{invalid.filename}"); expect(() => parseWebAssemblyModule(content, globalImportObject)).toThrow(Error, "{invalid.message}"); }}); @@ -333,6 +457,67 @@ def gen_pretty_expect(expr: str, got: str, expect: str): print(f"if (!{expr}) {{ expect().fail(`Failed with ${{{got}}}, expected {expect}`); }}") +def gen_expectation(gen_result: GeneratedValue, module: str): + match gen_result: + case str(): + print(f"expect(_result).toBe({gen_result});") + case GeneratedAnyFuncRef(): + print(f"/* {gen_result} */ ", end="") + gen_pretty_expect( + f"isValidFuncrefIn(_result, {module})", + "_result", + "(ref.func)", + ) + case ArithmeticNan(): + print(f"/* {gen_result} */ ", end="") + gen_pretty_expect( + f"isArithmeticNaN{gen_result.num_bits}(_result)", + "_result", + "nan:arithmetic", + ) + case CanonicalNan(): + print(f"/* {gen_result} */ ", end="") + gen_pretty_expect( + f"isCanonicalNaN{gen_result.num_bits}(_result)", + "_result", + "nan:canonical", + ) + case GeneratedVector(): + if gen_result.num_bits == 64: + array = "new BigUint64Array(_result)" + else: + array = f"new Uint{gen_result.num_bits}Array(_result)" + print(f"/* {gen_result} */ ", end="") + gen_pretty_expect( + f"testSIMDVector({gen_result.repr}, {array})", + array, + gen_result.repr, + ) + case GeneratedEitherOf(): + print("let matched = false;") + print("let error_sample = null;") + expectations = [] + for option in gen_result.options: + print("try {") + gen_expectation(option, module) + print("matched = true;") + print("} catch (e) { error_sample = e; }") + expectation = "unknown" + match option: + case str(): + expectation = option + case ArithmeticNan(): + expectation = "nan:arithmetic" + case CanonicalNan(): + expectation = "nan:canonical" + case GeneratedVector(): + expectation = option.repr + expectations.append(expectation) + print( + f"if (!matched) {{ expect().fail(`Expected one of {', '.join(expectations)}, got ${{_result}}: ${{error_sample}}`); }}" + ) + + def gen_invoke( line: int, invoke: Invoke, @@ -348,7 +533,7 @@ def gen_invoke( module = f'namedModules["{invoke.module}"]' utf8 = str(invoke.field.encode("utf8"))[2:-1].replace("\\'", "'").replace("`", "${'`'}") print( - f"""_test(`execution of {ctx.current_module_name}: {utf8} (line {line})`, () => {{ + f"""{gen_test_command_for_invoke(ctx.current_module_name)}(`execution of {ctx.current_module_name}: {utf8} (line {line})`, () => {{ let _field = {module}.getExport(decodeURIComponent(escape(`{utf8}`))); expect(_field).not.toBeUndefined();""" ) @@ -358,31 +543,7 @@ def gen_invoke( print(f"let _result = {module}.invoke(_field, {gen_args(invoke.args)});") if result is not None: gen_result = gen_value_result(result) - match gen_result: - case str(): - print(f"expect(_result).toBe({gen_result});") - case ArithmeticNan(): - gen_pretty_expect( - f"isArithmeticNaN{gen_result.num_bits}(_result)", - "_result", - "nan:arithmetic", - ) - case CanonicalNan(): - gen_pretty_expect( - f"isCanonicalNaN{gen_result.num_bits}(_result)", - "_result", - "nan:canonical", - ) - case GeneratedVector(): - if gen_result.num_bits == 64: - array = "new BigUint64Array(_result)" - else: - array = f"new Uint{gen_result.num_bits}Array(_result)" - gen_pretty_expect( - f"testSIMDVector({gen_result.repr}, {array})", - array, - gen_result.repr, - ) + gen_expectation(gen_result, module) print("});") if not ctx.has_unclosed: print("});") @@ -393,7 +554,7 @@ def gen_get(line: int, get: Get, result: WasmValue | None, ctx: Context): if get.module is not None: module = f'namedModules["{get.module}"]' print( - f"""_test("execution of {ctx.current_module_name}: get-{get.field} (line {line})", () => {{ + f"""{gen_test_command_for_invoke(ctx.current_module_name)}("execution of {ctx.current_module_name}: get-{get.field} (line {line})", () => {{ let _field = {module}.getExport("{get.field}");""" ) if result is not None: @@ -431,6 +592,10 @@ def gen_command(command: Command, ctx: Context): if not isinstance(command.action, Invoke): raise GenerateException(f"Not implemented: {type(command.action)}") gen_invoke(command.line, command.action, None, ctx, fail_msg=command.messsage) + case AssertException(): + if not isinstance(command.action, Invoke): + raise GenerateException(f"Not implemented: {type(command.action)}") + gen_invoke(command.line, command.action, None, ctx, fail_msg="exception") def generate(description: WastDescription): diff --git a/Meta/wasm_unimplemented_tests.txt b/Meta/wasm_unimplemented_tests.txt new file mode 100644 index 000000000000..1fac5e9b4aed --- /dev/null +++ b/Meta/wasm_unimplemented_tests.txt @@ -0,0 +1,204 @@ +module array.0 +module array.12 +module array.2 +module array.5 +module array.6 +module array.7 +module array.8 +module array_copy.4 +module array_fill.3 +module array_init_data.2 +module array_init_data.3 +module array_init_elem.3 +module array_new_data.0 +module array_new_data.1 +module array_new_data.2 +module array_new_data.3 +module array_new_data.4 +module array_new_elem.0 +module array_new_elem.1 +module array_new_elem.2 +module array_new_elem.3 +module br_on_cast.0 +module br_on_cast.1 +module br_on_cast.2 +module br_on_cast_fail.0 +module br_on_cast_fail.1 +module br_on_cast_fail.2 +module br_on_non_null.0 +module br_on_non_null.1 +module br_on_non_null.2 +module br_on_null.0 +module br_on_null.1 +module br_on_null.2 +module br_table.0 +module call_ref.0 +module call_ref.1 +module call_ref.2 +module call_ref.3 +module elem.2 +module elem.31 +module elem.47 +module elem.48 +module elem.49 +module elem.50 +module elem.51 +module elem.52 +module elem.53 +module elem.54 +module elem.57 +module elem.58 +module elem.59 +module elem.60 +module elem.61 +module elem.62 +module extern.0 +module func.21 +module global.50 +module i31.0 +module i31.1 +module i31.3 +module i31.4 +module i31.5 +module i31.6 +module instance.0 +module instance.1 +module instance.2 +module instance.3 +module instance.4 +module linking.10 +module linking.9 +module local_init.0 +module local_init.1 +module local_init.2 +module local_init.4 +module local_init.5 +module ref.1 +module ref.12 +module ref.2 +module ref.4 +module ref.5 +module ref.6 +module ref.8 +module ref_as_non_null.0 +module ref_as_non_null.2 +module ref_cast.0 +module ref_cast.1 +module ref_eq.0 +module ref_is_null.0 +module ref_null.0 +module ref_null.1 +module ref_test.0 +module ref_test.1 +module relaxed_dot_product.0 +module relaxed_madd_nmadd.0 +module return_call_ref.0 +module return_call_ref.1 +module return_call_ref.10 +module return_call_ref.8 +module return_call_ref.9 +module select.0 +module struct.0 +module struct.10 +module struct.2 +module struct.5 +module struct.7 +module struct.9 +module table-sub.0 +module table.13 +module table.14 +module table.15 +module table.29 +module table.30 +module table.31 +module table.32 +module table.33 +module table.34 +module table.35 +module table.36 +module table64.11 +module tag.2 +module tag.4 +module tag.5 +module throw.0 +module throw_ref.0 +module try_table.1 +module try_table.13 +module try_table.2 +module try_table.5 +module type-canon.0 +module type-canon.1 +module type-equivalence.14 +module type-equivalence.15 +module type-equivalence.16 +module type-equivalence.17 +module type-equivalence.18 +module type-equivalence.19 +module type-equivalence.2 +module type-equivalence.20 +module type-equivalence.21 +module type-equivalence.4 +module type-equivalence.5 +module type-equivalence.7 +module type-equivalence.8 +module type-equivalence.9 +module type-rec.0 +module type-rec.1 +module type-rec.13 +module type-rec.14 +module type-rec.17 +module type-rec.18 +module type-rec.19 +module type-rec.20 +module type-rec.3 +module type-rec.4 +module type-rec.7 +module type-rec.8 +module type-subtyping.0 +module type-subtyping.1 +module type-subtyping.11 +module type-subtyping.12 +module type-subtyping.13 +module type-subtyping.14 +module type-subtyping.17 +module type-subtyping.18 +module type-subtyping.19 +module type-subtyping.2 +module type-subtyping.20 +module type-subtyping.21 +module type-subtyping.22 +module type-subtyping.23 +module type-subtyping.24 +module type-subtyping.25 +module type-subtyping.26 +module type-subtyping.27 +module type-subtyping.28 +module type-subtyping.29 +module type-subtyping.3 +module type-subtyping.30 +module type-subtyping.34 +module type-subtyping.37 +module type-subtyping.38 +module type-subtyping.39 +module type-subtyping.4 +module type-subtyping.40 +module type-subtyping.41 +module type-subtyping.43 +module type-subtyping.44 +module type-subtyping.45 +module type-subtyping.46 +module type-subtyping.47 +module type-subtyping.48 +module type-subtyping.49 +module type-subtyping.5 +module type-subtyping.50 +module type-subtyping.51 +module type-subtyping.53 +module type-subtyping.6 +module type-subtyping.7 +module type-subtyping.76 +module type-subtyping.8 +module type-subtyping.9 +module unreached-valid.0 +module unreached-valid.2 +test local_init.5 diff --git a/Tests/LibWasm/test-wasm.cpp b/Tests/LibWasm/test-wasm.cpp index 6591e27b972a..c6384224b1f8 100644 --- a/Tests/LibWasm/test-wasm.cpp +++ b/Tests/LibWasm/test-wasm.cpp @@ -236,6 +236,16 @@ TESTJS_GLOBAL_FUNCTION(is_arithmetic_nan64, isArithmeticNaN64) return (bits & 0x7FF0000000000000ull) == 0x7FF0000000000000ull && payload >= 0x0008000000000000ull; } +TESTJS_GLOBAL_FUNCTION(is_valid_funcref_in, isValidFuncrefIn) +{ + auto value = TRY(vm.argument(0).to_index(vm)); + auto module_object = TRY(vm.argument(1).to_object(vm)); + if (!is(*module_object)) + return vm.throw_completion("Expected a WebAssemblyModule"sv); + auto& module = static_cast(*module_object); + return JS::Value(module.machine().store().get(Wasm::FunctionAddress { value }) != nullptr); +} + TESTJS_GLOBAL_FUNCTION(test_simd_vector, testSIMDVector) { auto expected = TRY(vm.argument(0).to_object(vm)); From 50156ad4b32ba0973c94d0d6d8e1f510843057e9 Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Thu, 4 Dec 2025 17:47:16 +0100 Subject: [PATCH 6/6] LibWasm: Ensure alignment values larger than 64 are rejected The instruction would be rejected for _much_ smaller values, but we shouldn't try to calculate (u64)1<64. --- .../LibWasm/AbstractMachine/Validator.cpp | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) diff --git a/Libraries/LibWasm/AbstractMachine/Validator.cpp b/Libraries/LibWasm/AbstractMachine/Validator.cpp index 344bde354dd2..f7a0dcd99cde 100644 --- a/Libraries/LibWasm/AbstractMachine/Validator.cpp +++ b/Libraries/LibWasm/AbstractMachine/Validator.cpp @@ -1525,6 +1525,9 @@ VALIDATE_INSTRUCTION(i32_load) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(i32)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(i32)); @@ -1540,6 +1543,9 @@ VALIDATE_INSTRUCTION(i64_load) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(i64)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(i64)); @@ -1554,6 +1560,9 @@ VALIDATE_INSTRUCTION(f32_load) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(float)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(float)); @@ -1568,6 +1577,9 @@ VALIDATE_INSTRUCTION(f64_load) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(double)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(double)); @@ -1582,6 +1594,9 @@ VALIDATE_INSTRUCTION(i32_load16_s) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 16 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8); @@ -1596,6 +1611,9 @@ VALIDATE_INSTRUCTION(i32_load16_u) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 16 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8); @@ -1610,6 +1628,9 @@ VALIDATE_INSTRUCTION(i32_load8_s) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 8 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8); @@ -1624,6 +1645,9 @@ VALIDATE_INSTRUCTION(i32_load8_u) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 8 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8); @@ -1638,6 +1662,9 @@ VALIDATE_INSTRUCTION(i64_load32_s) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 32 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 32 / 8); @@ -1652,6 +1679,9 @@ VALIDATE_INSTRUCTION(i64_load32_u) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 32 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 32 / 8); @@ -1666,6 +1696,9 @@ VALIDATE_INSTRUCTION(i64_load16_s) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 16 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8); @@ -1680,6 +1713,9 @@ VALIDATE_INSTRUCTION(i64_load16_u) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 16 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8); @@ -1694,6 +1730,9 @@ VALIDATE_INSTRUCTION(i64_load8_s) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 8 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8); @@ -1708,6 +1747,9 @@ VALIDATE_INSTRUCTION(i64_load8_u) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 8 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8); @@ -1722,6 +1764,9 @@ VALIDATE_INSTRUCTION(i32_store) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(i32)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(i32)); @@ -1737,6 +1782,9 @@ VALIDATE_INSTRUCTION(i64_store) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(i64)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(i64)); @@ -1752,6 +1800,9 @@ VALIDATE_INSTRUCTION(f32_store) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(float)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(float)); @@ -1767,6 +1818,9 @@ VALIDATE_INSTRUCTION(f64_store) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(double)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(double)); @@ -1782,6 +1836,9 @@ VALIDATE_INSTRUCTION(i32_store16) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 16 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8); @@ -1797,6 +1854,9 @@ VALIDATE_INSTRUCTION(i32_store8) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 8 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8); @@ -1812,6 +1872,9 @@ VALIDATE_INSTRUCTION(i64_store32) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 32 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 32 / 8); @@ -1827,6 +1890,9 @@ VALIDATE_INSTRUCTION(i64_store16) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 16 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 16 / 8); @@ -1842,6 +1908,9 @@ VALIDATE_INSTRUCTION(i64_store8) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > 8 / 8) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, 8 / 8); @@ -2318,6 +2387,9 @@ VALIDATE_INSTRUCTION(v128_load) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(u128)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(u128)); @@ -2336,6 +2408,9 @@ VALIDATE_INSTRUCTION(v128_load8x8_s) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2351,6 +2426,9 @@ VALIDATE_INSTRUCTION(v128_load8x8_u) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2366,6 +2444,9 @@ VALIDATE_INSTRUCTION(v128_load16x4_s) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2381,6 +2462,9 @@ VALIDATE_INSTRUCTION(v128_load16x4_u) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2396,6 +2480,9 @@ VALIDATE_INSTRUCTION(v128_load32x2_s) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2411,6 +2498,9 @@ VALIDATE_INSTRUCTION(v128_load32x2_u) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2425,6 +2515,9 @@ VALIDATE_INSTRUCTION(v128_load8_splat) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2439,6 +2532,9 @@ VALIDATE_INSTRUCTION(v128_load16_splat) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2453,6 +2549,9 @@ VALIDATE_INSTRUCTION(v128_load32_splat) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2467,6 +2566,9 @@ VALIDATE_INSTRUCTION(v128_load64_splat) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -2479,6 +2581,9 @@ VALIDATE_INSTRUCTION(v128_store) TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1ull << arg.align) > sizeof(u128)) return Errors::out_of_bounds("memory op alignment"sv, 1ull << arg.align, 0, sizeof(u128)); @@ -3001,6 +3106,9 @@ VALIDATE_INSTRUCTION(v128_load8_lane) auto memory = TRY(validate(arg.memory.memory_index)); + if (arg.memory.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.memory.align, 0, 64); + if ((1 << arg.memory.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment); @@ -3022,6 +3130,9 @@ VALIDATE_INSTRUCTION(v128_load16_lane) auto memory = TRY(validate(arg.memory.memory_index)); + if (arg.memory.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.memory.align, 0, 64); + if ((1 << arg.memory.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment); @@ -3043,6 +3154,9 @@ VALIDATE_INSTRUCTION(v128_load32_lane) auto memory = TRY(validate(arg.memory.memory_index)); + if (arg.memory.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.memory.align, 0, 64); + if ((1 << arg.memory.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment); @@ -3064,6 +3178,9 @@ VALIDATE_INSTRUCTION(v128_load64_lane) auto memory = TRY(validate(arg.memory.memory_index)); + if (arg.memory.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.memory.align, 0, 64); + if ((1 << arg.memory.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment); @@ -3085,6 +3202,9 @@ VALIDATE_INSTRUCTION(v128_store8_lane) auto memory = TRY(validate(arg.memory.memory_index)); + if (arg.memory.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.memory.align, 0, 64); + if ((1 << arg.memory.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment); @@ -3105,6 +3225,9 @@ VALIDATE_INSTRUCTION(v128_store16_lane) auto memory = TRY(validate(arg.memory.memory_index)); + if (arg.memory.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.memory.align, 0, 64); + if ((1 << arg.memory.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment); @@ -3125,6 +3248,9 @@ VALIDATE_INSTRUCTION(v128_store32_lane) auto memory = TRY(validate(arg.memory.memory_index)); + if (arg.memory.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.memory.align, 0, 64); + if ((1 << arg.memory.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment); @@ -3145,6 +3271,9 @@ VALIDATE_INSTRUCTION(v128_store64_lane) auto memory = TRY(validate(arg.memory.memory_index)); + if (arg.memory.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.memory.align, 0, 64); + if ((1 << arg.memory.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.memory.align, 0u, max_alignment); @@ -3161,6 +3290,9 @@ VALIDATE_INSTRUCTION(v128_load32_zero) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment); @@ -3177,6 +3309,9 @@ VALIDATE_INSTRUCTION(v128_load64_zero) auto memory = TRY(validate(arg.memory_index)); + if (arg.align > 64) + return Errors::out_of_bounds("memory op alignment value"sv, arg.align, 0, 64); + if ((1 << arg.align) > max_alignment) return Errors::out_of_bounds("memory op alignment"sv, 1 << arg.align, 0u, max_alignment);