diff options
Diffstat (limited to 'deps/v8/src/wasm/module-decoder-impl.h')
-rw-r--r-- | deps/v8/src/wasm/module-decoder-impl.h | 873 |
1 files changed, 410 insertions, 463 deletions
diff --git a/deps/v8/src/wasm/module-decoder-impl.h b/deps/v8/src/wasm/module-decoder-impl.h index 9cecb23da3..9396fe11d6 100644 --- a/deps/v8/src/wasm/module-decoder-impl.h +++ b/deps/v8/src/wasm/module-decoder-impl.h @@ -17,53 +17,18 @@ #include "src/wasm/constant-expression-interface.h" #include "src/wasm/function-body-decoder-impl.h" #include "src/wasm/module-decoder.h" +#include "src/wasm/wasm-engine.h" #include "src/wasm/wasm-module.h" #include "src/wasm/wasm-subtyping.h" +#include "src/wasm/well-known-imports.h" -namespace v8 { -namespace internal { -namespace wasm { +namespace v8::internal::wasm { #define TRACE(...) \ do { \ if (v8_flags.trace_wasm_decoder) PrintF(__VA_ARGS__); \ } while (false) -class NoTracer { - public: - // Hooks for extracting byte offsets of things. - void TypeOffset(uint32_t offset) {} - void ImportOffset(uint32_t offset) {} - void ImportsDone() {} - void TableOffset(uint32_t offset) {} - void MemoryOffset(uint32_t offset) {} - void TagOffset(uint32_t offset) {} - void GlobalOffset(uint32_t offset) {} - void StartOffset(uint32_t offset) {} - void ElementOffset(uint32_t offset) {} - void DataOffset(uint32_t offset) {} - - // Hooks for annotated hex dumps. - void Bytes(const byte* start, uint32_t count) {} - - void Description(const char* desc) {} - void Description(const char* desc, size_t length) {} - void Description(uint32_t number) {} - void Description(ValueType type) {} - void Description(HeapType type) {} - void Description(const FunctionSig* sig) {} - - void NextLine() {} - void NextLineIfFull() {} - void NextLineIfNonEmpty() {} - - void InitializerExpression(const byte* start, const byte* end, - ValueType expected_type) {} - void FunctionBody(const WasmFunction* func, const byte* start) {} - void FunctionName(uint32_t func_index) {} - void NameSection(const byte* start, const byte* end, uint32_t offset) {} -}; - constexpr char kNameString[] = "name"; constexpr char kSourceMappingURLString[] = "sourceMappingURL"; constexpr char kInstTraceString[] = "metadata.code.trace_inst"; @@ -96,23 +61,26 @@ inline bool validate_utf8(Decoder* decoder, WireBytesRef string) { // Reads a length-prefixed string, checking that it is within bounds. Returns // the offset of the string, and the length as an out parameter. -template <class Tracer> inline WireBytesRef consume_string(Decoder* decoder, unibrow::Utf8Variant grammar, - const char* name, Tracer& tracer) { - tracer.Description(name); + const char* name, ITracer* tracer) { + if (tracer) tracer->Description(name); uint32_t length = decoder->consume_u32v(" length:", tracer); - tracer.Description(length); - tracer.NextLine(); + if (tracer) { + tracer->Description(length); + tracer->NextLine(); + } uint32_t offset = decoder->pc_offset(); const byte* string_start = decoder->pc(); // Consume bytes before validation to guarantee that the string is not oob. if (length > 0) { - tracer.Bytes(decoder->pc(), length); - tracer.Description(name); - tracer.Description(": "); - tracer.Description(reinterpret_cast<const char*>(decoder->pc()), length); - tracer.NextLine(); + if (tracer) { + tracer->Bytes(decoder->pc(), length); + tracer->Description(name); + tracer->Description(": "); + tracer->Description(reinterpret_cast<const char*>(decoder->pc()), length); + tracer->NextLine(); + } decoder->consume_bytes(length, name); if (decoder->ok()) { switch (grammar) { @@ -128,6 +96,8 @@ inline WireBytesRef consume_string(Decoder* decoder, decoder->errorf(string_start, "%s: no valid WTF-8 string", name); } break; + case unibrow::Utf8Variant::kUtf8NoTrap: + UNREACHABLE(); } } } @@ -137,19 +107,16 @@ inline WireBytesRef consume_string(Decoder* decoder, inline WireBytesRef consume_string(Decoder* decoder, unibrow::Utf8Variant grammar, const char* name) { - NoTracer no_tracer; - return consume_string(decoder, grammar, name, no_tracer); + return consume_string(decoder, grammar, name, ITracer::NoTrace); } -template <class Tracer> inline WireBytesRef consume_utf8_string(Decoder* decoder, const char* name, - Tracer& tracer) { + ITracer* tracer) { return consume_string(decoder, unibrow::Utf8Variant::kUtf8, name, tracer); } -template <class Tracer> inline SectionCode IdentifyUnknownSectionInternal(Decoder* decoder, - Tracer& tracer) { + ITracer* tracer) { WireBytesRef string = consume_utf8_string(decoder, "section name", tracer); if (decoder->failed()) { return kUnknownSectionCode; @@ -185,10 +152,9 @@ inline SectionCode IdentifyUnknownSectionInternal(Decoder* decoder, // An iterator over the sections in a wasm binary module. // Automatically skips all unknown sections. -template <class Tracer> class WasmSectionIterator { public: - explicit WasmSectionIterator(Decoder* decoder, Tracer& tracer) + explicit WasmSectionIterator(Decoder* decoder, ITracer* tracer) : decoder_(decoder), tracer_(tracer), section_code_(kUnknownSectionCode), @@ -239,7 +205,7 @@ class WasmSectionIterator { private: Decoder* decoder_; - Tracer& tracer_; + ITracer* tracer_; SectionCode section_code_; const byte* section_start_; const byte* payload_start_; @@ -253,21 +219,28 @@ class WasmSectionIterator { return; } section_start_ = decoder_->pc(); - tracer_.NextLine(); // Empty line before next section. + // Empty line before next section. + if (tracer_) tracer_->NextLine(); uint8_t section_code = decoder_->consume_u8("section kind: ", tracer_); - tracer_.Description(SectionName(static_cast<SectionCode>(section_code))); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(SectionName(static_cast<SectionCode>(section_code))); + tracer_->NextLine(); + } // Read and check the section size. uint32_t section_length = decoder_->consume_u32v("section length", tracer_); - tracer_.Description(section_length); - tracer_.NextLine(); - + if (tracer_) { + tracer_->Description(section_length); + tracer_->NextLine(); + } payload_start_ = decoder_->pc(); - if (decoder_->checkAvailable(section_length)) { - // Get the limit of the section within the module. - section_end_ = payload_start_ + section_length; - } else { - // The section would extend beyond the end of the module. + section_end_ = payload_start_ + section_length; + if (section_length > decoder_->available_bytes()) { + decoder_->errorf( + section_start_, + "section (code %u, \"%s\") extends past end of the module " + "(length %u, remaining bytes %u)", + section_code, SectionName(static_cast<SectionCode>(section_code)), + section_length, decoder_->available_bytes()); section_end_ = payload_start_; } @@ -299,35 +272,18 @@ class WasmSectionIterator { } }; -// Add an explicit template deduction guide for {WasmSectionIterator}. -template <class T> -WasmSectionIterator(Decoder*, T&) -> WasmSectionIterator<T>; - // The main logic for decoding the bytes of a module. -template <class Tracer> -class ModuleDecoderTemplate : public Decoder { +class ModuleDecoderImpl : public Decoder { public: - explicit ModuleDecoderTemplate(const WasmFeatures& enabled, - ModuleOrigin origin, Tracer& tracer) - : Decoder(nullptr, nullptr), - enabled_features_(enabled), - tracer_(tracer), - origin_(origin) {} - - ModuleDecoderTemplate(const WasmFeatures& enabled, const byte* module_start, - const byte* module_end, ModuleOrigin origin, - Tracer& tracer) - : Decoder(module_start, module_end), - enabled_features_(enabled), - module_start_(module_start), - module_end_(module_end), - tracer_(tracer), - origin_(origin) { - if (end_ < start_) { - error(start_, "end is less than start"); - end_ = start_; - } - } + ModuleDecoderImpl(WasmFeatures enabled_features, + base::Vector<const uint8_t> wire_bytes, ModuleOrigin origin, + ITracer* tracer = ITracer::NoTrace) + : Decoder(wire_bytes), + enabled_features_(enabled_features), + module_(std::make_shared<WasmModule>(origin)), + module_start_(wire_bytes.begin()), + module_end_(wire_bytes.end()), + tracer_(tracer) {} void onFirstError() override { pc_ = end_; // On error, terminate section decoding loop. @@ -359,24 +315,13 @@ class ModuleDecoderTemplate : public Decoder { } } - void StartDecoding(Counters* counters, AccountingAllocator* allocator) { - CHECK_NULL(module_); - SetCounters(counters); - module_.reset( - new WasmModule(std::make_unique<Zone>(allocator, "signatures"))); - module_->initial_pages = 0; - module_->maximum_pages = 0; - module_->mem_export = false; - module_->origin = origin_; - } - void DecodeModuleHeader(base::Vector<const uint8_t> bytes, uint8_t offset) { if (failed()) return; Reset(bytes, offset); const byte* pos = pc_; uint32_t magic_word = consume_u32("wasm magic", tracer_); - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); #define BYTES(x) (x & 0xFF), (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF if (magic_word != kWasmMagic) { errorf(pos, @@ -388,7 +333,7 @@ class ModuleDecoderTemplate : public Decoder { pos = pc_; { uint32_t magic_version = consume_u32("wasm version", tracer_); - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); if (magic_version != kWasmVersion) { errorf(pos, "expected version %02x %02x %02x %02x, " @@ -447,9 +392,6 @@ class ModuleDecoderTemplate : public Decoder { // Now check the ordering constraints of specific unordered sections. switch (section_code) { case kDataCountSectionCode: - // If wasm-gc is enabled, we allow the data count section anywhere in - // the module. - if (enabled_features_.has_gc()) return true; return check_order(kElementSectionCode, kCodeSectionCode); case kTagSectionCode: return check_order(kMemorySectionCode, kGlobalSectionCode); @@ -601,54 +543,56 @@ class ModuleDecoderTemplate : public Decoder { TypeDefinition consume_base_type_definition() { DCHECK(enabled_features_.has_gc()); uint8_t kind = consume_u8(" kind: ", tracer_); - tracer_.Description(TypeKindName(kind)); + if (tracer_) tracer_->Description(TypeKindName(kind)); switch (kind) { case kWasmFunctionTypeCode: { - const FunctionSig* sig = consume_sig(module_->signature_zone.get()); - return {sig, kNoSuperType}; + const FunctionSig* sig = consume_sig(&module_->signature_zone); + return {sig, kNoSuperType, v8_flags.wasm_final_types}; } case kWasmStructTypeCode: { - const StructType* type = consume_struct(module_->signature_zone.get()); - return {type, kNoSuperType}; + const StructType* type = consume_struct(&module_->signature_zone); + return {type, kNoSuperType, v8_flags.wasm_final_types}; } case kWasmArrayTypeCode: { - const ArrayType* type = consume_array(module_->signature_zone.get()); - return {type, kNoSuperType}; + const ArrayType* type = consume_array(&module_->signature_zone); + return {type, kNoSuperType, v8_flags.wasm_final_types}; } default: - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); errorf(pc() - 1, "unknown type form: %d", kind); return {}; } } - bool check_supertype(uint32_t supertype) { - if (V8_UNLIKELY(supertype >= module_->types.size())) { - errorf(pc(), "type %zu: forward-declared supertype %d", - module_->types.size(), supertype); - return false; - } - return true; - } - TypeDefinition consume_subtype_definition() { DCHECK(enabled_features_.has_gc()); uint8_t kind = read_u8<Decoder::FullValidationTag>(pc(), "type kind"); - if (kind == kWasmSubtypeCode) { - consume_bytes(1, " subtype, ", tracer_); + if (kind == kWasmSubtypeCode || kind == kWasmSubtypeFinalCode) { + bool is_final = + v8_flags.wasm_final_types && kind == kWasmSubtypeFinalCode; + consume_bytes(1, is_final ? " subtype final, " : " subtype extensible, ", + tracer_); constexpr uint32_t kMaximumSupertypes = 1; uint32_t supertype_count = consume_count("supertype count", kMaximumSupertypes); - uint32_t supertype = supertype_count == 1 - ? consume_u32v("supertype", tracer_) - : kNoSuperType; + uint32_t supertype = kNoSuperType; if (supertype_count == 1) { - tracer_.Description(supertype); - tracer_.NextLine(); + supertype = consume_u32v("supertype", tracer_); + if (supertype >= kV8MaxWasmTypes) { + errorf( + "supertype %u is greater than the maximum number of type " + "definitions %zu supported by V8", + supertype, kV8MaxWasmTypes); + return {}; + } + if (tracer_) { + tracer_->Description(supertype); + tracer_->NextLine(); + } } - if (!check_supertype(supertype)) return {}; TypeDefinition type = consume_base_type_definition(); type.supertype = supertype; + type.is_final = is_final; return type; } else { return consume_base_type_definition(); @@ -661,29 +605,33 @@ class ModuleDecoderTemplate : public Decoder { // Non wasm-gc type section decoding. if (!enabled_features_.has_gc()) { - module_->types.reserve(types_count); + module_->types.resize(types_count); + module_->isorecursive_canonical_type_ids.resize(types_count); for (uint32_t i = 0; i < types_count; ++i) { TRACE("DecodeSignature[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); uint8_t opcode = read_u8<FullValidationTag>(pc(), "signature definition"); - tracer_.Bytes(pc_, 1); - tracer_.TypeOffset(pc_offset()); - tracer_.Description(" kind: "); - tracer_.Description(TypeKindName(opcode)); - tracer_.NextLine(); + if (tracer_) { + tracer_->Bytes(pc_, 1); + tracer_->TypeOffset(pc_offset()); + tracer_->Description(" kind: "); + tracer_->Description(TypeKindName(opcode)); + tracer_->NextLine(); + } switch (opcode) { case kWasmFunctionTypeCode: { consume_bytes(1, "function"); - const FunctionSig* sig = consume_sig(module_->signature_zone.get()); + const FunctionSig* sig = consume_sig(&module_->signature_zone); if (!ok()) break; - module_->add_signature(sig, kNoSuperType); - type_canon->AddRecursiveGroup(module_.get(), 1); + module_->types[i] = {sig, kNoSuperType, v8_flags.wasm_final_types}; + type_canon->AddRecursiveGroup(module_.get(), 1, i); break; } case kWasmArrayTypeCode: case kWasmStructTypeCode: case kWasmSubtypeCode: + case kWasmSubtypeFinalCode: case kWasmRecursiveTypeGroupCode: errorf( "Unknown type code 0x%02x, enable with --experimental-wasm-gc", @@ -701,30 +649,39 @@ class ModuleDecoderTemplate : public Decoder { for (uint32_t i = 0; ok() && i < types_count; ++i) { TRACE("DecodeType[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); uint8_t kind = read_u8<Decoder::FullValidationTag>(pc(), "type kind"); + size_t initial_size = module_->types.size(); if (kind == kWasmRecursiveTypeGroupCode) { consume_bytes(1, "rec. group definition", tracer_); - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); uint32_t group_size = consume_count("recursive group size", kV8MaxWasmTypes); - if (module_->types.size() + group_size > kV8MaxWasmTypes) { + if (initial_size + group_size > kV8MaxWasmTypes) { errorf(pc(), "Type definition count exceeds maximum %zu", kV8MaxWasmTypes); return; } - // Reserve space for the current recursive group, so we are - // allowed to reference its elements. - module_->types.reserve(module_->types.size() + group_size); + // We need to resize types before decoding the type definitions in this + // group, so that the correct type size is visible to type definitions. + module_->types.resize(initial_size + group_size); + module_->isorecursive_canonical_type_ids.resize(initial_size + + group_size); for (uint32_t j = 0; j < group_size; j++) { - tracer_.TypeOffset(pc_offset()); + if (tracer_) tracer_->TypeOffset(pc_offset()); TypeDefinition type = consume_subtype_definition(); - if (ok()) module_->add_type(type); + if (ok()) module_->types[initial_size + j] = type; + } + if (ok()) { + type_canon->AddRecursiveGroup(module_.get(), group_size, + static_cast<uint32_t>(initial_size)); } - if (ok()) type_canon->AddRecursiveGroup(module_.get(), group_size); } else { - tracer_.TypeOffset(pc_offset()); + if (tracer_) tracer_->TypeOffset(pc_offset()); + // Similarly to above, we need to resize types for a group of size 1. + module_->types.resize(initial_size + 1); + module_->isorecursive_canonical_type_ids.resize(initial_size + 1); TypeDefinition type = consume_subtype_definition(); if (ok()) { - module_->add_type(type); + module_->types[initial_size] = type; type_canon->AddRecursiveGroup(module_.get(), 1); } } @@ -735,16 +692,28 @@ class ModuleDecoderTemplate : public Decoder { for (uint32_t i = 0; ok() && i < module_->types.size(); ++i) { uint32_t explicit_super = module_->supertype(i); if (explicit_super == kNoSuperType) continue; - // {consume_super_type} has checked this. - DCHECK_LT(explicit_super, module_->types.size()); + if (explicit_super >= module_->types.size()) { + errorf("type %u: supertype %u out of bounds", i, explicit_super); + continue; + } + if (explicit_super >= i) { + errorf("type %u: forward-declared supertype %u", i, explicit_super); + continue; + } int depth = GetSubtypingDepth(module, i); DCHECK_GE(depth, 0); if (depth > static_cast<int>(kV8MaxRttSubtypingDepth)) { - errorf("type %d: subtyping depth is greater than allowed", i); + errorf("type %u: subtyping depth is greater than allowed", i); + continue; + } + // This check is technically redundant; we include for the improved error + // message. + if (module->types[explicit_super].is_final) { + errorf("type %u extends final type %u", i, explicit_super); continue; } if (!ValidSubtypeDefinition(i, explicit_super, module, module)) { - errorf("type %d has invalid explicit supertype %d", i, explicit_super); + errorf("type %u has invalid explicit supertype %u", i, explicit_super); continue; } } @@ -757,7 +726,7 @@ class ModuleDecoderTemplate : public Decoder { for (uint32_t i = 0; ok() && i < import_table_count; ++i) { TRACE("DecodeImportTable[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); - tracer_.ImportOffset(pc_offset()); + if (tracer_) tracer_->ImportOffset(pc_offset()); module_->import_table.push_back({ {0, 0}, // module_name @@ -771,7 +740,7 @@ class ModuleDecoderTemplate : public Decoder { import->field_name = consume_utf8_string(this, "field name", tracer_); import->kind = static_cast<ImportExportKindCode>(consume_u8("kind: ", tracer_)); - tracer_.Description(ExternalKindName(import->kind)); + if (tracer_) tracer_->Description(ExternalKindName(import->kind)); switch (import->kind) { case kExternalFunction: { // ===== Imported function =========================================== @@ -836,7 +805,7 @@ class ModuleDecoderTemplate : public Decoder { if (global->mutability) { module_->num_imported_mutable_globals++; } - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); break; } case kExternalTag: { @@ -845,8 +814,8 @@ class ModuleDecoderTemplate : public Decoder { module_->num_imported_tags++; const WasmTagSig* tag_sig = nullptr; consume_exception_attribute(); // Attribute ignored for now. - consume_tag_sig_index(module_.get(), &tag_sig); - module_->tags.emplace_back(tag_sig); + uint32_t sig_index = consume_tag_sig_index(module_.get(), &tag_sig); + module_->tags.emplace_back(tag_sig, sig_index); break; } default: @@ -854,30 +823,37 @@ class ModuleDecoderTemplate : public Decoder { break; } } - tracer_.ImportsDone(); + UpdateMemorySizes(); + module_->type_feedback.well_known_imports.Initialize( + module_->num_imported_functions); + if (tracer_) tracer_->ImportsDone(); } void DecodeFunctionSection() { uint32_t functions_count = - consume_count("functions count", kV8MaxWasmFunctions); - if (counters_ != nullptr) { - auto counter = SELECT_WASM_COUNTER(GetCounters(), origin_, - wasm_functions_per, module); - counter->AddSample(static_cast<int>(functions_count)); - } + consume_count("functions count", v8_flags.max_wasm_functions); DCHECK_EQ(module_->functions.size(), module_->num_imported_functions); uint32_t total_function_count = module_->num_imported_functions + functions_count; module_->functions.resize(total_function_count); module_->num_declared_functions = functions_count; + // Also initialize the {validated_functions} bitset here, now that we know + // the number of declared functions. DCHECK_NULL(module_->validated_functions); module_->validated_functions = std::make_unique<std::atomic<uint8_t>[]>((functions_count + 7) / 8); + if (is_asmjs_module(module_.get())) { + // Mark all asm.js functions as valid by design (it's faster to do this + // here than to check this in {WasmModule::function_was_validated}). + std::fill_n(module_->validated_functions.get(), (functions_count + 7) / 8, + 0xff); + } + for (uint32_t func_index = module_->num_imported_functions; func_index < total_function_count; ++func_index) { WasmFunction* function = &module_->functions[func_index]; function->func_index = func_index; - tracer_.FunctionName(func_index); + if (tracer_) tracer_->FunctionName(func_index); function->sig_index = consume_sig_index(module_.get(), &function->sig); if (!ok()) return; } @@ -887,7 +863,7 @@ class ModuleDecoderTemplate : public Decoder { uint32_t table_count = consume_count("table count", kV8MaxWasmTables); for (uint32_t i = 0; ok() && i < table_count; i++) { - tracer_.TableOffset(pc_offset()); + if (tracer_) tracer_->TableOffset(pc_offset()); module_->tables.emplace_back(); WasmTable* table = &module_->tables.back(); const byte* type_position = pc(); @@ -930,7 +906,7 @@ class ModuleDecoderTemplate : public Decoder { uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories); for (uint32_t i = 0; ok() && i < memory_count; i++) { - tracer_.MemoryOffset(pc_offset()); + if (tracer_) tracer_->MemoryOffset(pc_offset()); if (!AddMemory(module_.get())) break; consume_memory_flags(&module_->has_shared_memory, &module_->is_memory64, &module_->has_maximum_pages); @@ -941,6 +917,20 @@ class ModuleDecoderTemplate : public Decoder { module_->has_maximum_pages, max_pages, &module_->maximum_pages, module_->is_memory64 ? k64BitLimits : k32BitLimits); } + UpdateMemorySizes(); + } + + void UpdateMemorySizes() { + // Set min and max memory size. + const uintptr_t platform_max_pages = module_->is_memory64 + ? kV8MaxWasmMemory64Pages + : kV8MaxWasmMemory32Pages; + module_->min_memory_size = + std::min(platform_max_pages, uintptr_t{module_->initial_pages}) * + kWasmPageSize; + module_->max_memory_size = + std::min(platform_max_pages, uintptr_t{module_->maximum_pages}) * + kWasmPageSize; } void DecodeGlobalSection() { @@ -951,7 +941,7 @@ class ModuleDecoderTemplate : public Decoder { module_->globals.reserve(imported_globals + globals_count); for (uint32_t i = 0; ok() && i < globals_count; ++i) { TRACE("DecodeGlobal[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); - tracer_.GlobalOffset(pc_offset()); + if (tracer_) tracer_->GlobalOffset(pc_offset()); ValueType type = consume_value_type(); bool mutability = consume_mutability(); if (failed()) break; @@ -967,9 +957,11 @@ class ModuleDecoderTemplate : public Decoder { for (uint32_t i = 0; ok() && i < export_table_count; ++i) { TRACE("DecodeExportTable[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); - tracer_.Description("export #"); - tracer_.Description(i); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description("export #"); + tracer_->Description(i); + tracer_->NextLine(); + } module_->export_table.push_back({ {0, 0}, // name @@ -983,8 +975,10 @@ class ModuleDecoderTemplate : public Decoder { const byte* pos = pc(); exp->kind = static_cast<ImportExportKindCode>(consume_u8("kind: ", tracer_)); - tracer_.Description(ExternalKindName(exp->kind)); - tracer_.Description(" "); + if (tracer_) { + tracer_->Description(ExternalKindName(exp->kind)); + tracer_->Description(" "); + } switch (exp->kind) { case kExternalFunction: { WasmFunction* func = nullptr; @@ -1032,10 +1026,11 @@ class ModuleDecoderTemplate : public Decoder { errorf(pos, "invalid export kind 0x%02x", exp->kind); break; } - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); } // Check for duplicate exports (except for asm.js). - if (ok() && origin_ == kWasmOrigin && module_->export_table.size() > 1) { + if (ok() && module_->origin == kWasmOrigin && + module_->export_table.size() > 1) { std::vector<WasmExport> sorted_exports(module_->export_table); auto cmp_less = [this](const WasmExport& a, const WasmExport& b) { @@ -1066,11 +1061,11 @@ class ModuleDecoderTemplate : public Decoder { } void DecodeStartSection() { - tracer_.StartOffset(pc_offset()); + if (tracer_) tracer_->StartOffset(pc_offset()); WasmFunction* func; const byte* pos = pc_; module_->start_function_index = consume_func_index(module_.get(), &func); - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); if (func && (func->sig->parameter_count() > 0 || func->sig->return_count() > 0)) { error(pos, "invalid start function: non-zero parameter or return count"); @@ -1082,23 +1077,17 @@ class ModuleDecoderTemplate : public Decoder { consume_count("segment count", v8_flags.wasm_max_table_size); for (uint32_t i = 0; i < segment_count; ++i) { - tracer_.ElementOffset(pc_offset()); + if (tracer_) tracer_->ElementOffset(pc_offset()); WasmElemSegment segment = consume_element_segment_header(); - tracer_.NextLineIfNonEmpty(); + if (tracer_) tracer_->NextLineIfNonEmpty(); if (failed()) return; DCHECK_NE(segment.type, kWasmBottom); - uint32_t num_elem = - consume_count("number of elements", max_table_init_entries()); - - for (uint32_t j = 0; j < num_elem; j++) { - ConstantExpression entry = - segment.element_type == WasmElemSegment::kExpressionElements - ? consume_init_expr(module_.get(), segment.type) - : ConstantExpression::RefFunc( - consume_element_func_index(segment.type)); + for (uint32_t j = 0; j < segment.element_count; j++) { + // Just run validation on elements; do not store them anywhere. We will + // decode them again from wire bytes as needed. + consume_element_segment_entry(module_.get(), segment); if (failed()) return; - segment.entries.push_back(entry); } module_->elem_segments.push_back(std::move(segment)); } @@ -1110,8 +1099,10 @@ class ModuleDecoderTemplate : public Decoder { CalculateGlobalOffsets(module_.get()); uint32_t code_section_start = pc_offset(); uint32_t functions_count = consume_u32v("functions count", tracer_); - tracer_.Description(functions_count); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(functions_count); + tracer_->NextLine(); + } CheckFunctionsCount(functions_count, code_section_start); auto inst_traces_it = this->inst_traces_.begin(); @@ -1119,13 +1110,17 @@ class ModuleDecoderTemplate : public Decoder { for (uint32_t i = 0; ok() && i < functions_count; ++i) { int function_index = module_->num_imported_functions + i; - tracer_.Description("function #"); - tracer_.FunctionName(function_index); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description("function #"); + tracer_->FunctionName(function_index); + tracer_->NextLine(); + } const byte* pos = pc(); uint32_t size = consume_u32v("body size", tracer_); - tracer_.Description(size); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(size); + tracer_->NextLine(); + } if (size > kV8MaxWasmFunctionSize) { errorf(pos, "size %u > maximum function size %zu", size, kV8MaxWasmFunctionSize); @@ -1181,7 +1176,9 @@ class ModuleDecoderTemplate : public Decoder { uint32_t offset) { WasmFunction* function = &module_->functions[func_index]; function->code = {offset, length}; - tracer_.FunctionBody(function, pc_ - (pc_offset() - offset)); + if (tracer_) { + tracer_->FunctionBody(function, pc_ - (pc_offset() - offset)); + } } bool CheckDataSegmentsCount(uint32_t data_segments_count) { @@ -1204,7 +1201,7 @@ class ModuleDecoderTemplate : public Decoder { const byte* pos = pc(); TRACE("DecodeDataSegment[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); - tracer_.DataOffset(pc_offset()); + if (tracer_) tracer_->DataOffset(pc_offset()); bool is_active; uint32_t memory_index; @@ -1224,8 +1221,10 @@ class ModuleDecoderTemplate : public Decoder { } uint32_t source_length = consume_u32v("source size", tracer_); - tracer_.Description(source_length); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(source_length); + tracer_->NextLine(); + } uint32_t source_offset = pc_offset(); if (is_active) { @@ -1236,9 +1235,11 @@ class ModuleDecoderTemplate : public Decoder { WasmDataSegment* segment = &module_->data_segments.back(); - tracer_.Bytes(pc_, source_length); - tracer_.Description("segment data"); - tracer_.NextLine(); + if (tracer_) { + tracer_->Bytes(pc_, source_length); + tracer_->Description("segment data"); + tracer_->NextLine(); + } consume_bytes(source_length, "segment data"); if (failed()) break; @@ -1247,8 +1248,10 @@ class ModuleDecoderTemplate : public Decoder { } void DecodeNameSection() { - tracer_.NameSection(pc_, end_, - buffer_offset_ + static_cast<uint32_t>(pc_ - start_)); + if (tracer_) { + tracer_->NameSection( + pc_, end_, buffer_offset_ + static_cast<uint32_t>(pc_ - start_)); + } // TODO(titzer): find a way to report name errors as warnings. // Ignore all but the first occurrence of name section. if (!has_seen_unordered_section(kNameSectionCode)) { @@ -1268,11 +1271,10 @@ class ModuleDecoderTemplate : public Decoder { // Decode module name, ignore the rest. // Function and local names will be decoded when needed. - NoTracer tracing_already_done; if (name_type == NameSectionKindCode::kModuleCode) { WireBytesRef name = consume_string(&inner, unibrow::Utf8Variant::kLossyUtf8, - "module name", tracing_already_done); + "module name", ITracer::NoTrace); if (inner.ok() && validate_utf8(&inner, name)) { module_->name = name; } @@ -1550,18 +1552,18 @@ class ModuleDecoderTemplate : public Decoder { void DecodeDataCountSection() { module_->num_declared_data_segments = consume_count("data segments count", kV8MaxWasmDataSegments); - tracer_.NextLineIfNonEmpty(); + if (tracer_) tracer_->NextLineIfNonEmpty(); } void DecodeTagSection() { uint32_t tag_count = consume_count("tag count", kV8MaxWasmTags); for (uint32_t i = 0; ok() && i < tag_count; ++i) { TRACE("DecodeTag[%d] module+%d\n", i, static_cast<int>(pc_ - start_)); - tracer_.TagOffset(pc_offset()); + if (tracer_) tracer_->TagOffset(pc_offset()); const WasmTagSig* tag_sig = nullptr; consume_exception_attribute(); // Attribute ignored for now. - consume_tag_sig_index(module_.get(), &tag_sig); - module_->tags.emplace_back(tag_sig); + uint32_t sig_index = consume_tag_sig_index(module_.get(), &tag_sig); + module_->tags.emplace_back(tag_sig, sig_index); } } @@ -1620,26 +1622,25 @@ class ModuleDecoderTemplate : public Decoder { } void ValidateAllFunctions() { - DCHECK(ok()); - - // Spawn a {ValidateFunctionsTask} and join it. The earliest error found - // will be set on this decoder. - std::unique_ptr<JobHandle> job_handle = V8::GetCurrentPlatform()->CreateJob( - TaskPriority::kUserVisible, - std::make_unique<ValidateFunctionsTask>(this)); - job_handle->Join(); + DCHECK(!error_.has_error()); + // Pass nullptr for an "empty" filter function. + error_ = ValidateFunctions(module_.get(), enabled_features_, + base::VectorOf(start_, end_ - start_), nullptr); } // Decodes an entire module. - ModuleResult DecodeModule(Counters* counters, AccountingAllocator* allocator, - bool validate_functions) { - StartDecoding(counters, allocator); - uint32_t offset = 0; - base::Vector<const byte> orig_bytes(start(), end() - start()); - DecodeModuleHeader(base::VectorOf(start(), end() - start()), offset); - if (failed()) { - return FinishDecoding(); + ModuleResult DecodeModule(bool validate_functions) { + base::Vector<const byte> wire_bytes(start_, end_ - start_); + size_t max_size = max_module_size(); + if (wire_bytes.size() > max_size) { + return ModuleResult{WasmError{0, "size > maximum module size (%zu): %zu", + max_size, wire_bytes.size()}}; } + + uint32_t offset = 0; + DecodeModuleHeader(wire_bytes, offset); + if (failed()) return toResult(nullptr); + // Size of the module header. offset += 8; Decoder decoder(start_ + offset, end_, offset); @@ -1647,35 +1648,36 @@ class ModuleDecoderTemplate : public Decoder { WasmSectionIterator section_iter(&decoder, tracer_); while (ok()) { - // Shift the offset by the section header length + // Shift the offset by the section header length. offset += section_iter.payload_start() - section_iter.section_start(); if (section_iter.section_code() != SectionCode::kUnknownSectionCode) { DecodeSection(section_iter.section_code(), section_iter.payload(), offset); } - // Shift the offset by the remaining section payload + // Shift the offset by the remaining section payload. offset += section_iter.payload_length(); if (!section_iter.more() || !ok()) break; section_iter.advance(true); } if (ok() && validate_functions) { - Reset(orig_bytes); + Reset(wire_bytes); ValidateAllFunctions(); } - if (v8_flags.dump_wasm_module) DumpModule(orig_bytes); + if (v8_flags.dump_wasm_module) DumpModule(wire_bytes); if (decoder.failed()) { - return decoder.toResult<std::shared_ptr<WasmModule>>(nullptr); + return decoder.toResult(nullptr); } return FinishDecoding(); } // Decodes a single anonymous function starting at {start_}. - FunctionResult DecodeSingleFunctionForTesting( - Zone* zone, const ModuleWireBytes& wire_bytes, const WasmModule* module) { + FunctionResult DecodeSingleFunctionForTesting(Zone* zone, + ModuleWireBytes wire_bytes, + const WasmModule* module) { DCHECK(ok()); pc_ = start_; expect_u8("type form", kWasmFunctionTypeCode); @@ -1685,13 +1687,11 @@ class ModuleDecoderTemplate : public Decoder { if (!ok()) return FunctionResult{std::move(error_)}; - AccountingAllocator* allocator = zone->allocator(); - FunctionBody body{function.sig, off(pc_), pc_, end_}; WasmFeatures unused_detected_features; - DecodeResult result = ValidateFunctionBody( - allocator, enabled_features_, module, &unused_detected_features, body); + DecodeResult result = ValidateFunctionBody(enabled_features_, module, + &unused_detected_features, body); if (result.failed()) return FunctionResult{std::move(result).error()}; @@ -1710,48 +1710,21 @@ class ModuleDecoderTemplate : public Decoder { return consume_init_expr(module_.get(), expected); } - const std::shared_ptr<WasmModule>& shared_module() const { return module_; } - - Counters* GetCounters() const { - DCHECK_NOT_NULL(counters_); - return counters_; + // Takes a module as parameter so that wasm-disassembler.cc can pass its own + // module. + ConstantExpression consume_element_segment_entry( + WasmModule* module, const WasmElemSegment& segment) { + if (segment.element_type == WasmElemSegment::kExpressionElements) { + return consume_init_expr(module, segment.type); + } else { + return ConstantExpression::RefFunc( + consume_element_func_index(module, segment.type)); + } } - void SetCounters(Counters* counters) { - DCHECK_NULL(counters_); - counters_ = counters; - } + const std::shared_ptr<WasmModule>& shared_module() const { return module_; } private: - const WasmFeatures enabled_features_; - std::shared_ptr<WasmModule> module_; - const byte* module_start_ = nullptr; - const byte* module_end_ = nullptr; - Counters* counters_ = nullptr; - Tracer& tracer_; - // The type section is the first section in a module. - uint8_t next_ordered_section_ = kFirstSectionInModule; - // We store next_ordered_section_ as uint8_t instead of SectionCode so that - // we can increment it. This static_assert should make sure that SectionCode - // does not get bigger than uint8_t accidentally. - static_assert(sizeof(ModuleDecoderTemplate::next_ordered_section_) == - sizeof(SectionCode), - "type mismatch"); - uint32_t seen_unordered_sections_ = 0; - static_assert( - kBitsPerByte * sizeof(ModuleDecoderTemplate::seen_unordered_sections_) > - kLastKnownModuleSection, - "not enough bits"); - ModuleOrigin origin_; - AccountingAllocator allocator_; - Zone init_expr_zone_{&allocator_, "constant expr. zone"}; - - // Instruction traces are decoded in DecodeInstTraceSection as a 3-tuple - // of the function index, function offset, and mark_id. In DecodeCodeSection, - // after the functions have been decoded this is translated to pairs of module - // offsets and mark ids. - std::vector<std::tuple<uint32_t, uint32_t, uint32_t>> inst_traces_; - bool has_seen_unordered_section(SectionCode section_code) { return seen_unordered_sections_ & (1 << section_code); } @@ -1811,16 +1784,19 @@ class ModuleDecoderTemplate : public Decoder { uint32_t consume_sig_index(WasmModule* module, const FunctionSig** sig) { const byte* pos = pc_; uint32_t sig_index = consume_u32v("signature index"); - tracer_.Bytes(pos, static_cast<uint32_t>(pc_ - pos)); + if (tracer_) tracer_->Bytes(pos, static_cast<uint32_t>(pc_ - pos)); if (!module->has_signature(sig_index)) { - errorf(pos, "signature index %u out of bounds (%d signatures)", sig_index, - static_cast<int>(module->types.size())); + errorf(pos, "no signature at index %u (%d %s)", sig_index, + static_cast<int>(module->types.size()), + enabled_features_.has_gc() ? "types" : "signatures"); *sig = nullptr; return 0; } *sig = module->signature(sig_index); - tracer_.Description(*sig); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(*sig); + tracer_->NextLine(); + } return sig_index; } @@ -1838,11 +1814,13 @@ class ModuleDecoderTemplate : public Decoder { uint32_t consume_count(const char* name, size_t maximum) { const byte* p = pc_; uint32_t count = consume_u32v(name, tracer_); - tracer_.Description(count); - if (count == 1) { - tracer_.Description(": "); - } else { - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(count); + if (count == 1) { + tracer_->Description(": "); + } else { + tracer_->NextLine(); + } } if (count > maximum) { errorf(p, "%s of %u exceeds internal limit of %zu", name, count, maximum); @@ -1871,7 +1849,7 @@ class ModuleDecoderTemplate : public Decoder { uint32_t consume_index(const char* name, std::vector<T>* vector, T** ptr) { const byte* pos = pc_; uint32_t index = consume_u32v("index:", tracer_); - tracer_.Description(index); + if (tracer_) tracer_->Description(index); if (index >= vector->size()) { errorf(pos, "%s index %u out of bounds (%d entr%s)", name, index, static_cast<int>(vector->size()), @@ -1884,10 +1862,14 @@ class ModuleDecoderTemplate : public Decoder { } void consume_table_flags(const char* name, bool* has_maximum_out) { - tracer_.Bytes(pc_, 1); + if (tracer_) tracer_->Bytes(pc_, 1); uint8_t flags = consume_u8("table limits flags"); - tracer_.Description(flags == kNoMaximum ? " no maximum" : " with maximum"); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(flags == kNoMaximum ? " no maximum" + : " with maximum"); + tracer_->NextLine(); + } + static_assert(kNoMaximum == 0 && kWithMaximum == 1); *has_maximum_out = flags == kWithMaximum; if (V8_UNLIKELY(flags > kWithMaximum)) { @@ -1897,7 +1879,7 @@ class ModuleDecoderTemplate : public Decoder { void consume_memory_flags(bool* is_shared_out, bool* is_memory64_out, bool* has_maximum_out) { - tracer_.Bytes(pc_, 1); + if (tracer_) tracer_->Bytes(pc_, 1); uint8_t flags = consume_u8("memory limits flags"); // Flags 0..7 are valid (3 bits). if (flags & ~0x7) { @@ -1914,7 +1896,7 @@ class ModuleDecoderTemplate : public Decoder { // V8 does not support shared memory without a maximum. if (is_shared && !has_maximum) { - errorf(pc() - 1, "shared memory must have a maximum defined"); + error(pc() - 1, "shared memory must have a maximum defined"); } if (is_memory64 && !enabled_features_.has_memory64()) { @@ -1925,10 +1907,12 @@ class ModuleDecoderTemplate : public Decoder { } // Tracing. - if (is_shared) tracer_.Description(" shared"); - if (is_memory64) tracer_.Description(" mem64"); - tracer_.Description(has_maximum ? " with maximum" : " no maximum"); - tracer_.NextLine(); + if (tracer_) { + if (is_shared) tracer_->Description(" shared"); + if (is_memory64) tracer_->Description(" mem64"); + tracer_->Description(has_maximum ? " with maximum" : " no maximum"); + tracer_->NextLine(); + } } enum ResizableLimitsType : bool { k32BitLimits, k64BitLimits }; @@ -1949,8 +1933,10 @@ class ModuleDecoderTemplate : public Decoder { name, initial_64, units, max_initial, units); } *initial = static_cast<uint32_t>(initial_64); - tracer_.Description(*initial); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(*initial); + tracer_->NextLine(); + } if (has_maximum) { pos = pc(); uint64_t maximum_64 = type == k64BitLimits @@ -1968,8 +1954,10 @@ class ModuleDecoderTemplate : public Decoder { name, maximum_64, units, *initial, units); } *maximum = static_cast<uint32_t>(maximum_64); - tracer_.Description(*maximum); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(*maximum); + tracer_->NextLine(); + } } else { *maximum = max_initial; } @@ -1987,18 +1975,16 @@ class ModuleDecoderTemplate : public Decoder { } ConstantExpression consume_init_expr(WasmModule* module, ValueType expected) { - uint32_t length; - // The error message mimics the one generated by the {WasmFullDecoder}. #define TYPE_CHECK(found) \ - if (V8_UNLIKELY(!IsSubtypeOf(found, expected, module_.get()))) { \ + if (V8_UNLIKELY(!IsSubtypeOf(found, expected, module))) { \ errorf(pc() + 1, \ "type error in constant expression[0] (expected %s, got %s)", \ expected.name().c_str(), found.name().c_str()); \ return {}; \ } - tracer_.NextLineIfNonEmpty(); + if (tracer_) tracer_->NextLineIfNonEmpty(); // To avoid initializing a {WasmFullDecoder} for the most common // expressions, we replicate their decoding and validation here. The // manually handled cases correspond to {ConstantExpression}'s kinds. @@ -2011,48 +1997,55 @@ class ModuleDecoderTemplate : public Decoder { } switch (static_cast<WasmOpcode>(*pc())) { case kExprI32Const: { - int32_t value = - read_i32v<FullValidationTag>(pc() + 1, &length, "i32.const"); + auto [value, length] = + read_i32v<FullValidationTag>(pc() + 1, "i32.const"); if (V8_UNLIKELY(failed())) return {}; if (V8_LIKELY(lookahead(1 + length, kExprEnd))) { TYPE_CHECK(kWasmI32) - tracer_.InitializerExpression(pc_, pc_ + length + 2, kWasmI32); + if (tracer_) { + tracer_->InitializerExpression(pc_, pc_ + length + 2, kWasmI32); + } consume_bytes(length + 2); return ConstantExpression::I32Const(value); } break; } case kExprRefFunc: { - uint32_t index = - read_u32v<FullValidationTag>(pc() + 1, &length, "ref.func"); + auto [index, length] = + read_u32v<FullValidationTag>(pc() + 1, "ref.func"); if (V8_UNLIKELY(failed())) return {}; if (V8_LIKELY(lookahead(1 + length, kExprEnd))) { - if (V8_UNLIKELY(index >= module_->functions.size())) { + if (V8_UNLIKELY(index >= module->functions.size())) { errorf(pc() + 1, "function index %u out of bounds", index); return {}; } ValueType type = enabled_features_.has_typed_funcref() - ? ValueType::Ref(module_->functions[index].sig_index) + ? ValueType::Ref(module->functions[index].sig_index) : kWasmFuncRef; TYPE_CHECK(type) - module_->functions[index].declared = true; - tracer_.InitializerExpression(pc_, pc_ + length + 2, type); + module->functions[index].declared = true; + if (tracer_) { + tracer_->InitializerExpression(pc_, pc_ + length + 2, type); + } consume_bytes(length + 2); return ConstantExpression::RefFunc(index); } break; } case kExprRefNull: { - HeapType type = value_type_reader::read_heap_type<FullValidationTag>( - this, pc() + 1, &length, enabled_features_); - value_type_reader::ValidateHeapType<FullValidationTag>( - this, pc_, module_.get(), type); + auto [type, length] = + value_type_reader::read_heap_type<FullValidationTag>( + this, pc() + 1, enabled_features_); + value_type_reader::ValidateHeapType<FullValidationTag>(this, pc_, + module, type); if (V8_UNLIKELY(failed())) return {}; if (V8_LIKELY(lookahead(1 + length, kExprEnd))) { TYPE_CHECK(ValueType::RefNull(type)) - tracer_.InitializerExpression(pc_, pc_ + length + 2, - ValueType::RefNull(type)); + if (tracer_) { + tracer_->InitializerExpression(pc_, pc_ + length + 2, + ValueType::RefNull(type)); + } consume_bytes(length + 2); return ConstantExpression::RefNull(type.representation()); } @@ -2075,7 +2068,9 @@ class ModuleDecoderTemplate : public Decoder { decoder.DecodeFunctionBody(); - tracer_.InitializerExpression(pc_, decoder.end(), expected); + if (tracer_) { + tracer_->InitializerExpression(pc_, decoder.end(), expected); + } this->pc_ = decoder.end(); if (decoder.failed()) { @@ -2094,37 +2089,30 @@ class ModuleDecoderTemplate : public Decoder { // Read a mutability flag bool consume_mutability() { - tracer_.Bytes(pc_, 1); + if (tracer_) tracer_->Bytes(pc_, 1); byte val = consume_u8("mutability"); - tracer_.Description(val == 0 ? " immutable" - : val == 1 ? " mutable" - : " invalid"); + if (tracer_) { + tracer_->Description(val == 0 ? " immutable" + : val == 1 ? " mutable" + : " invalid"); + } if (val > 1) error(pc_ - 1, "invalid mutability"); return val != 0; } ValueType consume_value_type() { - uint32_t type_length; - ValueType result = value_type_reader::read_value_type<FullValidationTag>( - this, pc_, &type_length, - origin_ == kWasmOrigin ? enabled_features_ : WasmFeatures::None()); - value_type_reader::ValidateValueType<FullValidationTag>( - this, pc_, module_.get(), result); - tracer_.Bytes(pc_, type_length); - tracer_.Description(result); - consume_bytes(type_length, "value type"); - return result; - } - - HeapType consume_super_type() { - uint32_t type_length; - HeapType result = value_type_reader::read_heap_type<FullValidationTag>( - this, pc_, &type_length, enabled_features_); + auto [result, length] = + value_type_reader::read_value_type<FullValidationTag>( + this, pc_, + module_->origin == kWasmOrigin ? enabled_features_ + : WasmFeatures::None()); value_type_reader::ValidateValueType<FullValidationTag>( this, pc_, module_.get(), result); - tracer_.Bytes(pc_, type_length); - tracer_.Description(result); - consume_bytes(type_length, "heap type"); + if (tracer_) { + tracer_->Bytes(pc_, length); + tracer_->Description(result); + } + consume_bytes(length, "value type"); return result; } @@ -2144,7 +2132,7 @@ class ModuleDecoderTemplate : public Decoder { } const FunctionSig* consume_sig(Zone* zone) { - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); // Parse parameter types. uint32_t param_count = consume_count("param count", kV8MaxWasmFunctionParams); @@ -2152,9 +2140,9 @@ class ModuleDecoderTemplate : public Decoder { std::vector<ValueType> params; for (uint32_t i = 0; ok() && i < param_count; ++i) { params.push_back(consume_value_type()); - tracer_.NextLineIfFull(); + if (tracer_) tracer_->NextLineIfFull(); } - tracer_.NextLineIfNonEmpty(); + if (tracer_) tracer_->NextLineIfNonEmpty(); if (failed()) return nullptr; // Parse return types. @@ -2164,9 +2152,9 @@ class ModuleDecoderTemplate : public Decoder { if (failed()) return nullptr; for (uint32_t i = 0; ok() && i < return_count; ++i) { returns.push_back(consume_value_type()); - tracer_.NextLineIfFull(); + if (tracer_) tracer_->NextLineIfFull(); } - tracer_.NextLineIfNonEmpty(); + if (tracer_) tracer_->NextLineIfNonEmpty(); if (failed()) return nullptr; // FunctionSig stores the return types first. @@ -2187,17 +2175,20 @@ class ModuleDecoderTemplate : public Decoder { for (uint32_t i = 0; ok() && i < field_count; ++i) { fields[i] = consume_storage_type(); mutabilities[i] = consume_mutability(); - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); } if (failed()) return nullptr; uint32_t* offsets = zone->NewArray<uint32_t>(field_count); - return zone->New<StructType>(field_count, offsets, fields, mutabilities); + StructType* result = + zone->New<StructType>(field_count, offsets, fields, mutabilities); + result->InitializeOffsets(); + return result; } const ArrayType* consume_array(Zone* zone) { ValueType element_type = consume_storage_type(); bool mutability = consume_mutability(); - tracer_.NextLine(); + if (tracer_) tracer_->NextLine(); if (failed()) return nullptr; return zone->New<ArrayType>(element_type, mutability); } @@ -2206,7 +2197,7 @@ class ModuleDecoderTemplate : public Decoder { uint32_t consume_exception_attribute() { const byte* pos = pc_; uint32_t attribute = consume_u32v("exception attribute"); - tracer_.Bytes(pos, static_cast<uint32_t>(pc_ - pos)); + if (tracer_) tracer_->Bytes(pos, static_cast<uint32_t>(pc_ - pos)); if (attribute != kExceptionAttribute) { errorf(pos, "exception attribute %u not supported", attribute); return 0; @@ -2243,11 +2234,12 @@ class ModuleDecoderTemplate : public Decoder { : WasmElemSegment::kStatusPassive : WasmElemSegment::kStatusActive; const bool is_active = status == WasmElemSegment::kStatusActive; - // clang-format off - tracer_.Description(status == WasmElemSegment::kStatusActive ? "active" : - status == WasmElemSegment::kStatusPassive ? "passive," : - "declarative,"); - // clang-format on + if (tracer_) { + tracer_->Description(status == WasmElemSegment::kStatusActive ? "active" + : status == WasmElemSegment::kStatusPassive + ? "passive," + : "declarative,"); + } WasmElemSegment::ElementType element_type = flag & kExpressionsAsElementsMask @@ -2259,7 +2251,7 @@ class ModuleDecoderTemplate : public Decoder { uint32_t table_index = 0; if (has_table_index) { table_index = consume_u32v(", table index", tracer_); - tracer_.Description(table_index); + if (tracer_) tracer_->Description(table_index); } if (V8_UNLIKELY(is_active && table_index >= module_->tables.size())) { errorf(pos, "out of bounds%s table index %u", @@ -2271,8 +2263,10 @@ class ModuleDecoderTemplate : public Decoder { ConstantExpression offset; if (is_active) { - tracer_.Description(", offset:"); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description(", offset:"); + tracer_->NextLine(); + } offset = consume_init_expr(module_.get(), kWasmI32); // Failed to parse offset initializer, return early. if (failed()) return {}; @@ -2286,9 +2280,9 @@ class ModuleDecoderTemplate : public Decoder { if (backwards_compatible_mode) { type = kWasmFuncRef; } else { - tracer_.Description(" element type:"); + if (tracer_) tracer_->Description(" element type:"); type = consume_value_type(); - if (type == kWasmBottom) return {}; + if (failed()) return {}; } if (V8_UNLIKELY(is_active && !IsSubtypeOf(type, table_type, this->module_.get()))) { @@ -2334,10 +2328,14 @@ class ModuleDecoderTemplate : public Decoder { } } + uint32_t num_elem = + consume_count("number of elements", max_table_init_entries()); + if (is_active) { - return {type, table_index, std::move(offset), element_type}; + return {type, table_index, std::move(offset), + element_type, num_elem, pc_offset()}; } else { - return {type, status, element_type}; + return {type, status, element_type, num_elem, pc_offset()}; } } @@ -2345,12 +2343,14 @@ class ModuleDecoderTemplate : public Decoder { ConstantExpression* offset) { const byte* pos = pc(); uint32_t flag = consume_u32v("flag: ", tracer_); - tracer_.Description(flag == SegmentFlags::kActiveNoIndex ? "active no index" - : flag == SegmentFlags::kPassive ? "passive" - : flag == SegmentFlags::kActiveWithIndex - ? "active with index" - : "unknown"); - tracer_.NextLine(); + if (tracer_) { + tracer_->Description( + flag == SegmentFlags::kActiveNoIndex ? "active no index" + : flag == SegmentFlags::kPassive ? "passive" + : flag == SegmentFlags::kActiveWithIndex ? "active with index" + : "unknown"); + tracer_->NextLine(); + } // Some flag values are only valid for specific proposals. if (flag != SegmentFlags::kActiveNoIndex && @@ -2375,21 +2375,21 @@ class ModuleDecoderTemplate : public Decoder { if (flag == SegmentFlags::kActiveWithIndex) { *is_active = true; *index = consume_u32v("memory index", tracer_); - tracer_.Description(*index); + if (tracer_) tracer_->Description(*index); *offset = consume_init_expr(module_.get(), expected_type); } } - uint32_t consume_element_func_index(ValueType expected) { + uint32_t consume_element_func_index(WasmModule* module, ValueType expected) { WasmFunction* func = nullptr; const byte* initial_pc = pc(); - uint32_t index = consume_func_index(module_.get(), &func); - tracer_.NextLine(); + uint32_t index = consume_func_index(module, &func); + if (tracer_) tracer_->NextLine(); if (failed()) return index; DCHECK_NOT_NULL(func); DCHECK_EQ(index, func->func_index); ValueType entry_type = ValueType::Ref(func->sig_index); - if (V8_UNLIKELY(!IsSubtypeOf(entry_type, expected, module_.get()))) { + if (V8_UNLIKELY(!IsSubtypeOf(entry_type, expected, module))) { errorf(initial_pc, "Invalid type in element entry: expected %s, got %s instead.", expected.name().c_str(), entry_type.name().c_str()); @@ -2399,89 +2399,36 @@ class ModuleDecoderTemplate : public Decoder { return index; } - // A task that validates multiple functions in parallel, storing the earliest - // validation error in {this} decoder. - class ValidateFunctionsTask : public JobTask { - public: - ValidateFunctionsTask(ModuleDecoderTemplate* decoder) - : decoder_(decoder), - next_function_(decoder->module_->num_imported_functions), - after_last_function_(next_function_ + - decoder->module_->num_declared_functions) {} - - void Run(JobDelegate* delegate) override { - AccountingAllocator* allocator = decoder_->module_->allocator(); - do { - // Get the index of the next function to validate. - // {fetch_add} might overrun {after_last_function_} by a bit. Since the - // number of functions is limited to a value much smaller than the - // integer range, this is highly unlikely. - static_assert(kV8MaxWasmFunctions < kMaxInt / 2); - int func_index = next_function_.fetch_add(1, std::memory_order_relaxed); - if (V8_UNLIKELY(func_index >= after_last_function_)) return; - DCHECK_LE(0, func_index); - - if (!ValidateFunction(allocator, func_index)) { - // No need to validate any more functions. - next_function_.store(after_last_function_, std::memory_order_relaxed); - return; - } - } while (!delegate->ShouldYield()); - } - - size_t GetMaxConcurrency(size_t /* worker_count */) const override { - int next_func = next_function_.load(std::memory_order_relaxed); - return std::max(0, after_last_function_ - next_func); - } - - private: - // Validate a single function; use {SetError} on errors. - bool ValidateFunction(AccountingAllocator* allocator, int func_index) { - DCHECK(!decoder_->module_->function_was_validated(func_index)); - WasmFeatures unused_detected_features; - const WasmFunction& function = decoder_->module_->functions[func_index]; - FunctionBody body{function.sig, function.code.offset(), - decoder_->start_ + function.code.offset(), - decoder_->start_ + function.code.end_offset()}; - DecodeResult validation_result = ValidateFunctionBody( - allocator, decoder_->enabled_features_, decoder_->module_.get(), - &unused_detected_features, body); - if (V8_UNLIKELY(validation_result.failed())) { - SetError(func_index, std::move(validation_result).error()); - return false; - } - decoder_->module_->set_function_validated(func_index); - return true; - } - - // Set the error from the argument if it's earlier than the error we already - // have (or if we have none yet). Thread-safe. - void SetError(int func_index, WasmError error) { - base::MutexGuard mutex_guard{&set_error_mutex_}; - if (decoder_->error_.empty() || - decoder_->error_.offset() > error.offset()) { - // Wrap the error message from the function decoder. - const WasmFunction& function = decoder_->module_->functions[func_index]; - WasmFunctionName func_name{ - &function, - ModuleWireBytes{decoder_->start_, decoder_->end_}.GetNameOrNull( - &function, decoder_->module_.get())}; - std::ostringstream error_msg; - error_msg << "in function " << func_name << ": " << error.message(); - decoder_->error_ = WasmError{error.offset(), error_msg.str()}; - } - DCHECK(!decoder_->ok()); - } + const WasmFeatures enabled_features_; + const std::shared_ptr<WasmModule> module_; + const byte* module_start_ = nullptr; + const byte* module_end_ = nullptr; + ITracer* tracer_; + // The type section is the first section in a module. + uint8_t next_ordered_section_ = kFirstSectionInModule; + // We store next_ordered_section_ as uint8_t instead of SectionCode so that + // we can increment it. This static_assert should make sure that SectionCode + // does not get bigger than uint8_t accidentally. + static_assert(sizeof(ModuleDecoderImpl::next_ordered_section_) == + sizeof(SectionCode), + "type mismatch"); + uint32_t seen_unordered_sections_ = 0; + static_assert(kBitsPerByte * + sizeof(ModuleDecoderImpl::seen_unordered_sections_) > + kLastKnownModuleSection, + "not enough bits"); + AccountingAllocator allocator_; + Zone init_expr_zone_{&allocator_, "constant expr. zone"}; - ModuleDecoderTemplate* decoder_; - base::Mutex set_error_mutex_; - std::atomic<int> next_function_; - const int after_last_function_; - }; + // Instruction traces are decoded in DecodeInstTraceSection as a 3-tuple + // of the function index, function offset, and mark_id. In DecodeCodeSection, + // after the functions have been decoded this is translated to pairs of module + // offsets and mark ids. + std::vector<std::tuple<uint32_t, uint32_t, uint32_t>> inst_traces_; }; -} // namespace wasm -} // namespace internal -} // namespace v8 +} // namespace v8::internal::wasm + +#undef TRACE #endif // V8_WASM_MODULE_DECODER_IMPL_H_ |