[wasm][reference-types] Check type on Table.Init & Table.Copy

Added a type field to elements to distinguish anyref, funcref and
nullref elements and do a proper type checking at compile time as
the spec requires.

R=ahaas@chromium.org

Change-Id: I31be7aa1170439859ca7ec5e20aabb2720c290b3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2069330
Commit-Queue: Emanuel Ziegler <ecmziegler@chromium.org>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#66448}
This commit is contained in:
Emanuel Ziegler 2020-02-25 18:37:42 +01:00 committed by Commit Bot
parent 151b85a0a5
commit abb727a3d9
5 changed files with 88 additions and 44 deletions

View File

@ -1197,6 +1197,14 @@ class WasmDecoder : public Decoder {
}
if (!Validate(pc_ + imm.length - imm.table.length - 1, imm.table))
return false;
if (!VALIDATE(ValueTypes::IsSubType(
module_->elem_segments[imm.elem_segment_index].type,
module_->tables[imm.table.index].type))) {
errorf(pc_ + 2, "table %u is not a super-type of %s", imm.table.index,
ValueTypes::TypeName(
module_->elem_segments[imm.elem_segment_index].type));
return false;
}
return true;
}
@ -1212,6 +1220,13 @@ class WasmDecoder : public Decoder {
inline bool Validate(TableCopyImmediate<validate>& imm) {
if (!Validate(pc_ + 1, imm.table_src)) return false;
if (!Validate(pc_ + 2, imm.table_dst)) return false;
if (!VALIDATE(
ValueTypes::IsSubType(module_->tables[imm.table_src.index].type,
module_->tables[imm.table_dst.index].type))) {
errorf(pc_ + 2, "table %u is not a super-type of %s", imm.table_dst.index,
ValueTypes::TypeName(module_->tables[imm.table_src.index].type));
return false;
}
return true;
}

View File

@ -857,20 +857,21 @@ class ModuleDecoderImpl : public Decoder {
bool functions_as_elements;
uint32_t table_index;
WasmInitExpr offset;
consume_element_segment_header(&status, &functions_as_elements,
ValueType type = kWasmBottom;
consume_element_segment_header(&status, &functions_as_elements, &type,
&table_index, &offset);
if (failed()) return;
DCHECK_NE(type, kWasmBottom);
if (status == WasmElemSegment::kStatusActive) {
if (table_index >= module_->tables.size()) {
errorf(pos, "out of bounds table index %u", table_index);
break;
}
if (!ValueTypes::IsSubType(kWasmFuncRef,
module_->tables[table_index].type)) {
if (!ValueTypes::IsSubType(type, module_->tables[table_index].type)) {
errorf(pos,
"Invalid element segment. Table %u is not of type FuncRef",
table_index);
"Invalid element segment. Table %u is not a super-type of %s",
table_index, ValueTypes::TypeName(type));
break;
}
}
@ -885,6 +886,7 @@ class ModuleDecoderImpl : public Decoder {
}
WasmElemSegment* init = &module_->elem_segments.back();
init->type = type;
for (uint32_t j = 0; j < num_elem; j++) {
uint32_t index = functions_as_elements ? consume_element_expr()
: consume_element_func_index();
@ -1794,7 +1796,7 @@ class ModuleDecoderImpl : public Decoder {
void consume_element_segment_header(WasmElemSegment::Status* status,
bool* functions_as_elements,
uint32_t* table_index,
ValueType* type, uint32_t* table_index,
WasmInitExpr* offset) {
const byte* pos = pc();
uint8_t flag;
@ -1884,17 +1886,12 @@ class ModuleDecoderImpl : public Decoder {
// Active segments without table indices are a special case for backwards
// compatibility. These cases have an implicit element kind or element
// type, so we are done already with the segment header.
*type = kWasmFuncRef;
return;
}
if (*functions_as_elements) {
// We have to check that there is an element type of type FuncRef. All
// other element types are not valid yet.
ValueType type = consume_reference_type();
if (!ValueTypes::IsSubType(kWasmFuncRef, type)) {
error(pc_ - 1, "invalid element segment type");
return;
}
*type = consume_reference_type();
} else {
// We have to check that there is an element kind of type Function. All
// other element kinds are not valid yet.
@ -1904,6 +1901,7 @@ class ModuleDecoderImpl : public Decoder {
errorf(pos, "illegal element kind %x. Must be 0x00", val);
return;
}
*type = kWasmFuncRef;
}
}

View File

@ -116,18 +116,23 @@ struct WasmElemSegment {
// Construct an active segment.
WasmElemSegment(uint32_t table_index, WasmInitExpr offset)
: table_index(table_index), offset(offset), status(kStatusActive) {}
: type(kWasmFuncRef),
table_index(table_index),
offset(offset),
status(kStatusActive) {}
// Construct a passive or declarative segment, which has no table index or
// offset.
explicit WasmElemSegment(bool declarative)
: table_index(0),
: type(kWasmFuncRef),
table_index(0),
status(declarative ? kStatusDeclarative : kStatusPassive) {}
// Used in the {entries} vector to represent a `ref.null` entry in a passive
// segment.
V8_EXPORT_PRIVATE static const uint32_t kNullIndex = ~0u;
ValueType type;
uint32_t table_index;
WasmInitExpr offset;
std::vector<uint32_t> entries;

View File

@ -260,11 +260,16 @@ class TestModuleBuilder {
mod.maximum_pages = 100;
}
void InitializeTable() { mod.tables.emplace_back(); }
byte InitializeTable(wasm::ValueType type) {
mod.tables.emplace_back();
mod.tables.back().type = type;
return static_cast<byte>(mod.tables.size() - 1);
}
byte AddPassiveElementSegment() {
byte AddPassiveElementSegment(wasm::ValueType type) {
mod.elem_segments.emplace_back(false);
auto& init = mod.elem_segments.back();
init.type = type;
// Add 5 empty elements.
for (uint32_t j = 0; j < 5; j++) {
init.entries.push_back(WasmElemSegment::kNullIndex);
@ -1659,7 +1664,7 @@ TEST_F(FunctionBodyDecoderTest, IndirectReturnCallsWithMismatchedSigs3) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmStmt);
module = builder.module();
byte sig0 = builder.AddSignature(sigs.i_f());
@ -1706,7 +1711,7 @@ TEST_F(FunctionBodyDecoderTest, IndirectReturnCallsWithoutTableCrash) {
TEST_F(FunctionBodyDecoderTest, IncompleteIndirectReturnCall) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmStmt);
module = builder.module();
static byte code[] = {kExprReturnCallIndirect};
@ -1794,7 +1799,7 @@ TEST_F(FunctionBodyDecoderTest, IndirectCallsOutOfBounds) {
TEST_F(FunctionBodyDecoderTest, IndirectCallsWithMismatchedSigs3) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmStmt);
module = builder.module();
byte sig0 = builder.AddSignature(sigs.i_f());
@ -1832,7 +1837,7 @@ TEST_F(FunctionBodyDecoderTest, IndirectCallsWithoutTableCrash) {
TEST_F(FunctionBodyDecoderTest, IncompleteIndirectCall) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmStmt);
module = builder.module();
static byte code[] = {kExprCallIndirect};
@ -1843,7 +1848,7 @@ TEST_F(FunctionBodyDecoderTest, IncompleteStore) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeMemory();
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmStmt);
module = builder.module();
static byte code[] = {kExprI32StoreMem};
@ -1855,7 +1860,7 @@ TEST_F(FunctionBodyDecoderTest, IncompleteS8x16Shuffle) {
FunctionSig* sig = sigs.i_i();
TestModuleBuilder builder;
builder.InitializeMemory();
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmStmt);
module = builder.module();
static byte code[] = {kSimdPrefix,
@ -3210,8 +3215,8 @@ TEST_F(FunctionBodyDecoderTest, BulkMemoryOpsWithoutMemory) {
TEST_F(FunctionBodyDecoderTest, TableInit) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.AddPassiveElementSegment();
builder.InitializeTable(wasm::kWasmFuncRef);
builder.AddPassiveElementSegment(wasm::kWasmFuncRef);
module = builder.module();
ExpectFailure(sigs.v_v(),
@ -3223,10 +3228,22 @@ TEST_F(FunctionBodyDecoderTest, TableInit) {
{WASM_TABLE_INIT(0, 1, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, TableInitWrongType) {
TestModuleBuilder builder;
uint32_t table_index = builder.InitializeTable(wasm::kWasmFuncRef);
uint32_t element_index = builder.AddPassiveElementSegment(wasm::kWasmAnyRef);
module = builder.module();
WASM_FEATURE_SCOPE(bulk_memory);
WASM_FEATURE_SCOPE(anyref);
ExpectFailure(sigs.v_v(), {WASM_TABLE_INIT(table_index, element_index,
WASM_ZERO, WASM_ZERO, WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, TableInitInvalid) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.AddPassiveElementSegment();
builder.InitializeTable(wasm::kWasmFuncRef);
builder.AddPassiveElementSegment(wasm::kWasmFuncRef);
module = builder.module();
WASM_FEATURE_SCOPE(bulk_memory);
@ -3239,8 +3256,8 @@ TEST_F(FunctionBodyDecoderTest, TableInitInvalid) {
TEST_F(FunctionBodyDecoderTest, ElemDrop) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.AddPassiveElementSegment();
builder.InitializeTable(wasm::kWasmFuncRef);
builder.AddPassiveElementSegment(wasm::kWasmFuncRef);
module = builder.module();
ExpectFailure(sigs.v_v(), {WASM_ELEM_DROP(0)});
@ -3251,7 +3268,7 @@ TEST_F(FunctionBodyDecoderTest, ElemDrop) {
TEST_F(FunctionBodyDecoderTest, TableInitDeclarativeElem) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmFuncRef);
builder.AddDeclarativeElementSegment();
module = builder.module();
@ -3266,7 +3283,7 @@ TEST_F(FunctionBodyDecoderTest, TableInitDeclarativeElem) {
TEST_F(FunctionBodyDecoderTest, DeclarativeElemDrop) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmFuncRef);
builder.AddDeclarativeElementSegment();
module = builder.module();
@ -3279,7 +3296,7 @@ TEST_F(FunctionBodyDecoderTest, DeclarativeElemDrop) {
TEST_F(FunctionBodyDecoderTest, RefFuncDeclared) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmStmt);
byte function_index = builder.AddFunction(sigs.v_i());
module = builder.module();
@ -3291,7 +3308,7 @@ TEST_F(FunctionBodyDecoderTest, RefFuncDeclared) {
TEST_F(FunctionBodyDecoderTest, RefFuncUndeclared) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmStmt);
byte function_index = builder.AddFunction(sigs.v_i(), false);
module = builder.module();
@ -3302,9 +3319,9 @@ TEST_F(FunctionBodyDecoderTest, RefFuncUndeclared) {
TEST_F(FunctionBodyDecoderTest, ElemSegmentIndexUnsigned) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmFuncRef);
for (int i = 0; i < 65; ++i) {
builder.AddPassiveElementSegment();
builder.AddPassiveElementSegment(wasm::kWasmFuncRef);
}
module = builder.module();
@ -3318,7 +3335,7 @@ TEST_F(FunctionBodyDecoderTest, ElemSegmentIndexUnsigned) {
TEST_F(FunctionBodyDecoderTest, TableCopy) {
TestModuleBuilder builder;
builder.InitializeTable();
builder.InitializeTable(wasm::kWasmStmt);
module = builder.module();
ExpectFailure(sigs.v_v(),
@ -3328,6 +3345,18 @@ TEST_F(FunctionBodyDecoderTest, TableCopy) {
{WASM_TABLE_COPY(0, 0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, TableCopyWrongType) {
TestModuleBuilder builder;
uint32_t dst_table_index = builder.InitializeTable(wasm::kWasmFuncRef);
uint32_t src_table_index = builder.InitializeTable(wasm::kWasmAnyRef);
module = builder.module();
WASM_FEATURE_SCOPE(bulk_memory);
WASM_FEATURE_SCOPE(anyref);
ExpectFailure(sigs.v_v(), {WASM_TABLE_COPY(dst_table_index, src_table_index,
WASM_ZERO, WASM_ZERO, WASM_ZERO)});
}
TEST_F(FunctionBodyDecoderTest, TableGrow) {
TestModuleBuilder builder;
byte tab_func = builder.AddTable(kWasmFuncRef, 10, true, 20);
@ -3427,7 +3456,7 @@ TEST_F(FunctionBodyDecoderTest, TableOpsWithoutTable) {
}
{
WASM_FEATURE_SCOPE(bulk_memory);
builder.AddPassiveElementSegment();
builder.AddPassiveElementSegment(wasm::kWasmFuncRef);
ExpectFailure(sigs.v_v(),
{WASM_TABLE_INIT(0, 0, WASM_ZERO, WASM_ZERO, WASM_ZERO)});
ExpectFailure(sigs.v_v(),
@ -3441,7 +3470,7 @@ TEST_F(FunctionBodyDecoderTest, TableCopyMultiTable) {
{
TestModuleBuilder builder;
builder.AddTable(kWasmAnyRef, 10, true, 20);
builder.AddPassiveElementSegment();
builder.AddPassiveElementSegment(wasm::kWasmFuncRef);
module = builder.module();
// We added one table, therefore table.copy on table 0 should work.
int table_src = 0;
@ -3463,7 +3492,7 @@ TEST_F(FunctionBodyDecoderTest, TableCopyMultiTable) {
TestModuleBuilder builder;
builder.AddTable(kWasmAnyRef, 10, true, 20);
builder.AddTable(kWasmAnyRef, 10, true, 20);
builder.AddPassiveElementSegment();
builder.AddPassiveElementSegment(wasm::kWasmFuncRef);
module = builder.module();
// We added two tables, therefore table.copy on table 0 should work.
int table_src = 0;
@ -3491,7 +3520,7 @@ TEST_F(FunctionBodyDecoderTest, TableInitMultiTable) {
{
TestModuleBuilder builder;
builder.AddTable(kWasmAnyRef, 10, true, 20);
builder.AddPassiveElementSegment();
builder.AddPassiveElementSegment(wasm::kWasmFuncRef);
module = builder.module();
// We added one table, therefore table.init on table 0 should work.
int table_index = 0;
@ -3506,7 +3535,7 @@ TEST_F(FunctionBodyDecoderTest, TableInitMultiTable) {
TestModuleBuilder builder;
builder.AddTable(kWasmAnyRef, 10, true, 20);
builder.AddTable(kWasmAnyRef, 10, true, 20);
builder.AddPassiveElementSegment();
builder.AddPassiveElementSegment(wasm::kWasmFuncRef);
module = builder.module();
// We added two tables, therefore table.init on table 0 should work.
int table_index = 0;

View File

@ -15,9 +15,6 @@
'elem': [FAIL],
'data': [FAIL],
# TODO(ecmziegler): Fix failing spec tests after update.
'proposals/reference-types/table-sub': [FAIL],
# TODO(wasm): Roll newest tests into "js-types" repository.
'proposals/js-types/exports': [FAIL],
'proposals/js-types/globals': [FAIL],