[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:
parent
151b85a0a5
commit
abb727a3d9
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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],
|
||||
|
Loading…
Reference in New Issue
Block a user