Commit 5e1cf17b authored by Manos Koukoutos's avatar Manos Koukoutos Committed by Commit Bot

[wasm-gc] Check for illegal indexes in fun defs

Reference types in function definitions signatures are not allowed to
refer to function types (this will change when we fully integrate the
typed function references proposal).

Bug: v8:7748
Change-Id: I2456b810f85e608c48a952ef9e64d7a8ff78892b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2231352
Commit-Queue: Manos Koukoutos <manoskouk@chromium.org>
Reviewed-by: 's avatarAndreas Haas <ahaas@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68264}
parent ebfb8771
...@@ -560,7 +560,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -560,7 +560,8 @@ class ModuleDecoderImpl : public Decoder {
uint8_t kind = consume_u8("type kind"); uint8_t kind = consume_u8("type kind");
switch (kind) { switch (kind) {
case kWasmFunctionTypeCode: { case kWasmFunctionTypeCode: {
const FunctionSig* s = consume_sig(module_->signature_zone.get()); const FunctionSig* s = consume_sig(module_->signature_zone.get(),
DeferIndexCheckMode::kDeferCheck);
module_->add_signature(s); module_->add_signature(s);
break; break;
} }
...@@ -593,19 +594,21 @@ class ModuleDecoderImpl : public Decoder { ...@@ -593,19 +594,21 @@ class ModuleDecoderImpl : public Decoder {
VerifyDeferredTypeOffsets(); VerifyDeferredTypeOffsets();
} }
// TODO(7748): When typed function references are allowed, this should be
// deleted altogether and replaced by an inline in-bounds check.
void VerifyDeferredTypeOffsets() { void VerifyDeferredTypeOffsets() {
for (auto& struct_offset : deferred_struct_field_type_offsets_) { for (auto& type_offset : deferred_check_type_index_) {
if (struct_offset.first >= module_->type_kinds.size()) { uint32_t type_index = type_offset.first;
errorf(struct_offset.second, "reference to undeclared struct/array #%u", uint32_t code_offset = type_offset.second;
struct_offset.first); if (type_index >= module_->type_kinds.size()) {
errorf(code_offset, "reference to undeclared struct/array #%u",
type_index);
break; break;
} }
uint8_t type = module_->type_kinds[struct_offset.first]; uint8_t type = module_->type_kinds[type_index];
if (type != kWasmStructTypeCode && type != kWasmArrayTypeCode) { if (type == kWasmFunctionTypeCode) {
errorf(struct_offset.second, errorf(code_offset, "cannot build reference to function type index #%u",
"array element type or struct field " type_index);
"references non-type index #%u",
struct_offset.first);
break; break;
} }
} }
...@@ -1291,7 +1294,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1291,7 +1294,7 @@ class ModuleDecoderImpl : public Decoder {
pc_ = start_; pc_ = start_;
expect_u8("type form", kWasmFunctionTypeCode); expect_u8("type form", kWasmFunctionTypeCode);
if (!ok()) return FunctionResult{std::move(intermediate_error_)}; if (!ok()) return FunctionResult{std::move(intermediate_error_)};
function->sig = consume_sig(zone); function->sig = consume_sig(zone, DeferIndexCheckMode::kNoCheck);
function->code = {off(pc_), static_cast<uint32_t>(end_ - pc_)}; function->code = {off(pc_), static_cast<uint32_t>(end_ - pc_)};
if (ok()) if (ok())
...@@ -1309,7 +1312,8 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1309,7 +1312,8 @@ class ModuleDecoderImpl : public Decoder {
const FunctionSig* DecodeFunctionSignature(Zone* zone, const byte* start) { const FunctionSig* DecodeFunctionSignature(Zone* zone, const byte* start) {
pc_ = start; pc_ = start;
if (!expect_u8("type form", kWasmFunctionTypeCode)) return nullptr; if (!expect_u8("type form", kWasmFunctionTypeCode)) return nullptr;
const FunctionSig* result = consume_sig(zone); const FunctionSig* result =
consume_sig(zone, DeferIndexCheckMode::kNoCheck);
return ok() ? result : nullptr; return ok() ? result : nullptr;
} }
...@@ -1353,7 +1357,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1353,7 +1357,7 @@ class ModuleDecoderImpl : public Decoder {
// Set of type offsets discovered in field types during type section decoding. // Set of type offsets discovered in field types during type section decoding.
// Since struct types may be recursive, this is used for checking and error // Since struct types may be recursive, this is used for checking and error
// reporting once the whole type section is parsed. // reporting once the whole type section is parsed.
std::unordered_map<uint32_t, int> deferred_struct_field_type_offsets_; std::unordered_map<uint32_t, int> deferred_check_type_index_;
ModuleOrigin origin_; ModuleOrigin origin_;
bool has_seen_unordered_section(SectionCode section_code) { bool has_seen_unordered_section(SectionCode section_code) {
...@@ -1782,7 +1786,15 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1782,7 +1786,15 @@ class ModuleDecoderImpl : public Decoder {
return kWasmStmt; return kWasmStmt;
} }
const FunctionSig* consume_sig(Zone* zone) { enum DeferIndexCheckMode { kNoCheck, kDeferCheck };
void defer_index_check(ValueType type) {
if (type.has_immediate()) {
deferred_check_type_index_.emplace(type.ref_index(), pc_offset());
}
}
const FunctionSig* consume_sig(Zone* zone, DeferIndexCheckMode defer_check) {
// Parse parameter types. // Parse parameter types.
uint32_t param_count = uint32_t param_count =
consume_count("param count", kV8MaxWasmFunctionParams); consume_count("param count", kV8MaxWasmFunctionParams);
...@@ -1790,6 +1802,9 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1790,6 +1802,9 @@ class ModuleDecoderImpl : public Decoder {
std::vector<ValueType> params; std::vector<ValueType> params;
for (uint32_t i = 0; ok() && i < param_count; ++i) { for (uint32_t i = 0; ok() && i < param_count; ++i) {
ValueType param = consume_value_type(); ValueType param = consume_value_type();
if (defer_check == DeferIndexCheckMode::kDeferCheck) {
defer_index_check(param);
}
params.push_back(param); params.push_back(param);
} }
std::vector<ValueType> returns; std::vector<ValueType> returns;
...@@ -1801,6 +1816,9 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1801,6 +1816,9 @@ class ModuleDecoderImpl : public Decoder {
if (failed()) return nullptr; if (failed()) return nullptr;
for (uint32_t i = 0; ok() && i < return_count; ++i) { for (uint32_t i = 0; ok() && i < return_count; ++i) {
ValueType ret = consume_value_type(); ValueType ret = consume_value_type();
if (defer_check == DeferIndexCheckMode::kDeferCheck) {
defer_index_check(ret);
}
returns.push_back(ret); returns.push_back(ret);
} }
...@@ -1823,10 +1841,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1823,10 +1841,7 @@ class ModuleDecoderImpl : public Decoder {
bool* mutabilities = zone->NewArray<bool>(field_count); bool* mutabilities = zone->NewArray<bool>(field_count);
for (uint32_t i = 0; ok() && i < field_count; ++i) { for (uint32_t i = 0; ok() && i < field_count; ++i) {
ValueType field = consume_storage_type(); ValueType field = consume_storage_type();
if (field.has_immediate()) { defer_index_check(field);
deferred_struct_field_type_offsets_.emplace(field.ref_index(),
pc_offset());
}
fields[i] = field; fields[i] = field;
bool mutability = consume_mutability(); bool mutability = consume_mutability();
mutabilities[i] = mutability; mutabilities[i] = mutability;
...@@ -1839,10 +1854,7 @@ class ModuleDecoderImpl : public Decoder { ...@@ -1839,10 +1854,7 @@ class ModuleDecoderImpl : public Decoder {
const ArrayType* consume_array(Zone* zone) { const ArrayType* consume_array(Zone* zone) {
ValueType field = consume_storage_type(); ValueType field = consume_storage_type();
if (failed()) return nullptr; if (failed()) return nullptr;
if (field.has_immediate()) { defer_index_check(field);
deferred_struct_field_type_offsets_.emplace(field.ref_index(),
pc_offset());
}
bool mutability = consume_mutability(); bool mutability = consume_mutability();
if (!mutability) { if (!mutability) {
error(this->pc() - 1, "immutable arrays are not supported yet"); error(this->pc() - 1, "immutable arrays are not supported yet");
......
...@@ -991,7 +991,7 @@ TEST(GcStructIdsPass) { ...@@ -991,7 +991,7 @@ TEST(GcStructIdsPass) {
Cleanup(); Cleanup();
} }
TEST(GcStructIdsUndefinedIndex) { TEST(GcTypeIdsUndefinedIndex) {
{ {
EXPERIMENTAL_FLAG_SCOPE(gc); EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(anyref); EXPERIMENTAL_FLAG_SCOPE(anyref);
...@@ -999,7 +999,7 @@ TEST(GcStructIdsUndefinedIndex) { ...@@ -999,7 +999,7 @@ TEST(GcStructIdsUndefinedIndex) {
HandleScope scope(isolate); HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate); testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "GcStructIdsUndefinedIndex"); ErrorThrower thrower(isolate, "GcTypeIdsUndefinedIndex");
const byte data[] = { const byte data[] = {
WASM_MODULE_HEADER, // -- WASM_MODULE_HEADER, // --
...@@ -1024,7 +1024,7 @@ TEST(GcStructIdsUndefinedIndex) { ...@@ -1024,7 +1024,7 @@ TEST(GcStructIdsUndefinedIndex) {
Cleanup(); Cleanup();
} }
TEST(GcStructIdsIllegalIndex) { TEST(GcTypeIdsIllegalIndex) {
{ {
EXPERIMENTAL_FLAG_SCOPE(gc); EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(anyref); EXPERIMENTAL_FLAG_SCOPE(anyref);
...@@ -1032,7 +1032,7 @@ TEST(GcStructIdsIllegalIndex) { ...@@ -1032,7 +1032,7 @@ TEST(GcStructIdsIllegalIndex) {
HandleScope scope(isolate); HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate); testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "GcStructIdsIllegalIndex"); ErrorThrower thrower(isolate, "GcTypeIdsIllegalIndex");
const byte data[] = { const byte data[] = {
WASM_MODULE_HEADER, // -- WASM_MODULE_HEADER, // --
...@@ -1055,8 +1055,40 @@ TEST(GcStructIdsIllegalIndex) { ...@@ -1055,8 +1055,40 @@ TEST(GcStructIdsIllegalIndex) {
// There should be an error reflecting an invalid index. // There should be an error reflecting an invalid index.
CHECK(thrower.error()); CHECK(thrower.error());
CHECK_NE(std::string(thrower.error_msg()) CHECK_NE(std::string(thrower.error_msg())
.find("array element type or struct " .find("cannot build reference to function type index"),
"field references non-type index"), std::string::npos);
}
Cleanup();
}
TEST(GcTypeIdsFunSigIllegalIndex) {
{
EXPERIMENTAL_FLAG_SCOPE(gc);
EXPERIMENTAL_FLAG_SCOPE(anyref);
Isolate* isolate = CcTest::InitIsolateOnce();
HandleScope scope(isolate);
testing::SetupIsolateForWasmModule(isolate);
ErrorThrower thrower(isolate, "GcTypeIdsFumSigIllegalIndex");
const byte data[] = {
WASM_MODULE_HEADER, // --
kTypeSectionCode, // --
U32V_1(7), // Section size
U32V_1(1), // type count
kWasmFunctionTypeCode, // index 1 = int32 -> int32
U32V_1(1), // param count
kLocalI32, // param 0
U32V_1(1), // returns count
kLocalRef, // return 0
U32V_1(0) // --
};
CompileAndInstantiateForTesting(
isolate, &thrower, ModuleWireBytes(data, data + arraysize(data)));
// There should be an error reflecting an invalid index.
CHECK(thrower.error());
CHECK_NE(std::string(thrower.error_msg())
.find("cannot build reference to function type index"),
std::string::npos); std::string::npos);
} }
Cleanup(); Cleanup();
......
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