Commit 47434265 authored by Andreas Haas's avatar Andreas Haas Committed by Commit Bot

[wasm] Check size of tables dynamically

We used to check the size of tables at compile time, and threw a
CompilationError if a given size exceeded the implementation-defined
limit. However, the spec defines that an error should only be thrown
when the implementation-defined limit is reached, which is either at
instantiation time of during runtime at a table.grow.

With this CL the V8 implementation becomes spec compliant in this
regard.

R=jkummerow@chromium.org

Bug: v8:10556
Change-Id: I7d0e688b385a65e4060a569e5ab1dec68947ceea
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2326331
Commit-Queue: Andreas Haas <ahaas@chromium.org>
Reviewed-by: 's avatarJakob Kummerow <jkummerow@chromium.org>
Cr-Commit-Position: refs/heads/master@{#69267}
parent fd9c0d12
......@@ -722,11 +722,9 @@ DEFINE_BOOL(wasm_async_compilation, true,
"enable actual asynchronous compilation for WebAssembly.compile")
DEFINE_BOOL(wasm_test_streaming, false,
"use streaming compilation instead of async compilation for tests")
DEFINE_UINT(wasm_max_mem_pages,
v8::internal::wasm::kSpecMaxWasmInitialMemoryPages,
DEFINE_UINT(wasm_max_mem_pages, v8::internal::wasm::kSpecMaxMemoryPages,
"maximum initial number of 64KiB memory pages of a wasm instance")
DEFINE_UINT(wasm_max_mem_pages_growth,
v8::internal::wasm::kSpecMaxWasmMaximumMemoryPages,
DEFINE_UINT(wasm_max_mem_pages_growth, v8::internal::wasm::kSpecMaxMemoryPages,
"maximum number of 64KiB pages a Wasm memory can grow to")
DEFINE_UINT(wasm_max_table_size, v8::internal::wasm::kV8MaxWasmTableSize,
"maximum table size of a wasm instance")
......
......@@ -287,8 +287,14 @@ void LiftoffAssembler::Load(LiftoffRegister dst, Register src_addr,
Register offset_reg, uint32_t offset_imm,
LoadType type, LiftoffRegList pinned,
uint32_t* protected_load_pc, bool is_load_mem) {
if (offset_imm > std::numeric_limits<int32_t>::max()) {
// We do not generate code here, because such an offset should never pass
// the bounds check. However, the spec requires us to compile code with such
// an offset.
Trap();
return;
}
DCHECK_EQ(type.value_type() == kWasmI64, dst.is_gp_pair());
DCHECK_LE(offset_imm, std::numeric_limits<int32_t>::max());
Operand src_op = offset_reg == no_reg
? Operand(src_addr, offset_imm)
: Operand(src_addr, offset_reg, times_1, offset_imm);
......
......@@ -1899,11 +1899,11 @@ auto Memory::make(Store* store_abs, const MemoryType* type) -> own<Memory> {
uint32_t minimum = limits.min;
// The max_initial_mem_pages limit is only spec'ed for JS embeddings,
// so we'll directly use the maximum pages limit here.
if (minimum > i::wasm::kSpecMaxWasmMaximumMemoryPages) return nullptr;
if (minimum > i::wasm::kSpecMaxMemoryPages) return nullptr;
uint32_t maximum = limits.max;
if (maximum != Limits(0).max) {
if (maximum < minimum) return nullptr;
if (maximum > i::wasm::kSpecMaxWasmMaximumMemoryPages) return nullptr;
if (maximum > i::wasm::kSpecMaxMemoryPages) return nullptr;
}
// TODO(wasm+): Support shared memory.
i::SharedFlag shared = i::SharedFlag::kNotShared;
......
......@@ -614,9 +614,10 @@ class ModuleDecoderImpl : public Decoder {
table->type = type;
uint8_t flags = validate_table_flags("element count");
consume_resizable_limits(
"element count", "elements", FLAG_wasm_max_table_size,
"element count", "elements", std::numeric_limits<uint32_t>::max(),
&table->initial_size, &table->has_maximum_size,
FLAG_wasm_max_table_size, &table->maximum_size, flags);
std::numeric_limits<uint32_t>::max(), &table->maximum_size,
flags);
break;
}
case kExternalMemory: {
......@@ -709,9 +710,9 @@ class ModuleDecoderImpl : public Decoder {
}
uint8_t flags = validate_table_flags("table elements");
consume_resizable_limits(
"table elements", "elements", FLAG_wasm_max_table_size,
"table elements", "elements", std::numeric_limits<uint32_t>::max(),
&table->initial_size, &table->has_maximum_size,
FLAG_wasm_max_table_size, &table->maximum_size, flags);
std::numeric_limits<uint32_t>::max(), &table->maximum_size, flags);
}
}
......
......@@ -536,6 +536,13 @@ MaybeHandle<WasmInstanceObject> InstanceBuilder::Build() {
Handle<FixedArray> tables = isolate_->factory()->NewFixedArray(table_count);
for (int i = module_->num_imported_tables; i < table_count; i++) {
const WasmTable& table = module_->tables[i];
if (table.initial_size > FLAG_wasm_max_table_size) {
thrower_->RangeError(
"initial table size (%u elements) is larger than implementation "
"limit (%u elements)",
table.initial_size, FLAG_wasm_max_table_size);
return {};
}
Handle<WasmTableObject> table_obj = WasmTableObject::New(
isolate_, table.type, table.initial_size, table.has_maximum_size,
table.maximum_size, nullptr);
......
......@@ -1100,9 +1100,10 @@ void WebAssemblyTable(const v8::FunctionCallbackInfo<v8::Value>& args) {
// The descriptor's 'maximum'.
int64_t maximum = -1;
bool has_maximum = true;
if (!GetOptionalIntegerProperty(
isolate, &thrower, context, descriptor, v8_str(isolate, "maximum"),
&has_maximum, &maximum, initial, i::wasm::max_table_init_entries())) {
if (!GetOptionalIntegerProperty(isolate, &thrower, context, descriptor,
v8_str(isolate, "maximum"), &has_maximum,
&maximum, initial,
std::numeric_limits<uint32_t>::max())) {
return;
}
......
......@@ -16,10 +16,9 @@ namespace v8 {
namespace internal {
namespace wasm {
// These two constants are defined in the Wasm JS API spec and as such only
// This constant is defined in the Wasm JS API spec and as such only
// concern JS embeddings.
constexpr size_t kSpecMaxWasmInitialMemoryPages = 32767;
constexpr size_t kSpecMaxWasmMaximumMemoryPages = 65536;
constexpr size_t kSpecMaxMemoryPages = 65536;
// The following limits are imposed by V8 on WebAssembly modules.
// The limits are agreed upon with other engines for consistency.
......
......@@ -329,6 +329,7 @@ int WasmTableObject::Grow(Isolate* isolate, Handle<WasmTableObject> table,
if (!table->maximum_length().ToUint32(&max_size)) {
max_size = FLAG_wasm_max_table_size;
}
max_size = std::min(max_size, FLAG_wasm_max_table_size);
DCHECK_LE(old_size, max_size);
if (max_size - old_size < count) return -1;
......
......@@ -609,9 +609,6 @@
# Flaky due to huge string allocation.
'regress/regress-748069': [SKIP],
# Allocates a large array buffer, which TSAN sometimes cannot handle.
'regress/regress-599717': [SKIP],
# BUG(v8:7042). Uses a lot of memory.
'regress/regress-678917': [SKIP],
......@@ -688,7 +685,6 @@
# a large amount of memory if executed in parallel. Therefore we
# run only a single instance of these tests
'regress/regress-crbug-514081': [PASS, NO_VARIANTS],
'regress/regress-599717': [PASS, NO_VARIANTS],
'regress/regress-599414-array-concat-fast-path': [PASS, NO_VARIANTS],
'array-functions-prototype-misc': [PASS, NO_VARIANTS],
}], # 'arch in (mipsel, mips, mips64el, mips64)'
......@@ -1300,7 +1296,6 @@
# The entire snapshotting code assumes that the snapshot size fits
# into an int, so it doesn't support huge TypedArrays.
'regress/regress-319722-ArrayBuffer': [SKIP],
'regress/regress-599717': [SKIP],
'regress/regress-667603': [SKIP],
'regress/regress-crbug-1104608': [SKIP],
# Investigate (IsFixedArrayBase).
......
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --validate-asm --allow-natives-syntax
function __f_61(stdlib, foreign, buffer) {
"use asm";
var __v_14 = new stdlib.Float64Array(buffer);
function __f_74() {
var __v_35 = 6.0;
__v_14[2] = __v_35 + 1.0;
}
return {__f_74: __f_74};
}
var ok = false;
try {
var __v_12 = new ArrayBuffer(2147483648);
ok = true;
} catch (e) {
// Can happen if the allocation fails.
}
if (ok) {
var module = __f_61(this, null, __v_12);
// This fails AsmJS memory size validation because of kV8MaxWasmMemoryPages
// value.
assertFalse(%IsAsmWasmCode(__f_61));
}
......@@ -5,5 +5,5 @@
// Flags: --random-seed=-1101427159 --enable-slow-asserts --expose-wasm
(function __f_7() {
assertThrows(() => new WebAssembly.Memory({initial: 59199}), RangeError);
assertThrows(() => new WebAssembly.Memory({initial: 79199}), RangeError);
})();
......@@ -609,9 +609,9 @@ assertTrue(new Table({initial: 1, element: 'anyfunc'}) instanceof Table);
assertTrue(new Table({initial: 1.5, element: 'anyfunc'}) instanceof Table);
assertTrue(
new Table({initial: 1, maximum: 1.5, element: 'anyfunc'}) instanceof Table);
assertThrows(
() => new Table({initial: 1, maximum: Math.pow(2, 32) - 1, element: 'anyfunc'}),
RangeError, /above the upper bound/);
assertTrue(
new Table({initial: 1, maximum: Math.pow(2, 32) - 1, element: 'anyfunc'})
instanceof Table);
// 'WebAssembly.Table.prototype' data property
let tableProtoDesc = Object.getOwnPropertyDescriptor(Table, 'prototype');
......
......@@ -6,7 +6,7 @@
load("test/mjsunit/wasm/wasm-module-builder.js");
var kV8MaxWasmMemoryPages = 32767; // ~ 2 GiB
var kV8MaxWasmMemoryPages = 65536; // 4 GiB
var kSpecMaxWasmMemoryPages = 65536; // 4 GiB
(function testMemorySizeZero() {
......
......@@ -18,9 +18,9 @@ const oob = 11;
(function TestJSTableMaximumAboveTheLimit() {
print(arguments.callee.name);
assertThrows(
() => new WebAssembly.Table({ initial: 1, maximum: oob, element: "anyfunc" }),
RangeError, /above the upper bound/);
let table =
new WebAssembly.Table({initial: 1, maximum: oob, element: 'anyfunc'});
assertThrows(() => table.grow(oob - 1), RangeError, /failed to grow table/);
})();
(function TestDecodeTableInitialAboveTheLimit() {
......@@ -29,14 +29,14 @@ const oob = 11;
builder.setTableBounds(oob);
assertThrows(
() => builder.instantiate(),
WebAssembly.CompileError, /is larger than implementation limit/);
RangeError, /is larger than implementation limit/);
})();
(function TestDecodeTableMaximumAboveTheLimit() {
print(arguments.callee.name);
const builder = new WasmModuleBuilder();
builder.setTableBounds(1, oob);
assertThrows(
() => builder.instantiate(),
WebAssembly.CompileError, /is larger than implementation limit/);
// Should not throw, as the table does not exceed the limit at instantiation
// time.
builder.instantiate();
})();
......@@ -89,9 +89,12 @@ function assertTableIsValid(table, length) {
table = new WebAssembly.Table({element: "anyfunc", initial: 0, maximum: kV8MaxWasmTableSize});
assertTableIsValid(table, 0);
table = new WebAssembly.Table({element: "anyfunc", initial: 0, maximum: kV8MaxWasmTableSize + 1});
assertTableIsValid(table, 0);
assertThrows(
() => new WebAssembly.Table(
{element: "anyfunc", initial: 0, maximum: kV8MaxWasmTableSize + 1}),
{element: "anyfunc", initial: kV8MaxWasmTableSize + 1}),
RangeError, /above the upper bound/);
})();
......
......@@ -31,10 +31,6 @@
# This test requires the reftypes flag to be disabled.
'proposals/bulk-memory-operations/imports': [FAIL],
# TODO(wasm): This test declares a table larger than allowed by the spec.
'table': [FAIL],
'proposals/reference-types/table': [FAIL],
}], # ALWAYS
['arch == mipsel or arch == mips64el or arch == mips or arch == mips64', {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment