Commit 63608968 authored by Ben Smith's avatar Ben Smith Committed by Commit Bot

[wasm] Implement table.init for interpreter

This also fixes CheckCallViaJS when a trap occurs. In that case, the
trap callback is called instead of an exception being thrown, so if it
isn't handled, a bogus result will be returned instead.

Bug: v8:8965
Change-Id: I560e89f353756df23c062fb8c9484d9971c19253
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1539078Reviewed-by: 's avatarMichael Starzinger <mstarzinger@chromium.org>
Commit-Queue: Ben Smith <binji@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60473}
parent fff8b5ca
......@@ -1705,6 +1705,18 @@ class ThreadImpl {
len += imm.length;
return ok;
}
case kExprTableInit: {
TableInitImmediate<Decoder::kNoValidate> imm(decoder, code->at(pc));
auto size = Pop().to<uint32_t>();
auto src = Pop().to<uint32_t>();
auto dst = Pop().to<uint32_t>();
bool ok = WasmInstanceObject::InitTableEntries(
instance_object_->GetIsolate(), instance_object_, imm.table.index,
imm.elem_segment_index, dst, src, size);
if (!ok) DoTrap(kTrapTableOutOfBounds, pc);
len += imm.length;
return ok;
}
case kExprTableCopy: {
TableCopyImmediate<Decoder::kNoValidate> imm(decoder, code->at(pc));
auto size = Pop().to<uint32_t>();
......
......@@ -27,6 +27,9 @@ namespace v8 {
namespace internal {
namespace wasm {
// static
const uint32_t WasmElemSegment::kNullIndex;
WireBytesRef WasmModule::LookupFunctionName(const ModuleWireBytes& wire_bytes,
uint32_t function_index) const {
if (!function_names) {
......
......@@ -392,6 +392,133 @@ void CheckTableCall(Isolate* isolate, Handle<WasmTableObject> table,
}
} // namespace
WASM_EXEC_TEST(TableInitElems) {
EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
TestSignatures sigs;
WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
const uint32_t kTableSize = 5;
std::vector<uint32_t> function_indexes;
const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
for (uint32_t i = 0; i < kTableSize; ++i) {
WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
BUILD(fn, WASM_I32V_1(i));
fn.SetSigIndex(sig_index);
function_indexes.push_back(fn.function_index());
}
// Passive element segment has [f0, f1, f2, f3, f4, null].
function_indexes.push_back(WasmElemSegment::kNullIndex);
r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
r.builder().AddPassiveElementSegment(function_indexes);
WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
BUILD(call, WASM_CALL_INDIRECT0(sig_index, WASM_GET_LOCAL(0)));
const uint32_t call_index = call.function_index();
BUILD(r,
WASM_TABLE_INIT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
WASM_GET_LOCAL(2)),
kExprI32Const, 0);
auto table = handle(
WasmTableObject::cast(r.builder().instance_object()->tables().get(0)),
isolate);
const double null = 0xDEADBEEF;
CheckTableCall(isolate, table, r, call_index, null, null, null, null, null);
// 0 count is ok in bounds, and at end of regions.
r.CheckCallViaJS(0, 0, 0, 0);
r.CheckCallViaJS(0, kTableSize, 0, 0);
r.CheckCallViaJS(0, 0, kTableSize, 0);
// Test actual writes.
r.CheckCallViaJS(0, 0, 0, 1);
CheckTableCall(isolate, table, r, call_index, 0, null, null, null, null);
r.CheckCallViaJS(0, 0, 0, 2);
CheckTableCall(isolate, table, r, call_index, 0, 1, null, null, null);
r.CheckCallViaJS(0, 0, 0, 3);
CheckTableCall(isolate, table, r, call_index, 0, 1, 2, null, null);
r.CheckCallViaJS(0, 3, 0, 2);
CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 0, 1);
r.CheckCallViaJS(0, 3, 1, 2);
CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 1, 2);
r.CheckCallViaJS(0, 3, 2, 2);
CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 2, 3);
r.CheckCallViaJS(0, 3, 3, 2);
CheckTableCall(isolate, table, r, call_index, 0, 1, 2, 3, 4);
}
WASM_EXEC_TEST(TableInitOob) {
EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
TestSignatures sigs;
WasmRunner<uint32_t, uint32_t, uint32_t, uint32_t> r(execution_tier);
const uint32_t kTableSize = 5;
std::vector<uint32_t> function_indexes;
const uint32_t sig_index = r.builder().AddSignature(sigs.i_v());
for (uint32_t i = 0; i < kTableSize; ++i) {
WasmFunctionCompiler& fn = r.NewFunction(sigs.i_v(), "f");
BUILD(fn, WASM_I32V_1(i));
fn.SetSigIndex(sig_index);
function_indexes.push_back(fn.function_index());
}
r.builder().AddIndirectFunctionTable(nullptr, kTableSize);
r.builder().AddPassiveElementSegment(function_indexes);
WasmFunctionCompiler& call = r.NewFunction(sigs.i_i(), "call");
BUILD(call, WASM_CALL_INDIRECT0(sig_index, WASM_GET_LOCAL(0)));
const uint32_t call_index = call.function_index();
BUILD(r,
WASM_TABLE_INIT(0, WASM_GET_LOCAL(0), WASM_GET_LOCAL(1),
WASM_GET_LOCAL(2)),
kExprI32Const, 0);
auto table = handle(
WasmTableObject::cast(r.builder().instance_object()->tables().get(0)),
isolate);
const double null = 0xDEADBEEF;
CheckTableCall(isolate, table, r, call_index, null, null, null, null, null);
// Write all values up to the out-of-bounds write.
r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
CheckTableCall(isolate, table, r, call_index, null, null, null, 0, 1);
// Write all values up to the out-of-bounds read.
r.CheckCallViaJS(0xDEADBEEF, 0, 3, 3);
CheckTableCall(isolate, table, r, call_index, 3, 4, null, 0, 1);
// 0-count is oob.
r.CheckCallViaJS(0xDEADBEEF, kTableSize + 1, 0, 0);
r.CheckCallViaJS(0xDEADBEEF, 0, kTableSize + 1, 0);
r.CheckCallViaJS(0xDEADBEEF, 0, 0, 6);
r.CheckCallViaJS(0xDEADBEEF, 0, 1, 5);
r.CheckCallViaJS(0xDEADBEEF, 0, 2, 4);
r.CheckCallViaJS(0xDEADBEEF, 0, 3, 3);
r.CheckCallViaJS(0xDEADBEEF, 0, 4, 2);
r.CheckCallViaJS(0xDEADBEEF, 0, 5, 1);
r.CheckCallViaJS(0xDEADBEEF, 0, 0, 6);
r.CheckCallViaJS(0xDEADBEEF, 1, 0, 5);
r.CheckCallViaJS(0xDEADBEEF, 2, 0, 4);
r.CheckCallViaJS(0xDEADBEEF, 3, 0, 3);
r.CheckCallViaJS(0xDEADBEEF, 4, 0, 2);
r.CheckCallViaJS(0xDEADBEEF, 5, 0, 1);
r.CheckCallViaJS(0xDEADBEEF, 10, 0, 1);
r.CheckCallViaJS(0xDEADBEEF, 0, 10, 1);
}
WASM_EXEC_TEST(TableCopyElems) {
EXPERIMENTAL_FLAG_SCOPE(bulk_memory);
Isolate* isolate = CcTest::InitIsolateOnce();
......
......@@ -266,6 +266,15 @@ uint32_t TestingModuleBuilder::AddPassiveDataSegment(Vector<const byte> bytes) {
return index;
}
uint32_t TestingModuleBuilder::AddPassiveElementSegment(
const std::vector<uint32_t>& entries) {
uint32_t index = static_cast<uint32_t>(test_module_->elem_segments.size());
test_module_->elem_segments.emplace_back();
auto& elem_segment = test_module_->elem_segments.back();
elem_segment.entries = entries;
return index;
}
CompilationEnv TestingModuleBuilder::CreateCompilationEnv() {
return {
test_module_ptr_,
......
......@@ -191,6 +191,7 @@ class TestingModuleBuilder {
uint32_t AddException(FunctionSig* sig);
uint32_t AddPassiveDataSegment(Vector<const byte> bytes);
uint32_t AddPassiveElementSegment(const std::vector<uint32_t>& entries);
WasmFunction* GetFunctionAt(int index) {
return &test_module_->functions[index];
......@@ -450,18 +451,21 @@ class WasmRunner : public WasmRunnerBase {
: WasmRunner(execution_tier, nullptr, "main", kNoRuntimeExceptionSupport,
lower_simd) {}
ReturnType Call(ParamTypes... p) {
DCHECK(compiled_);
if (interpret()) return CallInterpreter(p...);
ReturnType return_value = static_cast<ReturnType>(0xDEADBEEFDEADBEEF);
void SetUpTrapCallback() {
WasmRunnerBase::trap_happened = false;
auto trap_callback = []() -> void {
WasmRunnerBase::trap_happened = true;
set_trap_callback_for_testing(nullptr);
};
set_trap_callback_for_testing(trap_callback);
}
ReturnType Call(ParamTypes... p) {
DCHECK(compiled_);
if (interpret()) return CallInterpreter(p...);
ReturnType return_value = static_cast<ReturnType>(0xDEADBEEFDEADBEEF);
SetUpTrapCallback();
wrapper_.SetInnerCode(builder_.GetFunctionCode(main_fn_index_));
wrapper_.SetInstance(builder_.instance_object());
......@@ -508,6 +512,7 @@ class WasmRunner : public WasmRunnerBase {
void CheckCallApplyViaJS(double expected, uint32_t function_index,
Handle<Object>* buffer, int count) {
Isolate* isolate = builder_.isolate();
SetUpTrapCallback();
if (jsfuncs_.size() <= function_index) {
jsfuncs_.resize(function_index + 1);
}
......@@ -520,7 +525,7 @@ class WasmRunner : public WasmRunnerBase {
Execution::TryCall(isolate, jsfunc, global, count, buffer,
Execution::MessageHandling::kReport, nullptr);
if (retval.is_null()) {
if (retval.is_null() || WasmRunnerBase::trap_happened) {
CHECK_EQ(expected, static_cast<double>(0xDEADBEEF));
} else {
Handle<Object> result = retval.ToHandleChecked();
......
// Copyright 2018 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: --experimental-wasm-bulk-memory
load("test/mjsunit/wasm/wasm-module-builder.js");
function addFunction(builder, k) {
let m = builder.addFunction("", kSig_i_v)
.addBody([...wasmI32Const(k)]);
return m;
}
function addFunctions(builder, count) {
let o = {};
for (var i = 0; i < count; i++) {
let name = `f${i}`;
o[name] = addFunction(builder, i);
o[name].exportAs(name);
}
return o;
}
function assertTable(obj, ...elems) {
for (var i = 0; i < elems.length; i++) {
assertEquals(elems[i], obj.get(i));
}
}
(function TestTableInitInBounds() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_v_iii = builder.addType(kSig_v_iii);
let kTableSize = 5;
builder.setTableBounds(kTableSize, kTableSize);
{
let o = addFunctions(builder, kTableSize);
builder.addPassiveElementSegment(
[o.f0.index, o.f1.index, o.f2.index, o.f3.index, o.f4.index, null]);
}
builder.addFunction("init0", sig_v_iii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprGetLocal, 2,
kNumericPrefix, kExprTableInit, kSegmentZero, kTableZero])
.exportAs("init0");
builder.addExportOfKind("table", kExternalTable, 0);
let instance = builder.instantiate();
let x = instance.exports;
assertTable(x.table, null, null, null, null, null);
// 0 count is ok in bounds, and at end of regions.
x.init0(0, 0, 0);
x.init0(kTableSize, 0, 0);
x.init0(0, kTableSize, 0);
// test actual writes.
x.init0(0, 0, 1);
assertTable(x.table, x.f0, null, null, null, null);
x.init0(0, 0, 2);
assertTable(x.table, x.f0, x.f1, null, null, null);
x.init0(0, 0, 3);
assertTable(x.table, x.f0, x.f1, x.f2, null, null);
x.init0(3, 0, 2);
assertTable(x.table, x.f0, x.f1, x.f2, x.f0, x.f1);
x.init0(3, 1, 2);
assertTable(x.table, x.f0, x.f1, x.f2, x.f1, x.f2);
x.init0(3, 2, 2);
assertTable(x.table, x.f0, x.f1, x.f2, x.f2, x.f3);
x.init0(3, 3, 2);
assertTable(x.table, x.f0, x.f1, x.f2, x.f3, x.f4);
// test writing null
x.init0(0, 5, 1);
assertTable(x.table, null, x.f1, x.f2, x.f3, x.f4);
})();
(function TestTableInitOob() {
print(arguments.callee.name);
let builder = new WasmModuleBuilder();
let sig_v_iii = builder.addType(kSig_v_iii);
let kTableSize = 5;
builder.setTableBounds(kTableSize, kTableSize);
{
let o = addFunctions(builder, kTableSize);
builder.addPassiveElementSegment(
[o.f0.index, o.f1.index, o.f2.index, o.f3.index, o.f4.index]);
}
builder.addFunction("init0", sig_v_iii)
.addBody([
kExprGetLocal, 0,
kExprGetLocal, 1,
kExprGetLocal, 2,
kNumericPrefix, kExprTableInit, kSegmentZero, kTableZero])
.exportAs("init0");
builder.addExportOfKind("table", kExternalTable, 0);
let instance = builder.instantiate();
let x = instance.exports;
assertTable(x.table, null, null, null, null, null);
// Write all values up to the out-of-bounds write.
assertThrows(() => x.init0(3, 0, 3));
assertTable(x.table, null, null, null, x.f0, x.f1);
// Write all values up to the out-of-bounds read.
assertThrows(() => x.init0(0, 3, 3));
assertTable(x.table, x.f3, x.f4, null, x.f0, x.f1);
// 0-count is oob.
assertThrows(() => x.init0(kTableSize+1, 0, 0));
assertThrows(() => x.init0(0, kTableSize+1, 0));
assertThrows(() => x.init0(0, 0, 6));
assertThrows(() => x.init0(0, 1, 5));
assertThrows(() => x.init0(0, 2, 4));
assertThrows(() => x.init0(0, 3, 3));
assertThrows(() => x.init0(0, 4, 2));
assertThrows(() => x.init0(0, 5, 1));
assertThrows(() => x.init0(0, 0, 6));
assertThrows(() => x.init0(1, 0, 5));
assertThrows(() => x.init0(2, 0, 4));
assertThrows(() => x.init0(3, 0, 3));
assertThrows(() => x.init0(4, 0, 2));
assertThrows(() => x.init0(5, 0, 1));
assertThrows(() => x.init0(10, 0, 1));
assertThrows(() => x.init0(0, 10, 1));
})();
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