Commit 4b6a6992 authored by Emanuel Ziegler's avatar Emanuel Ziegler Committed by Commit Bot

[wasm][bulk-memory] Adjust throw behavior to match new proposal

InstanceBuilder::LoadTableSegments - Throw RuntimeError instead of
  LinkError
WasmGraphBuilder::TableInit & WasmGraphBuilder::MemoryInit - Do not
  check for active/dropped status if size == 0
WasmGraphBuilder::MemoryFill - Throw out-of-bounds error BEFORE
  attempting any memory operations if necessary

R=ahaas@chromium.org

Bug: v8:9865
Change-Id: I6a67779dc99fdc1c6bda6a2526d0e9ee5385f3ed
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1924442Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org>
Cr-Commit-Position: refs/heads/master@{#65098}
parent 83bd11c8
...@@ -4835,7 +4835,6 @@ Node* WasmGraphBuilder::CheckDataSegmentIsPassiveAndNotDropped( ...@@ -4835,7 +4835,6 @@ Node* WasmGraphBuilder::CheckDataSegmentIsPassiveAndNotDropped(
Node* WasmGraphBuilder::MemoryInit(uint32_t data_segment_index, Node* dst, Node* WasmGraphBuilder::MemoryInit(uint32_t data_segment_index, Node* dst,
Node* src, Node* size, Node* src, Node* size,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
CheckDataSegmentIsPassiveAndNotDropped(data_segment_index, position);
auto m = mcgraph()->machine(); auto m = mcgraph()->machine();
auto common = mcgraph()->common(); auto common = mcgraph()->common();
Node* size_null_check = Node* size_null_check =
...@@ -4847,6 +4846,7 @@ Node* WasmGraphBuilder::MemoryInit(uint32_t data_segment_index, Node* dst, ...@@ -4847,6 +4846,7 @@ Node* WasmGraphBuilder::MemoryInit(uint32_t data_segment_index, Node* dst,
Node* size_null_if_false = Node* size_null_if_false =
graph()->NewNode(common->IfFalse(), size_null_branch); graph()->NewNode(common->IfFalse(), size_null_branch);
SetControl(size_null_if_false); SetControl(size_null_if_false);
CheckDataSegmentIsPassiveAndNotDropped(data_segment_index, position);
Node* dst_fail = BoundsCheckMemRange(&dst, &size, position); Node* dst_fail = BoundsCheckMemRange(&dst, &size, position);
TrapIfTrue(wasm::kTrapMemOutOfBounds, dst_fail, position); TrapIfTrue(wasm::kTrapMemOutOfBounds, dst_fail, position);
...@@ -4965,13 +4965,13 @@ Node* WasmGraphBuilder::MemoryFill(Node* dst, Node* value, Node* size, ...@@ -4965,13 +4965,13 @@ Node* WasmGraphBuilder::MemoryFill(Node* dst, Node* value, Node* size,
graph()->NewNode(common->IfFalse(), size_null_branch); graph()->NewNode(common->IfFalse(), size_null_branch);
SetControl(size_null_if_false); SetControl(size_null_if_false);
Node* fail = BoundsCheckMemRange(&dst, &size, position); Node* fail = BoundsCheckMemRange(&dst, &size, position);
TrapIfTrue(wasm::kTrapMemOutOfBounds, fail, position);
Node* function = graph()->NewNode(mcgraph()->common()->ExternalConstant( Node* function = graph()->NewNode(mcgraph()->common()->ExternalConstant(
ExternalReference::wasm_memory_fill())); ExternalReference::wasm_memory_fill()));
MachineType sig_types[] = {MachineType::Pointer(), MachineType::Uint32(), MachineType sig_types[] = {MachineType::Pointer(), MachineType::Uint32(),
MachineType::Uint32()}; MachineType::Uint32()};
MachineSignature sig(0, 3, sig_types); MachineSignature sig(0, 3, sig_types);
BuildCCall(&sig, function, dst, value, size); BuildCCall(&sig, function, dst, value, size);
TrapIfTrue(wasm::kTrapMemOutOfBounds, fail, position);
Node* size_null_if_true = Node* size_null_if_true =
graph()->NewNode(common->IfTrue(), size_null_branch); graph()->NewNode(common->IfTrue(), size_null_branch);
...@@ -5001,6 +5001,18 @@ Node* WasmGraphBuilder::TableInit(uint32_t table_index, ...@@ -5001,6 +5001,18 @@ Node* WasmGraphBuilder::TableInit(uint32_t table_index,
uint32_t elem_segment_index, Node* dst, uint32_t elem_segment_index, Node* dst,
Node* src, Node* size, Node* src, Node* size,
wasm::WasmCodePosition position) { wasm::WasmCodePosition position) {
auto machine = mcgraph()->machine();
auto common = mcgraph()->common();
// If size == 0, then table.init is a no-op.
Node* size_zero_check = graph()->NewNode(machine->Word32Equal(), size,
mcgraph()->Int32Constant(0));
Node* size_zero_branch = graph()->NewNode(common->Branch(BranchHint::kFalse),
size_zero_check, Control());
Node* size_zero_etrue = Effect();
Node* size_zero_if_false =
graph()->NewNode(common->IfFalse(), size_zero_branch);
SetControl(size_zero_if_false);
CheckElemSegmentIsPassiveAndNotDropped(elem_segment_index, position); CheckElemSegmentIsPassiveAndNotDropped(elem_segment_index, position);
Node* args[] = { Node* args[] = {
graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)), graph()->NewNode(mcgraph()->common()->NumberConstant(table_index)),
...@@ -5008,10 +5020,15 @@ Node* WasmGraphBuilder::TableInit(uint32_t table_index, ...@@ -5008,10 +5020,15 @@ Node* WasmGraphBuilder::TableInit(uint32_t table_index,
BuildConvertUint32ToSmiWithSaturation(dst, FLAG_wasm_max_table_size), BuildConvertUint32ToSmiWithSaturation(dst, FLAG_wasm_max_table_size),
BuildConvertUint32ToSmiWithSaturation(src, FLAG_wasm_max_table_size), BuildConvertUint32ToSmiWithSaturation(src, FLAG_wasm_max_table_size),
BuildConvertUint32ToSmiWithSaturation(size, FLAG_wasm_max_table_size)}; BuildConvertUint32ToSmiWithSaturation(size, FLAG_wasm_max_table_size)};
Node* result = BuildCallToRuntime(Runtime::kWasmTableInit, args, arraysize(args));
BuildCallToRuntime(Runtime::kWasmTableInit, args, arraysize(args)); Node* size_zero_if_true =
graph()->NewNode(common->IfTrue(), size_zero_branch);
return result; Node* merge = SetControl(
graph()->NewNode(common->Merge(2), size_zero_if_true, Control()));
SetEffect(
graph()->NewNode(common->EffectPhi(2), size_zero_etrue, Effect(), merge));
return merge;
} }
Node* WasmGraphBuilder::ElemDrop(uint32_t elem_segment_index, Node* WasmGraphBuilder::ElemDrop(uint32_t elem_segment_index,
......
...@@ -671,7 +671,7 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) { ...@@ -671,7 +671,7 @@ void InstanceBuilder::LoadDataSegments(Handle<WasmInstanceObject> instance) {
segment.source.offset(); segment.source.offset();
memory_copy_wrapper(dest_addr, src_addr, size); memory_copy_wrapper(dest_addr, src_addr, size);
if (!ok) { if (!ok) {
thrower_->LinkError("data segment is out of bounds"); thrower_->RuntimeError("data segment is out of bounds");
return; return;
} }
} else { } else {
...@@ -1713,7 +1713,7 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) { ...@@ -1713,7 +1713,7 @@ void InstanceBuilder::LoadTableSegments(Handle<WasmInstanceObject> instance) {
table_index, elem_segment, dst, src, count); table_index, elem_segment, dst, src, count);
if (enabled_.bulk_memory) { if (enabled_.bulk_memory) {
if (!success) { if (!success) {
thrower_->LinkError("table initializer is out of bounds"); thrower_->RuntimeError("table initializer is out of bounds");
// Break out instead of returning; we don't want to continue to // Break out instead of returning; we don't want to continue to
// initialize any further element segments, but still need to add // initialize any further element segments, but still need to add
// dispatch tables below. // dispatch tables below.
......
...@@ -1792,16 +1792,16 @@ class ThreadImpl { ...@@ -1792,16 +1792,16 @@ class ThreadImpl {
MemoryInitImmediate<Decoder::kNoValidate> imm(decoder, code->at(pc)); MemoryInitImmediate<Decoder::kNoValidate> imm(decoder, code->at(pc));
DCHECK_LT(imm.data_segment_index, module()->num_declared_data_segments); DCHECK_LT(imm.data_segment_index, module()->num_declared_data_segments);
*len += imm.length; *len += imm.length;
if (!CheckDataSegmentIsPassiveAndNotDropped(imm.data_segment_index,
pc)) {
return false;
}
auto size = Pop().to<uint32_t>(); auto size = Pop().to<uint32_t>();
auto src = Pop().to<uint32_t>(); auto src = Pop().to<uint32_t>();
auto dst = Pop().to<uint32_t>(); auto dst = Pop().to<uint32_t>();
if (size == 0) { if (size == 0) {
return true; return true;
} }
if (!CheckDataSegmentIsPassiveAndNotDropped(imm.data_segment_index,
pc)) {
return false;
}
Address dst_addr; Address dst_addr;
auto src_max = auto src_max =
instance_object_->data_segment_sizes()[imm.data_segment_index]; instance_object_->data_segment_sizes()[imm.data_segment_index];
...@@ -1857,20 +1857,26 @@ class ThreadImpl { ...@@ -1857,20 +1857,26 @@ class ThreadImpl {
} }
Address dst_addr; Address dst_addr;
bool ok = BoundsCheckMemRange(dst, &size, &dst_addr); bool ok = BoundsCheckMemRange(dst, &size, &dst_addr);
if (!ok) {
DoTrap(kTrapMemOutOfBounds, pc);
return false;
}
memory_fill_wrapper(dst_addr, value, size); memory_fill_wrapper(dst_addr, value, size);
if (!ok) DoTrap(kTrapMemOutOfBounds, pc); return true;
return ok;
} }
case kExprTableInit: { case kExprTableInit: {
TableInitImmediate<Decoder::kNoValidate> imm(decoder, code->at(pc)); TableInitImmediate<Decoder::kNoValidate> imm(decoder, code->at(pc));
*len += imm.length; *len += imm.length;
auto size = Pop().to<uint32_t>();
auto src = Pop().to<uint32_t>();
auto dst = Pop().to<uint32_t>();
if (size == 0) {
return true;
}
if (!CheckElemSegmentIsPassiveAndNotDropped(imm.elem_segment_index, if (!CheckElemSegmentIsPassiveAndNotDropped(imm.elem_segment_index,
pc)) { pc)) {
return false; return false;
} }
auto size = Pop().to<uint32_t>();
auto src = Pop().to<uint32_t>();
auto dst = Pop().to<uint32_t>();
HandleScope scope(isolate_); // Avoid leaking handles. HandleScope scope(isolate_); // Avoid leaking handles.
bool ok = WasmInstanceObject::InitTableEntries( bool ok = WasmInstanceObject::InitTableEntries(
instance_object_->GetIsolate(), instance_object_, imm.table.index, instance_object_->GetIsolate(), instance_object_, imm.table.index,
......
...@@ -295,7 +295,7 @@ WASM_EXEC_TEST(MemoryFillOutOfBoundsData) { ...@@ -295,7 +295,7 @@ WASM_EXEC_TEST(MemoryFillOutOfBoundsData) {
kExprI32Const, 0); kExprI32Const, 0);
const byte v = 123; const byte v = 123;
CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize - 5, v, 999)); CHECK_EQ(0xDEADBEEF, r.Call(kWasmPageSize - 5, v, 999));
CheckMemoryEquals(&r.builder(), kWasmPageSize - 6, {0, v, v, v, v, v}); CheckMemoryEquals(&r.builder(), kWasmPageSize - 6, {0, 0, 0, 0, 0, 0});
} }
WASM_EXEC_TEST(MemoryFillOutOfBounds) { WASM_EXEC_TEST(MemoryFillOutOfBounds) {
...@@ -870,14 +870,16 @@ WASM_EXEC_TEST(ElemDropTwice) { ...@@ -870,14 +870,16 @@ WASM_EXEC_TEST(ElemDropTwice) {
WASM_EXEC_TEST(ElemDropThenTableInit) { WASM_EXEC_TEST(ElemDropThenTableInit) {
EXPERIMENTAL_FLAG_SCOPE(bulk_memory); EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
WasmRunner<uint32_t> r(execution_tier); WasmRunner<uint32_t, uint32_t> r(execution_tier);
r.builder().AddIndirectFunctionTable(nullptr, 1); r.builder().AddIndirectFunctionTable(nullptr, 1);
r.builder().AddPassiveElementSegment({}); r.builder().AddPassiveElementSegment({});
BUILD(r, WASM_ELEM_DROP(0), BUILD(
WASM_TABLE_INIT(0, 0, WASM_I32V_1(0), WASM_I32V_1(0), WASM_I32V_1(0)), r, WASM_ELEM_DROP(0),
kExprI32Const, 0); WASM_TABLE_INIT(0, 0, WASM_I32V_1(0), WASM_I32V_1(0), WASM_GET_LOCAL(0)),
kExprI32Const, 0);
r.CheckCallViaJS(0xDEADBEEF); r.CheckCallViaJS(0, 0);
r.CheckCallViaJS(0xDEADBEEF, 1);
} }
} // namespace test_run_wasm_bulk_memory } // namespace test_run_wasm_bulk_memory
......
...@@ -64,11 +64,11 @@ function getMemoryInit(mem, segment_data) { ...@@ -64,11 +64,11 @@ function getMemoryInit(mem, segment_data) {
builder.addMemory(1); builder.addMemory(1);
builder.addPassiveDataSegment([1, 2, 3]); builder.addPassiveDataSegment([1, 2, 3]);
builder.addDataSegment(0, [4, 5, 6]); builder.addDataSegment(0, [4, 5, 6]);
builder.addFunction('init', kSig_v_v) builder.addFunction('init', kSig_v_i)
.addBody([ .addBody([
kExprI32Const, 0, // Dest. kExprI32Const, 0, // Dest.
kExprI32Const, 0, // Source. kExprI32Const, 0, // Source.
kExprI32Const, 0, // Size in bytes. kExprLocalGet, 0, // Size in bytes.
kNumericPrefix, kExprMemoryInit, kNumericPrefix, kExprMemoryInit,
1, // Data segment index. 1, // Data segment index.
0, // Memory index. 0, // Memory index.
...@@ -79,7 +79,11 @@ function getMemoryInit(mem, segment_data) { ...@@ -79,7 +79,11 @@ function getMemoryInit(mem, segment_data) {
// is a trap, not a validation error. // is a trap, not a validation error.
const instance = builder.instantiate(); const instance = builder.instantiate();
assertTraps(kTrapDataSegmentDropped, () => instance.exports.init()); // Initialization succeeds, because the size is 0 which is always valid.
instance.exports.init(0);
// Initialization fails, because the size > 0 on dropped segment
assertTraps(kTrapDataSegmentDropped, () => instance.exports.init(1));
})(); })();
(function TestDataDropOnActiveSegment() { (function TestDataDropOnActiveSegment() {
...@@ -177,7 +181,7 @@ function getMemoryFill(mem) { ...@@ -177,7 +181,7 @@ function getMemoryFill(mem) {
assertEquals(0, view[kPageSize - 1]); assertEquals(0, view[kPageSize - 1]);
// Instantiation fails, but still modifies memory. // Instantiation fails, but still modifies memory.
assertThrows(() => builder.instantiate({m: {memory}}), WebAssembly.LinkError); assertThrows(() => builder.instantiate({m: {memory}}), WebAssembly.RuntimeError);
assertEquals(42, view[kPageSize - 1]); assertEquals(42, view[kPageSize - 1]);
// The second segment is not initialized. // The second segment is not initialized.
...@@ -202,7 +206,7 @@ function getMemoryFill(mem) { ...@@ -202,7 +206,7 @@ function getMemoryFill(mem) {
// Instantiation fails, but still modifies the table. The memory is not // Instantiation fails, but still modifies the table. The memory is not
// modified, since data segments are initialized after element segments. // modified, since data segments are initialized after element segments.
assertThrows( assertThrows(
() => builder.instantiate({m: {memory, table}}), WebAssembly.LinkError); () => builder.instantiate({m: {memory, table}}), WebAssembly.RuntimeError);
assertEquals(0, view[0]); assertEquals(0, view[0]);
})(); })();
...@@ -17,12 +17,6 @@ ...@@ -17,12 +17,6 @@
'proposals/js-types/exports': [FAIL], 'proposals/js-types/exports': [FAIL],
'proposals/js-types/globals': [FAIL], 'proposals/js-types/globals': [FAIL],
'proposals/js-types/linking': [FAIL], 'proposals/js-types/linking': [FAIL],
# TODO(v8:9865): Throwing behavior changed for these tests.
'proposals/bulk-memory-operations/linking': [FAIL],
'proposals/bulk-memory-operations/bulk': [FAIL],
'proposals/bulk-memory-operations/elem': [FAIL],
'proposals/bulk-memory-operations/data': [FAIL],
# TODO(thibaudm): Spec tests do not check multi-return functions correctly. # TODO(thibaudm): Spec tests do not check multi-return functions correctly.
'proposals/multi-value/call': [FAIL], 'proposals/multi-value/call': [FAIL],
'proposals/multi-value/if': [FAIL], 'proposals/multi-value/if': [FAIL],
......
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