v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
module-decoder-impl.h
Go to the documentation of this file.
1// Copyright 2022 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_WASM_MODULE_DECODER_IMPL_H_
6#define V8_WASM_MODULE_DECODER_IMPL_H_
7
8#if !V8_ENABLE_WEBASSEMBLY
9#error This header should only be included if WebAssembly is enabled.
10#endif // !V8_ENABLE_WEBASSEMBLY
11
14#include "src/strings/unicode.h"
15#include "src/utils/ostreams.h"
24
25namespace v8::internal::wasm {
26
27#define TRACE(...) \
28 do { \
29 if (v8_flags.trace_wasm_decoder) PrintF(__VA_ARGS__); \
30 } while (false)
31
32constexpr char kNameString[] = "name";
33constexpr char kSourceMappingURLString[] = "sourceMappingURL";
34constexpr char kInstTraceString[] = "metadata.code.trace_inst";
35constexpr char kCompilationHintsString[] = "compilationHints";
36constexpr char kBranchHintsString[] = "metadata.code.branch_hint";
37constexpr char kDebugInfoString[] = ".debug_info";
38constexpr char kExternalDebugInfoString[] = "external_debug_info";
39constexpr char kBuildIdString[] = "build_id";
40
42 switch (kind) {
44 return "function";
45 case kExternalTable:
46 return "table";
47 case kExternalMemory:
48 return "memory";
49 case kExternalGlobal:
50 return "global";
51 case kExternalTag:
52 return "tag";
53 }
54 return "unknown";
55}
56
57inline bool validate_utf8(Decoder* decoder, WireBytesRef string) {
59 decoder->start() + decoder->GetBufferRelativeOffset(string.offset()),
60 string.length());
61}
62
63// Reads a length-prefixed string, checking that it is within bounds. Returns
64// the offset of the string, and the length as an out parameter.
67 const char* name, ITracer* tracer) {
68 if (tracer) {
69 tracer->Description(name);
70 tracer->Description(" ");
71 }
72 uint32_t length = decoder->consume_u32v("length", tracer);
73 if (tracer) {
74 tracer->Description(": ");
75 tracer->Description(length);
76 tracer->NextLine();
77 }
78 uint32_t offset = decoder->pc_offset();
79 const uint8_t* string_start = decoder->pc();
80 // Consume bytes before validation to guarantee that the string is not oob.
81 if (length > 0) {
82 if (tracer) {
83 tracer->Bytes(decoder->pc(), length);
84 tracer->Description(name);
85 tracer->Description(": ");
86 tracer->Description(reinterpret_cast<const char*>(decoder->pc()), length);
87 tracer->NextLine();
88 }
89 decoder->consume_bytes(length, name);
90 if (decoder->ok()) {
91 switch (grammar) {
93 break;
94 case unibrow::Utf8Variant::kUtf8:
95 if (!unibrow::Utf8::ValidateEncoding(string_start, length)) {
96 decoder->errorf(string_start, "%s: no valid UTF-8 string", name);
97 }
98 break;
99 case unibrow::Utf8Variant::kWtf8:
100 if (!unibrow::Wtf8::ValidateEncoding(string_start, length)) {
101 decoder->errorf(string_start, "%s: no valid WTF-8 string", name);
102 }
103 break;
104 case unibrow::Utf8Variant::kUtf8NoTrap:
105 UNREACHABLE();
106 }
107 }
108 }
109 return {offset, decoder->failed() ? 0 : length};
110}
111
113 unibrow::Utf8Variant grammar,
114 const char* name) {
115 return consume_string(decoder, grammar, name, ITracer::NoTrace);
116}
117
118inline WireBytesRef consume_utf8_string(Decoder* decoder, const char* name,
119 ITracer* tracer) {
120 return consume_string(decoder, unibrow::Utf8Variant::kUtf8, name, tracer);
121}
122
124 ITracer* tracer) {
125 WireBytesRef string = consume_utf8_string(decoder, "section name", tracer);
126 if (decoder->failed()) {
127 return kUnknownSectionCode;
128 }
129 const uint8_t* section_name_start =
130 decoder->start() + decoder->GetBufferRelativeOffset(string.offset());
131
132 TRACE(" +%d section name : \"%.*s\"\n",
133 static_cast<int>(section_name_start - decoder->start()),
134 string.length() < 20 ? string.length() : 20, section_name_start);
135
136 using SpecialSectionPair = std::pair<base::Vector<const char>, SectionCode>;
137 static constexpr SpecialSectionPair kSpecialSections[]{
149
150 auto name_vec = base::Vector<const char>::cast(
151 base::VectorOf(section_name_start, string.length()));
152 for (auto& special_section : kSpecialSections) {
153 if (name_vec == special_section.first) return special_section.second;
154 }
155
156 return kUnknownSectionCode;
157}
158
159// An iterator over the sections in a wasm binary module.
160// Automatically skips all unknown sections.
162 public:
163 explicit WasmSectionIterator(Decoder* decoder, ITracer* tracer)
164 : decoder_(decoder),
165 tracer_(tracer),
167 section_start_(decoder->pc()),
168 section_end_(decoder->pc()) {
169 next();
170 }
171
172 bool more() const { return decoder_->ok() && decoder_->more(); }
173
175
176 const uint8_t* section_start() const { return section_start_; }
177
178 uint32_t section_length() const {
179 return static_cast<uint32_t>(section_end_ - section_start_);
180 }
181
185
186 const uint8_t* payload_start() const { return payload_start_; }
187
188 uint32_t payload_length() const {
189 return static_cast<uint32_t>(section_end_ - payload_start_);
190 }
191
192 const uint8_t* section_end() const { return section_end_; }
193
194 // Advances to the next section, checking that decoding the current section
195 // stopped at {section_end_}.
196 void advance(bool move_to_section_end = false) {
197 if (move_to_section_end && decoder_->pc() < section_end_) {
199 static_cast<uint32_t>(section_end_ - decoder_->pc()));
200 }
201 if (decoder_->pc() != section_end_) {
202 const char* msg = decoder_->pc() < section_end_ ? "shorter" : "longer";
204 "section was %s than expected size "
205 "(%u bytes expected, %zu decoded)",
206 msg, section_length(),
207 static_cast<size_t>(decoder_->pc() - section_start_));
208 }
209 next();
210 }
211
212 private:
216 const uint8_t* section_start_;
217 const uint8_t* payload_start_;
218 const uint8_t* section_end_;
219
220 // Reads the section code/name at the current position and sets up
221 // the embedder fields.
222 void next() {
223 if (!decoder_->more()) {
225 return;
226 }
228 // Empty line before next section.
229 if (tracer_) tracer_->NextLine();
230 uint8_t section_code = decoder_->consume_u8("section kind", tracer_);
231 if (tracer_) {
232 tracer_->Description(": ");
234 tracer_->NextLine();
235 }
236 // Read and check the section size.
237 uint32_t section_length = decoder_->consume_u32v("section length", tracer_);
238 if (tracer_) {
240 tracer_->NextLine();
241 }
247 "section (code %u, \"%s\") extends past end of the module "
248 "(length %u, remaining bytes %u)",
252 }
253
255 // Check for the known "name", "sourceMappingURL", or "compilationHints"
256 // section.
257 // To identify the unknown section we set the end of the decoder bytes to
258 // the end of the custom section, so that we do not read the section name
259 // beyond the end of the section.
260 const uint8_t* module_end = decoder_->end();
263 if (decoder_->ok()) decoder_->set_end(module_end);
264 // As a side effect, the above function will forward the decoder to after
265 // the identifier string.
267 } else if (!IsValidSectionCode(section_code)) {
268 decoder_->errorf(decoder_->pc(), "unknown section code #0x%02x",
270 }
272 : static_cast<SectionCode>(section_code);
273
275 // Skip to the end of the unknown section.
276 uint32_t remaining = static_cast<uint32_t>(section_end_ - decoder_->pc());
277 decoder_->consume_bytes(remaining, "section payload", tracer_);
278 }
279 }
280};
281
282inline void DumpModule(const base::Vector<const uint8_t> module_bytes,
283 bool ok) {
284 std::string path;
285 if (v8_flags.dump_wasm_module_path) {
286 path = v8_flags.dump_wasm_module_path;
287 if (path.size() && !base::OS::isDirectorySeparator(path[path.size() - 1])) {
289 }
290 }
291 // File are named `<hash>.{ok,failed}.wasm`.
292 // Limit the hash to 8 characters (32 bits).
293 uint32_t hash = static_cast<uint32_t>(GetWireBytesHash(module_bytes));
295 SNPrintF(buf, "%08x.%s.wasm", hash, ok ? "ok" : "failed");
296 path += buf.begin();
297 size_t rv = 0;
298 if (FILE* file = base::OS::FOpen(path.c_str(), "wb")) {
299 rv = fwrite(module_bytes.begin(), module_bytes.length(), 1, file);
300 base::Fclose(file);
301 }
302 if (rv != 1) {
303 OFStream os(stderr);
304 os << "Error while dumping wasm file to " << path << std::endl;
305 }
306}
307
308// The main logic for decoding the bytes of a module.
310 public:
312 base::Vector<const uint8_t> wire_bytes, ModuleOrigin origin,
313 WasmDetectedFeatures* detected_features,
314 ITracer* tracer = ITracer::NoTrace)
315 : Decoder(wire_bytes),
316 enabled_features_(enabled_features),
317 detected_features_(detected_features),
318 module_(std::make_shared<WasmModule>(origin)),
319 module_start_(wire_bytes.begin()),
320 module_end_(wire_bytes.end()),
321 tracer_(tracer) {}
322
323 void onFirstError() override {
324 pc_ = end_; // On error, terminate section decoding loop.
325 }
326
328 if (failed()) return;
329 Reset(bytes);
330
331 const uint8_t* pos = pc_;
332 uint32_t magic_word = consume_u32("wasm magic", tracer_);
333 if (tracer_) tracer_->NextLine();
334#define BYTES(x) (x & 0xFF), (x >> 8) & 0xFF, (x >> 16) & 0xFF, (x >> 24) & 0xFF
335 if (magic_word != kWasmMagic) {
336 errorf(pos,
337 "expected magic word %02x %02x %02x %02x, "
338 "found %02x %02x %02x %02x",
339 BYTES(kWasmMagic), BYTES(magic_word));
340 }
341
342 pos = pc_;
343 {
344 uint32_t magic_version = consume_u32("wasm version", tracer_);
345 if (tracer_) tracer_->NextLine();
346 if (magic_version != kWasmVersion) {
347 errorf(pos,
348 "expected version %02x %02x %02x %02x, "
349 "found %02x %02x %02x %02x",
350 BYTES(kWasmVersion), BYTES(magic_version));
351 }
352 }
353#undef BYTES
354 }
355
356 bool CheckSectionOrder(SectionCode section_code) {
357 // Check the order of ordered sections.
358 if (section_code >= kFirstSectionInModule &&
359 section_code < kFirstUnorderedSection) {
360 if (section_code < next_ordered_section_) {
361 errorf(pc(), "unexpected section <%s>", SectionName(section_code));
362 return false;
363 }
364 next_ordered_section_ = section_code + 1;
365 return true;
366 }
367
368 // Ignore ordering problems in unknown / custom sections. Even allow them to
369 // appear multiple times. As optional sections we use them on a "best
370 // effort" basis.
371 if (section_code == kUnknownSectionCode) return true;
372 if (section_code > kLastKnownModuleSection) return true;
373
374 // The rest is standardized unordered sections; they are checked more
375 // thoroughly..
376 DCHECK_LE(kFirstUnorderedSection, section_code);
377 DCHECK_GE(kLastKnownModuleSection, section_code);
378
379 // Check that unordered sections don't appear multiple times.
380 if (has_seen_unordered_section(section_code)) {
381 errorf(pc(), "Multiple %s sections not allowed",
382 SectionName(section_code));
383 return false;
384 }
385 set_seen_unordered_section(section_code);
386
387 // Define a helper to ensure that sections <= {before} appear before the
388 // current unordered section, and everything >= {after} appears after it.
389 auto check_order = [this, section_code](SectionCode before,
390 SectionCode after) -> bool {
391 DCHECK_LT(before, after);
392 if (next_ordered_section_ > after) {
393 errorf(pc(), "The %s section must appear before the %s section",
394 SectionName(section_code), SectionName(after));
395 return false;
396 }
397 if (next_ordered_section_ <= before) next_ordered_section_ = before + 1;
398 return true;
399 };
400
401 // Now check the ordering constraints of specific unordered sections.
402 switch (section_code) {
404 return check_order(kElementSectionCode, kCodeSectionCode);
405 case kTagSectionCode:
406 return check_order(kMemorySectionCode, kGlobalSectionCode);
408 // TODO(12868): If there's a tag section, assert that we're after the
409 // tag section.
410 return check_order(kMemorySectionCode, kGlobalSectionCode);
412 // Custom section following code.metadata tool convention containing
413 // offsets specifying where trace marks should be emitted.
414 // Be lenient with placement of instruction trace section. All except
415 // first occurrence after function section and before code section are
416 // ignored.
417 return true;
418 default:
419 return true;
420 }
421 }
422
423 void DecodeSection(SectionCode section_code,
424 base::Vector<const uint8_t> bytes, uint32_t offset) {
425 if (failed()) return;
426 Reset(bytes, offset);
427 TRACE("Section: %s\n", SectionName(section_code));
428 TRACE("Decode Section %p - %p\n", bytes.begin(), bytes.end());
429
430 if (!CheckSectionOrder(section_code)) return;
431
432 switch (section_code) {
434 break;
435 case kTypeSectionCode:
437 break;
440 break;
443 break;
446 break;
449 break;
452 break;
455 break;
458 break;
459 case kCodeSectionCode:
461 break;
464 break;
465 case kDataSectionCode:
467 break;
468 case kNameSectionCode:
470 break;
473 break;
477 consume_bytes(static_cast<uint32_t>(end_ - start_), ".debug_info");
478 break;
481 break;
484 break;
486 if (enabled_features_.has_instruction_tracing()) {
488 } else {
489 // Ignore this section when feature is disabled. It is an optional
490 // custom section anyways.
491 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
492 }
493 break;
495 // TODO(jkummerow): We're missing tracing support for well-known
496 // custom sections. This confuses `wami --full-hexdump` e.g.
497 // for the modules created by
498 // mjsunit/wasm/compilation-hints-streaming-compilation.js.
499 if (enabled_features_.has_compilation_hints()) {
501 } else {
502 // Ignore this section when feature was disabled. It is an optional
503 // custom section anyways.
504 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
505 }
506 break;
508 if (enabled_features_.has_branch_hinting()) {
510 } else {
511 // Ignore this section when feature was disabled. It is an optional
512 // custom section anyways.
513 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
514 }
515 break;
518 break;
519 case kTagSectionCode:
521 break;
523 if (enabled_features_.has_stringref()) {
525 } else {
526 errorf(pc(),
527 "unexpected section <%s> (enable with "
528 "--experimental-wasm-stringref)",
529 SectionName(section_code));
530 }
531 break;
532 default:
533 errorf(pc(), "unexpected section <%s>", SectionName(section_code));
534 return;
535 }
536
537 if (pc() != bytes.end()) {
538 const char* msg = pc() < bytes.end() ? "shorter" : "longer";
539 errorf(pc(),
540 "section was %s than expected size "
541 "(%zu bytes expected, %zu decoded)",
542 msg, bytes.size(), static_cast<size_t>(pc() - bytes.begin()));
543 }
544 }
545
546 static constexpr const char* TypeKindName(uint8_t kind) {
547 switch (kind) {
548 // clang-format off
549 case kWasmFunctionTypeCode: return "func";
550 case kWasmStructTypeCode: return "struct";
551 case kWasmArrayTypeCode: return "array";
552 case kWasmContTypeCode: return "cont";
553 default: return "unknown";
554 // clang-format on
555 }
556 }
557
559 const bool is_final = true;
560 const bool shared = false;
561 uint8_t kind = consume_u8(" kind", tracer_);
562 if (tracer_) {
563 tracer_->Description(": ");
565 }
566 switch (kind) {
568 const FunctionSig* sig = consume_sig(&module_->signature_zone);
569 if (sig == nullptr) {
570 CHECK(!ok());
571 return {};
572 }
573 return {sig, kNoSuperType, is_final, shared};
574 }
575 case kWasmStructTypeCode: {
576 module_->is_wasm_gc = true;
577 const StructType* type =
578 consume_struct(&module_->signature_zone, is_descriptor);
579 if (type == nullptr) {
580 CHECK(!ok());
581 return {};
582 }
583 return {type, kNoSuperType, is_final, shared};
584 }
585 case kWasmArrayTypeCode: {
586 module_->is_wasm_gc = true;
587 const ArrayType* type = consume_array(&module_->signature_zone);
588 if (type == nullptr) {
589 CHECK(!ok());
590 return {};
591 }
592 return {type, kNoSuperType, is_final, shared};
593 }
594 case kWasmContTypeCode: {
595 if (!enabled_features_.has_wasmfx()) {
596 error(pc() - 1,
597 "core stack switching not enabled (enable with "
598 "--experimental-wasm-wasmfx)");
599 return {};
600 }
601
602 const uint8_t* pos = pc();
604
605 if (!hp.is_index()) {
606 error(pos, "cont type must refer to a signature index");
607 return {};
608 }
609
610 ContType* type = module_->signature_zone.New<ContType>(hp.ref_index());
611 return {type, kNoSuperType, is_final, shared};
612 }
613 default:
614 if (tracer_) tracer_->NextLine();
615 errorf(pc() - 1, "unknown type form: %d", kind);
616 return {};
617 }
618 }
619
621 uint8_t kind = read_u8<Decoder::FullValidationTag>(pc(), "type kind");
622 if (kind == kWasmDescriptorCode) {
623 if (!enabled_features_.has_custom_descriptors()) {
624 error(pc(),
625 "descriptor types need --experimental-wasm-custom-descriptors");
626 return {};
627 }
628 detected_features_->add_custom_descriptors();
629 consume_bytes(1, " described_by", tracer_);
630 const uint8_t* pos = pc();
631 uint32_t descriptor = consume_u32v("descriptor", tracer_);
632 if (descriptor >= module_->types.size()) {
633 errorf(pos, "descriptor type index %u is out of bounds", descriptor);
634 return {};
635 }
636 if (tracer_) tracer_->NextLine();
637 TypeDefinition type = consume_base_type_definition(is_descriptor);
638 if (type.kind != TypeDefinition::kStruct) {
639 error(pos - 1, "'descriptor' may only be used with structs");
640 return {};
641 }
642 type.descriptor = ModuleTypeIndex{descriptor};
643 return type;
644 } else {
645 return consume_base_type_definition(is_descriptor);
646 }
647 }
648
649 TypeDefinition consume_describing_type(size_t current_type_index) {
650 uint8_t kind = read_u8<Decoder::FullValidationTag>(pc(), "type kind");
651 if (kind == kWasmDescribesCode) {
652 if (!enabled_features_.has_custom_descriptors()) {
653 error(pc(),
654 "descriptor types need --experimental-wasm-custom-descriptors");
655 return {};
656 }
657 detected_features_->add_custom_descriptors();
658 consume_bytes(1, " describes", tracer_);
659 const uint8_t* pos = pc();
660 uint32_t describes = consume_u32v("describes", tracer_);
661 if (describes >= current_type_index) {
662 error(pos, "types can only describe previously-declared types");
663 return {};
664 }
665 if (tracer_) tracer_->NextLine();
667 if (type.kind != TypeDefinition::kStruct) {
668 error(pos - 1, "'describes' may only be used with structs");
669 return {};
670 }
671 type.describes = ModuleTypeIndex{describes};
672 return type;
673 } else {
674 return consume_described_type(false);
675 }
676 }
677
678 TypeDefinition consume_shared_type(size_t current_type_index) {
679 uint8_t kind = read_u8<Decoder::FullValidationTag>(pc(), "type kind");
680 if (kind == kSharedFlagCode) {
681 if (!v8_flags.experimental_wasm_shared) {
682 errorf(pc() - 1,
683 "unknown type form: %d, enable with --experimental-wasm-shared",
684 kind);
685 return {};
686 }
687 module_->has_shared_part = true;
688 consume_bytes(1, " shared", tracer_);
689 TypeDefinition type = consume_describing_type(current_type_index);
690 type.is_shared = true;
691 return type;
692 } else {
693 return consume_describing_type(current_type_index);
694 }
695 }
696
697 // {current_type_index} is the index of the type that's being decoded.
698 // Any supertype must have a lower index.
699 TypeDefinition consume_subtype_definition(size_t current_type_index) {
700 uint8_t kind = read_u8<Decoder::FullValidationTag>(pc(), "type kind");
702 module_->is_wasm_gc = true;
703 bool is_final = kind == kWasmSubtypeFinalCode;
704 consume_bytes(1, is_final ? " subtype final, " : " subtype extensible, ",
705 tracer_);
706 constexpr uint32_t kMaximumSupertypes = 1;
707 uint32_t supertype_count =
708 consume_count("supertype count", kMaximumSupertypes);
709 uint32_t supertype = kNoSuperType.index;
710 if (supertype_count == 1) {
711 supertype = consume_u32v("supertype", tracer_);
712 if (supertype >= current_type_index) {
713 errorf("type %u: invalid supertype %u", current_type_index,
714 supertype);
715 return {};
716 }
717 if (tracer_) {
718 tracer_->Description(supertype);
719 tracer_->NextLine();
720 }
721 }
722 TypeDefinition type = consume_shared_type(current_type_index);
723 type.supertype = ModuleTypeIndex{supertype};
724 type.is_final = is_final;
725 return type;
726 } else {
727 return consume_shared_type(current_type_index);
728 }
729 }
730
733 uint32_t types_count = consume_count("types count", kV8MaxWasmTypes);
734
735 for (uint32_t i = 0; ok() && i < types_count; ++i) {
736 TRACE("DecodeType[%d] module+%d\n", i, static_cast<int>(pc_ - start_));
737 uint8_t kind = read_u8<Decoder::FullValidationTag>(pc(), "type kind");
738 size_t initial_size = module_->types.size();
739 uint32_t group_size = 1;
741 module_->is_wasm_gc = true;
742 uint32_t rec_group_offset = pc_offset();
743 consume_bytes(1, "rec. group definition", tracer_);
744 if (tracer_) tracer_->NextLine();
745 group_size = consume_count("recursive group size", kV8MaxWasmTypes);
746 if (tracer_) tracer_->RecGroupOffset(rec_group_offset, group_size);
747 }
748 if (initial_size + group_size > kV8MaxWasmTypes) {
749 errorf(pc(), "Type definition count exceeds maximum %zu",
751 return;
752 }
753 // We need to resize types before decoding the type definitions in this
754 // group, so that the correct type size is visible to type definitions.
755 module_->types.resize(initial_size + group_size);
756 for (uint32_t j = 0; j < group_size; j++) {
758 TypeDefinition type = consume_subtype_definition(initial_size + j);
759 if (failed()) return;
760 module_->types[initial_size + j] = type;
761 }
762 FinalizeRecgroup(group_size, type_canon);
764 tracer_->Description("end of rec. group");
765 tracer_->NextLine();
766 }
767 }
768 }
769
770 void FinalizeRecgroup(uint32_t group_size, TypeCanonicalizer* type_canon) {
771 // Now that we have decoded the entire recgroup, check its validity,
772 // initialize additional data, and canonicalize it:
773 // - check supertype validity
774 // - propagate subtyping depths
775 // - validate is_shared bits and set up RefTypeKind fields
776 WasmModule* module = module_.get();
777 uint32_t end_index = static_cast<uint32_t>(module->types.size());
778 uint32_t start_index = end_index - group_size;
779 for (uint32_t i = start_index; ok() && i < end_index; ++i) {
780 TypeDefinition& type_def = module_->types[i];
781 bool is_shared = type_def.is_shared;
782 switch (type_def.kind) {
784 base::Vector<const ValueType> all = type_def.function_sig->all();
785 size_t count = all.size();
786 ValueType* storage = const_cast<ValueType*>(all.begin());
787 for (uint32_t j = 0; j < count; j++) {
788 value_type_reader::Populate(&storage[j], module);
789 ValueType type = storage[j];
790 if (is_shared && !type.is_shared()) {
791 DCHECK(v8_flags.experimental_wasm_shared);
792 uint32_t retcount =
793 static_cast<uint32_t>(type_def.function_sig->return_count());
794 const char* param_or_return =
795 j < retcount ? "return" : "parameter";
796 uint32_t index = j < retcount ? j : j - retcount;
797 // {pc_} isn't very accurate, it's pointing at the end of the
798 // recgroup. So to ease debugging, we print the type's index.
799 errorf(pc_,
800 "Type %u: shared signature must have shared %s types, "
801 "actual type for %s %d is %s",
802 i, param_or_return, param_or_return, index,
803 type.name().c_str());
804 return;
805 }
806 }
807 break;
808 }
810 size_t count = type_def.struct_type->field_count();
811 ValueType* storage =
812 const_cast<ValueType*>(type_def.struct_type->fields().begin());
813 for (uint32_t j = 0; j < count; j++) {
814 value_type_reader::Populate(&storage[j], module);
815 ValueType type = storage[j];
816 if (is_shared && !type.is_shared()) {
817 errorf(pc_,
818 "Type %u: shared struct must have shared field types, "
819 "actual type for field %d is %s",
820 i, j, type.name().c_str());
821 return;
822 }
823 }
824 if (type_def.descriptor.valid()) {
825 const TypeDefinition& descriptor =
826 module->type(type_def.descriptor);
827 if (descriptor.describes.index != i) {
828 uint32_t d = type_def.descriptor.index;
829 errorf(pc_,
830 "Type %u has descriptor %u but %u doesn't describe %u", i,
831 d, d, i);
832 return;
833 }
834 if (descriptor.is_shared != type_def.is_shared) {
835 errorf(pc_,
836 "Type %u and its descriptor %u must have same sharedness",
837 i, type_def.descriptor.index);
838 return;
839 }
840 }
841 if (type_def.describes.valid()) {
842 if (module->type(type_def.describes).descriptor.index != i) {
843 uint32_t d = type_def.describes.index;
844 errorf(pc_,
845 "Type %u describes %u but %u isn't a descriptor for %u", i,
846 d, d, i);
847 return;
848 }
849 }
850 break;
851 }
854 const_cast<ArrayType*>(type_def.array_type)
856 module);
857 ValueType type = type_def.array_type->element_type();
858 if (is_shared && !type.is_shared()) {
859 errorf(pc_,
860 "Type %u: shared array must have shared element type, "
861 "actual element type is %s",
862 i, type.name().c_str());
863 return;
864 }
865 break;
866 }
868 ModuleTypeIndex contfun_typeid =
869 type_def.cont_type->contfun_typeindex();
870 const TypeDefinition contfun_type =
871 module_->types[contfun_typeid.index];
872 if (contfun_type.kind != TypeDefinition::kFunction) {
873 errorf(pc_,
874 "Type %u: cont type must refer to a signature index, "
875 "actual type is %s",
876 i, module_->heap_type(contfun_typeid).name().c_str());
877 return;
878 }
879 if (is_shared && !contfun_type.is_shared) {
880 errorf(pc_,
881 "Type %u: shared cont type must refer to a shared signature,"
882 " actual type is %s",
883 module_->heap_type(contfun_typeid).name().c_str());
884 return;
885 }
886 break;
887 }
888 }
889 }
890 module->isorecursive_canonical_type_ids.resize(end_index);
891 type_canon->AddRecursiveGroup(module, group_size);
892 for (uint32_t i = start_index; ok() && i < end_index; ++i) {
893 TypeDefinition& type_def = module_->types[i];
894 ModuleTypeIndex explicit_super = type_def.supertype;
895 if (!explicit_super.valid()) continue; // No supertype.
896 DCHECK_LT(explicit_super.index, i); // Checked during decoding.
897 uint32_t depth = module->type(explicit_super).subtyping_depth + 1;
898 type_def.subtyping_depth = depth;
899 DCHECK_GE(depth, 0);
900 if (depth > kV8MaxRttSubtypingDepth) {
901 errorf("type %u: subtyping depth is greater than allowed", i);
902 return;
903 }
904 // This check is technically redundant; we include for the improved error
905 // message.
906 if (module->type(explicit_super).is_final) {
907 errorf("type %u extends final type %u", i, explicit_super.index);
908 return;
909 }
910 if (!ValidSubtypeDefinition(ModuleTypeIndex{i}, explicit_super, module,
911 module)) {
912 errorf("type %u has invalid explicit supertype %u", i,
913 explicit_super.index);
914 return;
915 }
916 }
917 }
918
920 uint32_t import_table_count =
921 consume_count("imports count", kV8MaxWasmImports);
922 module_->import_table.reserve(import_table_count);
923 for (uint32_t i = 0; ok() && i < import_table_count; ++i) {
924 TRACE("DecodeImportTable[%d] module+%d\n", i,
925 static_cast<int>(pc_ - start_));
927
928 const uint8_t* pos = pc_;
929 WireBytesRef module_name =
930 consume_utf8_string(this, "module name", tracer_);
931 WireBytesRef field_name =
932 consume_utf8_string(this, "field name", tracer_);
934 static_cast<ImportExportKindCode>(consume_u8("kind", tracer_));
935 if (tracer_) {
936 tracer_->Description(": ");
938 }
939 module_->import_table.push_back(WasmImport{
940 .module_name = module_name, .field_name = field_name, .kind = kind});
941 WasmImport* import = &module_->import_table.back();
942 switch (kind) {
943 case kExternalFunction: {
944 // ===== Imported function ===========================================
945 import->index = static_cast<uint32_t>(module_->functions.size());
946 module_->num_imported_functions++;
947 module_->functions.push_back(WasmFunction{
948 .func_index = import->index,
949 .imported = true,
950 });
951 WasmFunction* function = &module_->functions.back();
952 function->sig_index =
953 consume_sig_index(module_.get(), &function->sig);
954 break;
955 }
956 case kExternalTable: {
957 // ===== Imported table ==============================================
958 import->index = static_cast<uint32_t>(module_->tables.size());
959 const uint8_t* type_position = pc();
961 if (!type.is_object_reference()) {
962 errorf(type_position, "Invalid table type %s", type.name().c_str());
963 break;
964 }
965 module_->num_imported_tables++;
966 module_->tables.push_back(WasmTable{
967 .type = type,
968 .imported = true,
969 });
970 WasmTable* table = &module_->tables.back();
971 consume_table_flags(table);
972 DCHECK_IMPLIES(table->shared,
973 v8_flags.experimental_wasm_shared || !ok());
974 if (table->shared && v8_flags.experimental_wasm_shared) {
975 module_->has_shared_part = true;
976 if (!IsShared(type, module_.get())) {
977 errorf(type_position,
978 "Shared table %i must have shared element type, actual "
979 "type %s",
980 i, type.name().c_str());
981 break;
982 }
983 }
984 // Note that we should not throw an error if the declared maximum size
985 // is oob. We will instead fail when growing at runtime.
986 uint64_t kNoMaximum = kMaxUInt64;
988 "table", "elements", wasm::max_table_size(), &table->initial_size,
989 table->has_maximum_size, kNoMaximum, &table->maximum_size,
990 table->is_table64() ? k64BitLimits : k32BitLimits);
991 break;
992 }
993 case kExternalMemory: {
994 // ===== Imported memory =============================================
995 static_assert(kV8MaxWasmMemories <= kMaxUInt32);
996 if (module_->memories.size() >= kV8MaxWasmMemories - 1) {
997 errorf("At most %u imported memories are supported",
999 break;
1000 }
1001 uint32_t mem_index = static_cast<uint32_t>(module_->memories.size());
1002 import->index = mem_index;
1003 module_->memories.emplace_back();
1004 WasmMemory* external_memory = &module_->memories.back();
1005 external_memory->imported = true;
1006 external_memory->index = mem_index;
1007
1008 consume_memory_flags(external_memory);
1009 uint32_t max_pages = external_memory->is_memory64()
1013 "memory", "pages", max_pages, &external_memory->initial_pages,
1014 external_memory->has_maximum_pages, max_pages,
1015 &external_memory->maximum_pages,
1016 external_memory->is_memory64() ? k64BitLimits : k32BitLimits);
1017 break;
1018 }
1019 case kExternalGlobal: {
1020 // ===== Imported global =============================================
1021 import->index = static_cast<uint32_t>(module_->globals.size());
1022 ValueType type = consume_value_type(module_.get());
1023 auto [mutability, shared] = consume_global_flags();
1024 if (V8_UNLIKELY(failed())) break;
1025 if (V8_UNLIKELY(shared && !IsShared(type, module_.get()))) {
1026 error("shared imported global must have shared type");
1027 break;
1028 }
1029 module_->globals.push_back(
1030 WasmGlobal{.type = type,
1031 .mutability = mutability,
1032 .index = 0, // set later in CalculateGlobalOffsets
1033 .shared = shared,
1034 .imported = true});
1035 module_->num_imported_globals++;
1036 DCHECK_EQ(module_->globals.size(), module_->num_imported_globals);
1037 if (shared) module_->has_shared_part = true;
1038 if (mutability) module_->num_imported_mutable_globals++;
1039 if (tracer_) tracer_->NextLine();
1040 break;
1041 }
1042 case kExternalTag: {
1043 // ===== Imported tag ================================================
1044 import->index = static_cast<uint32_t>(module_->tags.size());
1045 module_->num_imported_tags++;
1046 const WasmTagSig* tag_sig = nullptr;
1047 consume_exception_attribute(); // Attribute ignored for now.
1048 ModuleTypeIndex sig_index =
1049 consume_tag_sig_index(module_.get(), &tag_sig);
1050 module_->tags.emplace_back(tag_sig, sig_index);
1051 break;
1052 }
1053 default:
1054 errorf(pos, "unknown import kind 0x%02x", kind);
1055 break;
1056 }
1057 }
1058 if (module_->memories.size() > 1) {
1059 detected_features_->add_multi_memory();
1060 if (v8_flags.wasm_jitless) {
1061 error("Multiple memories not supported in Wasm jitless mode");
1062 }
1063 }
1065 module_->type_feedback.well_known_imports.Initialize(
1066 module_->num_imported_functions);
1067 if (tracer_) tracer_->ImportsDone(module_.get());
1068 }
1069
1071 uint32_t functions_count =
1072 consume_count("functions count", v8_flags.max_wasm_functions);
1073 DCHECK_EQ(module_->functions.size(), module_->num_imported_functions);
1074 uint32_t total_function_count =
1075 module_->num_imported_functions + functions_count;
1076 module_->functions.resize(total_function_count);
1077 module_->num_declared_functions = functions_count;
1078 // Also initialize the {validated_functions} bitset here, now that we know
1079 // the number of declared functions.
1080 DCHECK_NULL(module_->validated_functions);
1081 module_->validated_functions =
1082 std::make_unique<std::atomic<uint8_t>[]>((functions_count + 7) / 8);
1083 if (is_asmjs_module(module_.get())) {
1084 // Mark all asm.js functions as valid by design (it's faster to do this
1085 // here than to check this in {WasmModule::function_was_validated}).
1086 std::fill_n(module_->validated_functions.get(), (functions_count + 7) / 8,
1087 0xff);
1088 }
1089
1090 for (uint32_t func_index = module_->num_imported_functions;
1091 func_index < total_function_count; ++func_index) {
1092 WasmFunction* function = &module_->functions[func_index];
1093 function->func_index = func_index;
1094 if (tracer_) tracer_->FunctionName(func_index);
1095 function->sig_index = consume_sig_index(module_.get(), &function->sig);
1096 if (!ok()) return;
1097 }
1098 }
1099
1101 static_assert(kV8MaxWasmTables <= kMaxUInt32);
1102 uint32_t table_count = consume_count("table count", kV8MaxWasmTables);
1103
1104 for (uint32_t i = 0; ok() && i < table_count; i++) {
1106 module_->tables.emplace_back();
1107 WasmTable* table = &module_->tables.back();
1108 const uint8_t* type_position = pc();
1109
1110 bool has_initializer = false;
1112 pc(), "table-with-initializer byte") == 0x40) {
1113 consume_bytes(1, "with-initializer ", tracer_);
1114 has_initializer = true;
1115 type_position++;
1116 uint8_t reserved = consume_u8("reserved-byte", tracer_);
1117 if (reserved != 0) {
1118 error(type_position, "Reserved byte must be 0x00");
1119 break;
1120 }
1121 type_position++;
1122 }
1123
1124 ValueType table_type = consume_value_type(module_.get());
1125 if (!table_type.is_object_reference()) {
1126 error(type_position, "Only reference types can be used as table types");
1127 break;
1128 }
1129 if (!has_initializer && !table_type.is_defaultable()) {
1130 errorf(type_position,
1131 "Table of non-defaultable table %s needs initial value",
1132 table_type.name().c_str());
1133 break;
1134 }
1135 table->type = table_type;
1136
1137 consume_table_flags(table);
1138 DCHECK_IMPLIES(table->shared, v8_flags.experimental_wasm_shared || !ok());
1139 if (table->shared && v8_flags.experimental_wasm_shared) {
1140 module_->has_shared_part = true;
1141 if (!IsShared(table_type, module_.get())) {
1142 errorf(
1143 type_position,
1144 "Shared table %i must have shared element type, actual type %s",
1145 i + module_->num_imported_tables, table_type.name().c_str());
1146 break;
1147 }
1148 }
1149 // Note that we should not throw an error if the declared maximum size is
1150 // oob. We will instead fail when growing at runtime.
1151 uint64_t kNoMaximum = kMaxUInt64;
1153 "table", "elements", wasm::max_table_size(), &table->initial_size,
1154 table->has_maximum_size, kNoMaximum, &table->maximum_size,
1155 table->is_table64() ? k64BitLimits : k32BitLimits);
1156
1157 if (has_initializer) {
1158 table->initial_value =
1159 consume_init_expr(module_.get(), table_type, table->shared);
1160 }
1161 }
1162 }
1163
1165 const uint8_t* mem_count_pc = pc();
1166 static_assert(kV8MaxWasmMemories <= kMaxUInt32);
1167 // Use {kV8MaxWasmMemories} here, but only allow for >1 memory if
1168 // multi-memory is enabled (checked below). This allows for better error
1169 // messages.
1170 uint32_t memory_count = consume_count("memory count", kV8MaxWasmMemories);
1171 size_t imported_memories = module_->memories.size();
1172 DCHECK_GE(kV8MaxWasmMemories, imported_memories);
1173 if (memory_count > kV8MaxWasmMemories - imported_memories) {
1174 errorf(mem_count_pc,
1175 "Exceeding maximum number of memories (%u; declared %u, "
1176 "imported %zu)",
1177 kV8MaxWasmMemories, memory_count, imported_memories);
1178 }
1179 module_->memories.resize(imported_memories + memory_count);
1180
1181 for (uint32_t i = 0; ok() && i < memory_count; i++) {
1182 WasmMemory* memory = module_->memories.data() + imported_memories + i;
1183 memory->index = static_cast<uint32_t>(imported_memories + i);
1185 consume_memory_flags(memory);
1186 uint32_t max_pages =
1187 memory->is_memory64() ? kSpecMaxMemory64Pages : kSpecMaxMemory32Pages;
1189 "memory", "pages", max_pages, &memory->initial_pages,
1190 memory->has_maximum_pages, max_pages, &memory->maximum_pages,
1191 memory->is_memory64() ? k64BitLimits : k32BitLimits);
1192 }
1193 if (module_->memories.size() > 1) {
1194 detected_features_->add_multi_memory();
1195 if (v8_flags.wasm_jitless) {
1196 error("Multiple memories not supported in Wasm jitless mode");
1197 }
1198 }
1200 }
1201
1203 for (WasmMemory& memory : module_->memories) {
1204 UpdateComputedInformation(&memory, module_->origin);
1205 }
1206 }
1207
1209 uint32_t globals_count = consume_count("globals count", kV8MaxWasmGlobals);
1210 uint32_t imported_globals = static_cast<uint32_t>(module_->globals.size());
1211 // It is important to not resize the globals vector from the beginning,
1212 // because we use its current size when decoding the initializer.
1213 module_->globals.reserve(imported_globals + globals_count);
1214 for (uint32_t i = 0; ok() && i < globals_count; ++i) {
1215 TRACE("DecodeGlobal[%d] module+%d\n", i, static_cast<int>(pc_ - start_));
1217 const uint8_t* pos = pc_;
1218 ValueType type = consume_value_type(module_.get());
1219 auto [mutability, shared] = consume_global_flags();
1220 if (failed()) return;
1221 if (shared && !IsShared(type, module_.get())) {
1222 CHECK(v8_flags.experimental_wasm_shared);
1223 errorf(pos, "Shared global %i must have shared type, actual type %s",
1224 i + imported_globals, type.name().c_str());
1225 return;
1226 }
1227 // Validation that {type} and {shared} are compatible will happen in
1228 // {consume_init_expr}.
1229 ConstantExpression init = consume_init_expr(module_.get(), type, shared);
1230 module_->globals.push_back(
1231 WasmGlobal{.type = type,
1232 .mutability = mutability,
1233 .init = init,
1234 .index = 0, // set later in CalculateGlobalOffsets
1235 .shared = shared});
1236 if (shared) module_->has_shared_part = true;
1237 }
1238 }
1239
1241 uint32_t export_table_count =
1242 consume_count("exports count", kV8MaxWasmExports);
1243 module_->export_table.reserve(export_table_count);
1244 for (uint32_t i = 0; ok() && i < export_table_count; ++i) {
1245 TRACE("DecodeExportTable[%d] module+%d\n", i,
1246 static_cast<int>(pc_ - start_));
1247 if (tracer_) {
1248 tracer_->Description("export #");
1250 tracer_->NextLine();
1251 }
1252
1253 WireBytesRef name = consume_utf8_string(this, "field name", tracer_);
1254
1255 const uint8_t* kind_pos = pc();
1257 static_cast<ImportExportKindCode>(consume_u8("kind", tracer_));
1258
1259 module_->export_table.push_back(WasmExport{.name = name, .kind = kind});
1260 WasmExport* exp = &module_->export_table.back();
1261
1262 if (tracer_) {
1263 tracer_->Description(": ");
1265 tracer_->Description(" ");
1266 }
1267 switch (kind) {
1268 case kExternalFunction: {
1269 WasmFunction* func = nullptr;
1270 exp->index = consume_func_index(module_.get(), &func);
1271
1272 if (failed()) break;
1273 DCHECK_NOT_NULL(func);
1274
1275 module_->num_exported_functions++;
1276 func->exported = true;
1277 // Exported functions are considered "declared".
1278 func->declared = true;
1279 break;
1280 }
1281 case kExternalTable: {
1282 WasmTable* table = nullptr;
1283 exp->index = consume_table_index(module_.get(), &table);
1284 if (table) table->exported = true;
1285 break;
1286 }
1287 case kExternalMemory: {
1288 const uint8_t* index_pos = pc();
1289 exp->index = consume_u32v("memory index", tracer_);
1290 size_t num_memories = module_->memories.size();
1291 if (exp->index >= module_->memories.size()) {
1292 errorf(index_pos,
1293 "invalid exported memory index %u (having %zu memor%s)",
1294 exp->index, num_memories, num_memories == 1 ? "y" : "ies");
1295 break;
1296 }
1297 module_->memories[exp->index].exported = true;
1298 break;
1299 }
1300 case kExternalGlobal: {
1301 WasmGlobal* global = nullptr;
1302 exp->index = consume_global_index(module_.get(), &global);
1303 if (global) {
1304 global->exported = true;
1305 }
1306 break;
1307 }
1308 case kExternalTag: {
1309 WasmTag* tag = nullptr;
1310 exp->index = consume_tag_index(module_.get(), &tag);
1311 break;
1312 }
1313 default:
1314 errorf(kind_pos, "invalid export kind 0x%02x", exp->kind);
1315 break;
1316 }
1317 if (tracer_) tracer_->NextLine();
1318 }
1319 // Check for duplicate exports (except for asm.js).
1320 if (ok() && module_->origin == kWasmOrigin &&
1321 module_->export_table.size() > 1) {
1322 std::vector<WasmExport> sorted_exports(module_->export_table);
1323
1324 auto cmp_less = [this](const WasmExport& a, const WasmExport& b) {
1325 // Return true if a < b.
1326 if (a.name.length() != b.name.length()) {
1327 return a.name.length() < b.name.length();
1328 }
1329 const uint8_t* left =
1330 start() + GetBufferRelativeOffset(a.name.offset());
1331 const uint8_t* right =
1332 start() + GetBufferRelativeOffset(b.name.offset());
1333 return memcmp(left, right, a.name.length()) < 0;
1334 };
1335 std::stable_sort(sorted_exports.begin(), sorted_exports.end(), cmp_less);
1336
1337 auto it = sorted_exports.begin();
1338 WasmExport* last = &*it++;
1339 for (auto end = sorted_exports.end(); it != end; last = &*it++) {
1340 DCHECK(!cmp_less(*it, *last)); // Vector must be sorted.
1341 if (!cmp_less(*last, *it)) {
1342 const uint8_t* pc =
1343 start() + GetBufferRelativeOffset(it->name.offset());
1344 TruncatedUserString<> name(pc, it->name.length());
1345 errorf(pc, "Duplicate export name '%.*s' for %s %d and %s %d",
1346 name.length(), name.start(), ExternalKindName(last->kind),
1347 last->index, ExternalKindName(it->kind), it->index);
1348 break;
1349 }
1350 }
1351 }
1352 }
1353
1356 WasmFunction* func;
1357 const uint8_t* pos = pc_;
1358 module_->start_function_index = consume_func_index(module_.get(), &func);
1359 if (tracer_) tracer_->NextLine();
1360 if (func &&
1361 (func->sig->parameter_count() > 0 || func->sig->return_count() > 0)) {
1362 error(pos, "invalid start function: non-zero parameter or return count");
1363 }
1364 }
1365
1367 uint32_t segment_count =
1368 consume_count("segment count", wasm::max_table_size());
1369
1370 for (uint32_t i = 0; i < segment_count; ++i) {
1374 if (failed()) return;
1375 DCHECK_NE(segment.type, kWasmBottom);
1376
1377 for (uint32_t j = 0; j < segment.element_count; j++) {
1378 // Just run validation on elements; do not store them anywhere. We will
1379 // decode them again from wire bytes as needed.
1380 consume_element_segment_entry(module_.get(), segment);
1381 if (failed()) return;
1382 }
1383 module_->elem_segments.push_back(std::move(segment));
1384 }
1385 }
1386
1388 // Make sure global offset were calculated before they get accessed during
1389 // function compilation.
1391 uint32_t code_section_start = pc_offset();
1392 uint32_t functions_count = consume_u32v("functions count", tracer_);
1393 if (tracer_) {
1394 tracer_->Description(functions_count);
1395 tracer_->NextLine();
1396 }
1397 CheckFunctionsCount(functions_count, code_section_start);
1398
1399 auto inst_traces_it = this->inst_traces_.begin();
1400 std::vector<std::pair<uint32_t, uint32_t>> inst_traces;
1401
1402 for (uint32_t i = 0; ok() && i < functions_count; ++i) {
1403 int function_index = module_->num_imported_functions + i;
1404 if (tracer_) {
1405 tracer_->Description("function #");
1406 tracer_->FunctionName(function_index);
1407 tracer_->NextLine();
1408 }
1409 const uint8_t* pos = pc();
1410 uint32_t size = consume_u32v("body size", tracer_);
1411 if (tracer_) {
1412 tracer_->Description(size);
1413 tracer_->NextLine();
1414 }
1415 if (size > kV8MaxWasmFunctionSize) {
1416 errorf(pos, "size %u > maximum function size %zu", size,
1418 return;
1419 }
1420 uint32_t offset = pc_offset();
1421 consume_bytes(size, "function body");
1422 if (failed()) break;
1423 DecodeFunctionBody(function_index, size, offset);
1424
1425 // Now that the function has been decoded, we can compute module offsets.
1426 for (; inst_traces_it != this->inst_traces_.end() &&
1427 std::get<0>(*inst_traces_it) == i;
1428 ++inst_traces_it) {
1429 uint32_t trace_offset = offset + std::get<1>(*inst_traces_it);
1430 uint32_t mark_id = std::get<2>(*inst_traces_it);
1431 std::pair<uint32_t, uint32_t> trace_mark = {trace_offset, mark_id};
1432 inst_traces.push_back(trace_mark);
1433 }
1434 }
1435 // If we have actually decoded traces and they were all decoded without
1436 // error, then we can move them to the module. If any errors are found, it
1437 // is safe to throw away all traces.
1438 if (V8_UNLIKELY(!inst_traces.empty() &&
1439 inst_traces_it == this->inst_traces_.end())) {
1440 // This adds an invalid entry at the end of the traces. An invalid entry
1441 // is defined as having an module offset of 0 and a markid of 0.
1442 inst_traces.push_back({0, 0});
1443 this->module_->inst_traces = std::move(inst_traces);
1444 }
1445 DCHECK_GE(pc_offset(), code_section_start);
1446 module_->code = {code_section_start, pc_offset() - code_section_start};
1447 }
1448
1449 void StartCodeSection(WireBytesRef section_bytes) {
1451 // Make sure global offset were calculated before they get accessed during
1452 // function compilation.
1454 module_->code = section_bytes;
1455 }
1456
1457 bool CheckFunctionsCount(uint32_t functions_count, uint32_t error_offset) {
1458 if (functions_count != module_->num_declared_functions) {
1459 errorf(error_offset, "function body count %u mismatch (%u expected)",
1460 functions_count, module_->num_declared_functions);
1461 return false;
1462 }
1463 return true;
1464 }
1465
1466 void DecodeFunctionBody(uint32_t func_index, uint32_t length,
1467 uint32_t offset) {
1468 WasmFunction* function = &module_->functions[func_index];
1469 function->code = {offset, length};
1470 constexpr uint32_t kSmallFunctionThreshold = 50;
1471 if (length < kSmallFunctionThreshold) {
1472 ++module_->num_small_functions;
1473 }
1474 if (tracer_) {
1475 tracer_->FunctionBody(function, pc_ - (pc_offset() - offset));
1476 }
1477 }
1478
1479 bool CheckDataSegmentsCount(uint32_t data_segments_count) {
1481 data_segments_count != module_->num_declared_data_segments) {
1482 errorf(pc(), "data segments count %u mismatch (%u expected)",
1483 data_segments_count, module_->num_declared_data_segments);
1484 return false;
1485 }
1486 return true;
1487 }
1488
1495
1497 uint32_t data_segments_count =
1498 consume_count("data segments count", kV8MaxWasmDataSegments);
1499 if (!CheckDataSegmentsCount(data_segments_count)) return;
1500
1501 module_->data_segments.reserve(data_segments_count);
1502 for (uint32_t i = 0; i < data_segments_count; ++i) {
1503 TRACE("DecodeDataSegment[%d] module+%d\n", i,
1504 static_cast<int>(pc_ - start_));
1506
1508
1509 uint32_t source_length = consume_u32v("source size", tracer_);
1510 if (tracer_) {
1511 tracer_->Description(source_length);
1512 tracer_->NextLine();
1513 }
1514 uint32_t source_offset = pc_offset();
1515
1516 if (tracer_) {
1517 tracer_->Bytes(pc_, source_length);
1518 tracer_->Description("segment data");
1519 tracer_->NextLine();
1520 }
1521 consume_bytes(source_length, "segment data");
1522
1523 if (failed()) break;
1524 module_->data_segments.emplace_back(
1525 header.is_active, header.is_shared, header.memory_index,
1526 header.dest_addr, WireBytesRef{source_offset, source_length});
1527 }
1528 }
1529
1531 if (tracer_) {
1533 pc_, end_, buffer_offset_ + static_cast<uint32_t>(pc_ - start_));
1534 }
1535 // TODO(titzer): find a way to report name errors as warnings.
1536 // Ignore all but the first occurrence of name section.
1539 module_->name_section = {buffer_offset_,
1540 static_cast<uint32_t>(end_ - start_)};
1541 // Use an inner decoder so that errors don't fail the outer decoder.
1543 // Decode all name subsections.
1544 // Be lenient with their order.
1545 while (inner.ok() && inner.more()) {
1546 uint8_t name_type = inner.consume_u8("name type");
1547 if (name_type & 0x80) inner.error("name type if not varuint7");
1548
1549 uint32_t name_payload_len = inner.consume_u32v("name payload length");
1550 if (!inner.checkAvailable(name_payload_len)) break;
1551
1552 // Decode module name, ignore the rest.
1553 // Function and local names will be decoded when needed.
1554 if (name_type == NameSectionKindCode::kModuleCode) {
1555 WireBytesRef name =
1557 "module name", ITracer::NoTrace);
1558 if (inner.ok() && validate_utf8(&inner, name)) {
1559 module_->name = name;
1560 }
1561 } else {
1562 inner.consume_bytes(name_payload_len, "name subsection payload");
1563 }
1564 }
1565 }
1566 // Skip the whole names section in the outer decoder.
1567 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1568 }
1569
1572 WireBytesRef url =
1573 wasm::consume_utf8_string(&inner, "module name", tracer_);
1574 if (inner.ok() &&
1575 module_->debug_symbols[WasmDebugSymbols::Type::SourceMap].type ==
1577 module_->debug_symbols[WasmDebugSymbols::Type::SourceMap] = {
1579 }
1581 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1582 }
1583
1586 WireBytesRef url =
1587 wasm::consume_utf8_string(&inner, "external symbol file", tracer_);
1588 if (inner.ok()) {
1592 }
1593 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1594 }
1595
1598 WireBytesRef build_id =
1600 if (inner.ok()) {
1601 module_->build_id = build_id;
1603 }
1604 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1605 }
1606
1608 TRACE("DecodeInstTrace module+%d\n", static_cast<int>(pc_ - start_));
1611
1612 // Use an inner decoder so that errors don't fail the outer decoder.
1614
1615 std::vector<std::tuple<uint32_t, uint32_t, uint32_t>> inst_traces;
1616
1617 uint32_t func_count = inner.consume_u32v("number of functions");
1618 // Keep track of the previous function index to validate the ordering.
1619 int64_t last_func_idx = -1;
1620 for (uint32_t i = 0; i < func_count; i++) {
1621 uint32_t func_idx = inner.consume_u32v("function index");
1622 if (int64_t{func_idx} <= last_func_idx) {
1623 inner.errorf("Invalid function index: %d", func_idx);
1624 break;
1625 }
1626 last_func_idx = func_idx;
1627
1628 uint32_t num_traces = inner.consume_u32v("number of trace marks");
1629 TRACE("DecodeInstTrace[%d] module+%d\n", func_idx,
1630 static_cast<int>(inner.pc() - inner.start()));
1631 // Keep track of the previous offset to validate the ordering.
1632 int64_t last_func_off = -1;
1633 for (uint32_t j = 0; j < num_traces; ++j) {
1634 uint32_t func_off = inner.consume_u32v("function offset");
1635
1636 uint32_t mark_size = inner.consume_u32v("mark size");
1637 uint32_t trace_mark_id = 0;
1638 // Build the mark id from the individual bytes.
1639 for (uint32_t k = 0; k < mark_size; k++) {
1640 trace_mark_id |= inner.consume_u8("trace mark id") << k * 8;
1641 }
1642 if (int64_t{func_off} <= last_func_off) {
1643 inner.errorf("Invalid branch offset: %d", func_off);
1644 break;
1645 }
1646 last_func_off = func_off;
1647 TRACE("DecodeInstTrace[%d][%d] module+%d\n", func_idx, func_off,
1648 static_cast<int>(inner.pc() - inner.start()));
1649 // Store the function index, function offset, and mark id into a
1650 // temporary 3-tuple. This will later be translated to a module
1651 // offset and mark id.
1652 std::tuple<uint32_t, uint32_t, uint32_t> mark_tuple = {
1653 func_idx, func_off, trace_mark_id};
1654 inst_traces.push_back(mark_tuple);
1655 }
1656 }
1657 // Extra unexpected bytes are an error.
1658 if (inner.more()) {
1659 inner.errorf("Unexpected extra bytes: %d\n",
1660 static_cast<int>(inner.pc() - inner.start()));
1661 }
1662 // If everything went well, accept the traces for the module.
1663 if (inner.ok()) {
1664 this->inst_traces_ = std::move(inst_traces);
1665 }
1666 }
1667
1668 // Skip the whole instruction trace section in the outer decoder.
1669 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1670 }
1671
1673 TRACE("DecodeCompilationHints module+%d\n", static_cast<int>(pc_ - start_));
1674
1675 // TODO(frgossen): Find a way to report compilation hint errors as warnings.
1676 // All except first occurrence after function section and before code
1677 // section are ignored.
1678 const bool before_function_section =
1680 const bool after_code_section = next_ordered_section_ > kCodeSectionCode;
1681 if (before_function_section || after_code_section ||
1683 return;
1684 }
1686
1687 // TODO(frgossen) Propagate errors to outer decoder in experimental phase.
1688 // We should use an inner decoder later and propagate its errors as
1689 // warnings.
1690 Decoder& decoder = *this;
1691 // Decoder decoder(start_, pc_, end_, buffer_offset_);
1692
1693 // Ensure exactly one compilation hint per function.
1694 uint32_t hint_count = decoder.consume_u32v("compilation hint count");
1695 if (hint_count != module_->num_declared_functions) {
1696 decoder.errorf(decoder.pc(), "Expected %u compilation hints (%u found)",
1697 module_->num_declared_functions, hint_count);
1698 }
1699
1700 // Decode sequence of compilation hints.
1701 if (decoder.ok()) {
1702 module_->compilation_hints.reserve(hint_count);
1703 }
1704 for (uint32_t i = 0; decoder.ok() && i < hint_count; i++) {
1705 TRACE("DecodeCompilationHints[%d] module+%d\n", i,
1706 static_cast<int>(pc_ - start_));
1707
1708 // Compilation hints are encoded in one byte each.
1709 // +-------+----------+---------------+----------+
1710 // | 2 bit | 2 bit | 2 bit | 2 bit |
1711 // | ... | Top tier | Baseline tier | Strategy |
1712 // +-------+----------+---------------+----------+
1713 uint8_t hint_byte = decoder.consume_u8("compilation hint");
1714 if (!decoder.ok()) break;
1715
1716 // Validate the hint_byte.
1717 // For the compilation strategy, all 2-bit values are valid. For the tier,
1718 // only 0x0, 0x1, and 0x2 are allowed.
1719 static_assert(
1720 static_cast<int>(WasmCompilationHintTier::kDefault) == 0 &&
1721 static_cast<int>(WasmCompilationHintTier::kBaseline) == 1 &&
1722 static_cast<int>(WasmCompilationHintTier::kOptimized) == 2,
1723 "The check below assumes that 0x03 is the only invalid 2-bit number "
1724 "for a compilation tier");
1725 if (((hint_byte >> 2) & 0x03) == 0x03 ||
1726 ((hint_byte >> 4) & 0x03) == 0x03) {
1727 decoder.errorf(decoder.pc(),
1728 "Invalid compilation hint %#04x (invalid tier 0x03)",
1729 hint_byte);
1730 break;
1731 }
1732
1733 // Decode compilation hint.
1735 hint.strategy =
1736 static_cast<WasmCompilationHintStrategy>(hint_byte & 0x03);
1737 hint.baseline_tier =
1738 static_cast<WasmCompilationHintTier>((hint_byte >> 2) & 0x03);
1739 hint.top_tier =
1740 static_cast<WasmCompilationHintTier>((hint_byte >> 4) & 0x03);
1741
1742 // Ensure that the top tier never downgrades a compilation result. If
1743 // baseline and top tier are the same compilation will be invoked only
1744 // once.
1745 if (hint.top_tier < hint.baseline_tier &&
1747 decoder.errorf(decoder.pc(),
1748 "Invalid compilation hint %#04x (forbidden downgrade)",
1749 hint_byte);
1750 }
1751
1752 // Happily accept compilation hint.
1753 if (decoder.ok()) {
1754 module_->compilation_hints.push_back(std::move(hint));
1755 }
1756 }
1757
1758 // If section was invalid reset compilation hints.
1759 if (decoder.failed()) {
1760 module_->compilation_hints.clear();
1761 }
1762
1763 // @TODO(frgossen) Skip the whole compilation hints section in the outer
1764 // decoder if inner decoder was used.
1765 // consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1766 }
1767
1769 TRACE("DecodeBranchHints module+%d\n", static_cast<int>(pc_ - start_));
1770 detected_features_->add_branch_hinting();
1773 // Use an inner decoder so that errors don't fail the outer decoder.
1775 BranchHintInfo branch_hints;
1776
1777 uint32_t func_count = inner.consume_u32v("number of functions");
1778 // Keep track of the previous function index to validate the ordering
1779 int64_t last_func_idx = -1;
1780 for (uint32_t i = 0; i < func_count; i++) {
1781 uint32_t func_idx = inner.consume_u32v("function index");
1782 if (int64_t{func_idx} <= last_func_idx) {
1783 inner.errorf("Invalid function index: %d", func_idx);
1784 break;
1785 }
1786 last_func_idx = func_idx;
1787 uint32_t num_hints = inner.consume_u32v("number of hints");
1788 BranchHintMap func_branch_hints;
1789 TRACE("DecodeBranchHints[%d] module+%d\n", func_idx,
1790 static_cast<int>(inner.pc() - inner.start()));
1791 // Keep track of the previous branch offset to validate the ordering
1792 int64_t last_br_off = -1;
1793 for (uint32_t j = 0; j < num_hints; ++j) {
1794 uint32_t br_off = inner.consume_u32v("branch instruction offset");
1795 if (int64_t{br_off} <= last_br_off) {
1796 inner.errorf("Invalid branch offset: %d", br_off);
1797 break;
1798 }
1799 last_br_off = br_off;
1800 uint32_t data_size = inner.consume_u32v("data size");
1801 if (data_size != 1) {
1802 inner.errorf("Invalid data size: %#x. Expected 1.", data_size);
1803 break;
1804 }
1805 uint32_t br_dir = inner.consume_u8("branch direction");
1806 TRACE("DecodeBranchHints[%d][%d] module+%d\n", func_idx, br_off,
1807 static_cast<int>(inner.pc() - inner.start()));
1808 BranchHint hint;
1809 switch (br_dir) {
1810 case 0:
1811 hint = BranchHint::kFalse;
1812 break;
1813 case 1:
1814 hint = BranchHint::kTrue;
1815 break;
1816 default:
1817 hint = BranchHint::kNone;
1818 inner.errorf(inner.pc(), "Invalid branch hint %#x", br_dir);
1819 break;
1820 }
1821 if (!inner.ok()) {
1822 break;
1823 }
1824 func_branch_hints.insert(br_off, hint);
1825 }
1826 if (!inner.ok()) {
1827 break;
1828 }
1829 branch_hints.emplace(func_idx, std::move(func_branch_hints));
1830 }
1831 // Extra unexpected bytes are an error.
1832 if (inner.more()) {
1833 inner.errorf("Unexpected extra bytes: %d\n",
1834 static_cast<int>(inner.pc() - inner.start()));
1835 }
1836 // If everything went well, accept the hints for the module.
1837 if (inner.ok()) {
1838 module_->branch_hints = std::move(branch_hints);
1839 }
1840 }
1841 // Skip the whole branch hints section in the outer decoder.
1842 consume_bytes(static_cast<uint32_t>(end_ - start_), nullptr);
1843 }
1844
1846 module_->num_declared_data_segments =
1847 consume_count("data segments count", kV8MaxWasmDataSegments);
1849 }
1850
1852 uint32_t tag_count = consume_count("tag count", kV8MaxWasmTags);
1853 for (uint32_t i = 0; ok() && i < tag_count; ++i) {
1854 TRACE("DecodeTag[%d] module+%d\n", i, static_cast<int>(pc_ - start_));
1856 const WasmTagSig* tag_sig = nullptr;
1857 consume_exception_attribute(); // Attribute ignored for now.
1858 ModuleTypeIndex sig_index =
1859 consume_tag_sig_index(module_.get(), &tag_sig);
1860 module_->tags.emplace_back(tag_sig, sig_index);
1861 }
1862 }
1863
1865 uint32_t deferred = consume_count("deferred string literal count",
1867 if (deferred) {
1868 errorf(pc(), "Invalid deferred string literal count %u (expected 0)",
1869 deferred);
1870 }
1871 uint32_t immediate = consume_count("string literal count",
1872 kV8MaxWasmStringLiterals - deferred);
1873 for (uint32_t i = 0; ok() && i < immediate; ++i) {
1874 TRACE("DecodeStringLiteral[%d] module+%d\n", i,
1875 static_cast<int>(pc_ - start_));
1877 // TODO(12868): Throw if the string's utf-16 length > String::kMaxLength.
1878 WireBytesRef pos = wasm::consume_string(this, unibrow::Utf8Variant::kWtf8,
1879 "string literal", tracer_);
1880 module_->stringref_literals.emplace_back(pos);
1881 }
1882 }
1883
1885 // The declared vs. defined function count is normally checked when
1886 // decoding the code section, but we have to check it here too in case the
1887 // code section is absent.
1888 if (module_->num_declared_functions != 0) {
1889 DCHECK_LT(module_->num_imported_functions, module_->functions.size());
1890 // We know that the code section has been decoded if the first
1891 // non-imported function has its code set.
1892 if (!module_->functions[module_->num_imported_functions].code.is_set()) {
1893 errorf(pc(), "function count is %u, but code section is absent",
1894 module_->num_declared_functions);
1895 return false;
1896 }
1897 }
1898 // Perform a similar check for the DataCount and Data sections, where data
1899 // segments are declared but the Data section is absent.
1901 static_cast<uint32_t>(module_->data_segments.size()))) {
1902 return false;
1903 }
1904 return true;
1905 }
1906
1908 if (ok() && CheckMismatchedCounts()) {
1909 // We calculate the global offsets here, because there may not be a
1910 // global section and code section that would have triggered the
1911 // calculation before. Even without the globals section the calculation
1912 // is needed because globals can also be defined in the import section.
1914 }
1915
1916 if (module_->has_shared_part) detected_features_->add_shared();
1917
1918 return toResult(std::move(module_));
1919 }
1920
1921 // Decodes an entire module.
1922 ModuleResult DecodeModule(bool validate_functions) {
1923 // Keep a reference to the wire bytes, in case this decoder gets reset on
1924 // error.
1926 size_t max_size = max_module_size();
1927 if (wire_bytes.size() > max_size) {
1928 return ModuleResult{WasmError{0, "size > maximum module size (%zu): %zu",
1929 max_size, wire_bytes.size()}};
1930 }
1931
1932 DecodeModuleHeader(wire_bytes);
1933 if (failed()) return toResult(nullptr);
1934
1935 static constexpr uint32_t kWasmHeaderSize = 8;
1936 Decoder section_iterator_decoder(start_ + kWasmHeaderSize, end_,
1937 kWasmHeaderSize);
1938 WasmSectionIterator section_iter(&section_iterator_decoder, tracer_);
1939
1940 while (ok()) {
1941 if (section_iter.section_code() != SectionCode::kUnknownSectionCode) {
1942 uint32_t offset = static_cast<uint32_t>(section_iter.payload().begin() -
1943 wire_bytes.begin());
1944 DecodeSection(section_iter.section_code(), section_iter.payload(),
1945 offset);
1946 if (!ok()) break;
1947 }
1948 if (!section_iter.more()) break;
1949 section_iter.advance(true);
1950 }
1951
1952 // Check for module structure errors before validating function bodies, to
1953 // produce consistent error message independent of whether validation
1954 // happens here or later.
1955 if (section_iterator_decoder.failed()) {
1956 return section_iterator_decoder.toResult(nullptr);
1957 }
1958
1960 if (!result.failed() && validate_functions) {
1961 std::function<bool(int)> kNoFilter;
1962 if (WasmError validation_error =
1963 ValidateFunctions(module_.get(), enabled_features_, wire_bytes,
1964 kNoFilter, detected_features_)) {
1965 result = ModuleResult{validation_error};
1966 }
1967 }
1968
1969 if (v8_flags.dump_wasm_module) DumpModule(wire_bytes, result.ok());
1970
1971 return result;
1972 }
1973
1974 // Decodes a single anonymous function starting at {start_}.
1976 ModuleWireBytes wire_bytes,
1977 const WasmModule* module) {
1978 DCHECK(ok());
1979 pc_ = start_;
1980 expect_u8("type form", kWasmFunctionTypeCode);
1981 WasmFunction function;
1982 function.sig = consume_sig(zone);
1983 function.code = {off(pc_), static_cast<uint32_t>(end_ - pc_)};
1984
1985 if (!ok()) return FunctionResult{std::move(error_)};
1986
1987 constexpr bool kShared = false;
1988 FunctionBody body{function.sig, off(pc_), pc_, end_, kShared};
1989
1990 WasmDetectedFeatures unused_detected_features;
1992 &unused_detected_features, body);
1993
1994 if (result.failed()) return FunctionResult{std::move(result).error()};
1995
1996 return FunctionResult{std::make_unique<WasmFunction>(function)};
1997 }
1998
1999 // Decodes a single function signature at {start}.
2001 const uint8_t* start) {
2002 pc_ = start;
2003 if (!expect_u8("type form", kWasmFunctionTypeCode)) return nullptr;
2004 const FunctionSig* result = consume_sig(zone);
2005 return ok() ? result : nullptr;
2006 }
2007
2009 constexpr bool kIsShared = false; // TODO(14616): Extend this.
2010 return consume_init_expr(module_.get(), expected, kIsShared);
2011 }
2012
2013 // Takes a module as parameter so that wasm-disassembler.cc can pass its own
2014 // module.
2016 WasmModule* module, const WasmElemSegment& segment) {
2018 return consume_init_expr(module, segment.type, segment.shared);
2019 } else {
2021 consume_element_func_index(module, segment.type));
2022 }
2023 }
2024
2025 const std::shared_ptr<WasmModule>& shared_module() const { return module_; }
2026
2027 private:
2029 return seen_unordered_sections_ & (1 << section_code);
2030 }
2031
2033 seen_unordered_sections_ |= 1 << section_code;
2034 }
2035
2036 uint32_t off(const uint8_t* ptr) {
2037 return static_cast<uint32_t>(ptr - start_) + buffer_offset_;
2038 }
2039
2040 // Calculate individual global offsets and total size of globals table. This
2041 // function should be called after all globals have been defined, which is
2042 // after the import section and the global section, but before the global
2043 // offsets are accessed, e.g. by the function compilers. The moment when this
2044 // function should be called is not well-defined, as the global section may
2045 // not exist. Therefore this function is called multiple times.
2047 if (module->globals.empty() || module->untagged_globals_buffer_size != 0 ||
2048 module->tagged_globals_buffer_size != 0) {
2049 // This function has already been executed before, so we don't have to
2050 // execute it again.
2051 return;
2052 }
2053 uint32_t untagged_offset = 0;
2054 uint32_t tagged_offset = 0;
2055 uint32_t num_imported_mutable_globals = 0;
2056 for (WasmGlobal& global : module->globals) {
2057 if (global.mutability && global.imported) {
2058 global.index = num_imported_mutable_globals++;
2059 } else if (global.type.is_reference()) {
2060 global.offset = tagged_offset;
2061 // All entries in the tagged_globals_buffer have size 1.
2062 tagged_offset++;
2063 } else {
2064 int size = global.type.value_kind_size();
2065 untagged_offset = (untagged_offset + size - 1) & ~(size - 1); // align
2066 global.offset = untagged_offset;
2067 untagged_offset += size;
2068 }
2069 }
2070 module->untagged_globals_buffer_size = untagged_offset;
2071 module->tagged_globals_buffer_size = tagged_offset;
2072 }
2073
2075 const FunctionSig** sig) {
2076 const uint8_t* pos = pc_;
2077 ModuleTypeIndex sig_index{consume_u32v("signature index")};
2078 if (tracer_) tracer_->Bytes(pos, static_cast<uint32_t>(pc_ - pos));
2079 if (!module->has_signature(sig_index)) {
2080 errorf(pos, "no signature at index %u (%d types)", sig_index.index,
2081 static_cast<int>(module->types.size()));
2082 *sig = nullptr;
2083 return {};
2084 }
2085 *sig = module->signature(sig_index);
2086 if (tracer_) {
2088 tracer_->NextLine();
2089 }
2090 return sig_index;
2091 }
2092
2094 const FunctionSig** sig) {
2095 const uint8_t* pos = pc_;
2096 ModuleTypeIndex sig_index = consume_sig_index(module, sig);
2097
2098 if (!enabled_features_.has_wasmfx() && *sig &&
2099 (*sig)->return_count() != 0) {
2100 errorf(pos, "tag signature %u has non-void return", sig_index);
2101 *sig = nullptr;
2102 return {};
2103 }
2104 return sig_index;
2105 }
2106
2107 uint32_t consume_count(const char* name, size_t maximum) {
2108 const uint8_t* p = pc_;
2109 uint32_t count = consume_u32v(name, tracer_);
2110 if (tracer_) {
2112 if (count == 1) {
2113 tracer_->Description(": ");
2114 } else {
2115 tracer_->NextLine();
2116 }
2117 }
2118 if (count > maximum) {
2119 errorf(p, "%s of %u exceeds internal limit of %zu", name, count, maximum);
2120 return 0;
2121 }
2122 return count;
2123 }
2124
2125 uint32_t consume_func_index(WasmModule* module, WasmFunction** func) {
2126 return consume_index("function", &module->functions, func);
2127 }
2128
2129 uint32_t consume_global_index(WasmModule* module, WasmGlobal** global) {
2130 return consume_index("global", &module->globals, global);
2131 }
2132
2133 uint32_t consume_table_index(WasmModule* module, WasmTable** table) {
2134 return consume_index("table", &module->tables, table);
2135 }
2136
2137 uint32_t consume_tag_index(WasmModule* module, WasmTag** tag) {
2138 return consume_index("tag", &module->tags, tag);
2139 }
2140
2141 template <typename T>
2142 uint32_t consume_index(const char* name, std::vector<T>* vector, T** ptr) {
2143 const uint8_t* pos = pc_;
2144 uint32_t index = consume_u32v("index", tracer_);
2145 if (tracer_) {
2146 tracer_->Description(": ");
2147 tracer_->Description(index);
2148 }
2149 if (index >= vector->size()) {
2150 errorf(pos, "%s index %u out of bounds (%d entr%s)", name, index,
2151 static_cast<int>(vector->size()),
2152 vector->size() == 1 ? "y" : "ies");
2153 *ptr = nullptr;
2154 return 0;
2155 }
2156 *ptr = &(*vector)[index];
2157 return index;
2158 }
2159
2160 // The limits byte structure is used for memories and tables.
2161 struct LimitsByte {
2162 uint8_t flags;
2163
2164 // Flags 0..7 are valid (3 bits).
2165 bool is_valid() const { return (flags & ~0x7) == 0; }
2166 bool has_maximum() const { return flags & 0x1; }
2167 bool is_shared() const { return flags & 0x2; }
2168 bool is_64bit() const { return flags & 0x4; }
2172 };
2173
2175
2176 template <LimitsByteType limits_type>
2178 if (tracer_) tracer_->Bytes(pc_, 1);
2179 LimitsByte limits{consume_u8(
2180 limits_type == kMemory ? "memory limits flags" : "table limits flags")};
2181 if (!limits.is_valid()) {
2182 errorf(pc() - 1, "invalid %s limits flags 0x%x",
2183 limits_type == kMemory ? "memory" : "table", limits.flags);
2184 }
2185
2186 if (limits.is_shared()) {
2187 if constexpr (limits_type == kMemory) {
2188 // V8 does not support shared memory without a maximum.
2189 if (!limits.has_maximum()) {
2190 error(pc() - 1, "shared memory must have a maximum defined");
2191 }
2192 if (v8_flags.experimental_wasm_shared) {
2193 error(pc() - 1,
2194 "shared memories are not supported with "
2195 "--experimental-wasm-shared yet.");
2196 }
2197 } else if (!v8_flags.experimental_wasm_shared) { // table
2198 error(pc() - 1,
2199 "invalid table limits flags, enable with "
2200 "--experimental-wasm-shared");
2201 }
2202 }
2203
2204 if (tracer_) {
2205 if (limits.is_shared()) tracer_->Description(" shared");
2206 if (limits.is_64bit()) {
2207 tracer_->Description(limits_type == kMemory ? " mem64" : " table64");
2208 }
2209 tracer_->Description(limits.has_maximum() ? " with maximum"
2210 : " no maximum");
2211 tracer_->NextLine();
2212 }
2213
2214 return limits;
2215 }
2216
2219 table->has_maximum_size = limits.has_maximum();
2220 table->shared = limits.is_shared();
2221 table->address_type = limits.address_type();
2222
2223 if (table->is_table64()) detected_features_->add_memory64();
2224 }
2225
2228 memory->has_maximum_pages = limits.has_maximum();
2229 memory->is_shared = limits.is_shared();
2230 memory->address_type = limits.address_type();
2231
2232 if (memory->is_shared) detected_features_->add_shared_memory();
2233 if (memory->is_memory64()) detected_features_->add_memory64();
2234 }
2235
2236 std::pair<bool, bool> consume_global_flags() {
2237 uint8_t flags = consume_u8("global flags");
2238 if (flags & ~0b11) {
2239 errorf(pc() - 1, "invalid global flags 0x%x", flags);
2240 return {false, false};
2241 }
2242 bool mutability = flags & 0b1;
2243 bool shared = flags & 0b10;
2244 if (tracer_) {
2245 tracer_->Bytes(pc_ - 1, 1); // The flags byte.
2246 if (shared) tracer_->Description(" shared");
2247 tracer_->Description(mutability ? " mutable" : " immutable");
2248 }
2249 if (shared && !v8_flags.experimental_wasm_shared) {
2250 errorf(
2251 pc() - 1,
2252 "invalid global flags 0x%x (enable via --experimental-wasm-shared)",
2253 flags);
2254 return {false, false};
2255 }
2256 return {mutability, shared};
2257 }
2258
2261 const char* name, const char* units,
2262 // Not: both memories and tables have a 32-bit limit on the initial size.
2263 uint32_t max_initial, uint32_t* initial, bool has_maximum,
2264 uint64_t max_maximum, uint64_t* maximum, ResizableLimitsType type) {
2265 const uint8_t* pos = pc();
2266 // Note that even if we read the values as 64-bit value, all V8 limits are
2267 // still within uint32_t range.
2268 uint64_t initial_64 = type == k64BitLimits
2269 ? consume_u64v("initial size", tracer_)
2270 : consume_u32v("initial size", tracer_);
2271 if (initial_64 > max_initial) {
2272 errorf(pos,
2273 "initial %s size (%" PRIu64
2274 " %s) is larger than implementation limit (%u %s)",
2275 name, initial_64, units, max_initial, units);
2276 }
2277 *initial = static_cast<uint32_t>(initial_64);
2278 if (tracer_) {
2279 tracer_->Description(*initial);
2280 tracer_->NextLine();
2281 }
2282 if (has_maximum) {
2283 pos = pc();
2284 uint64_t maximum_64 = type == k64BitLimits
2285 ? consume_u64v("maximum size", tracer_)
2286 : consume_u32v("maximum size", tracer_);
2287 if (maximum_64 > max_maximum) {
2288 errorf(pos,
2289 "maximum %s size (%" PRIu64
2290 " %s) is larger than implementation limit (%" PRIu64 " %s)",
2291 name, maximum_64, units, max_maximum, units);
2292 }
2293 if (maximum_64 < *initial) {
2294 errorf(pos,
2295 "maximum %s size (%" PRIu64 " %s) is less than initial (%u %s)",
2296 name, maximum_64, units, *initial, units);
2297 }
2298 *maximum = maximum_64;
2299 if (tracer_) {
2300 tracer_->Description(*maximum);
2301 tracer_->NextLine();
2302 }
2303 } else {
2304 *maximum = max_initial;
2305 }
2306 }
2307
2308 // Consumes a byte, and emits an error if it does not equal {expected}.
2309 bool expect_u8(const char* name, uint8_t expected) {
2310 const uint8_t* pos = pc();
2311 uint8_t value = consume_u8(name);
2312 if (value != expected) {
2313 errorf(pos, "expected %s 0x%02x, got 0x%02x", name, expected, value);
2314 return false;
2315 }
2316 return true;
2317 }
2318
2320 bool is_shared) {
2321 // The error message mimics the one generated by the {WasmFullDecoder}.
2322#define TYPE_CHECK(found) \
2323 if (V8_UNLIKELY(!IsSubtypeOf(found, expected, module))) { \
2324 errorf(pc() + 1, \
2325 "type error in constant expression[0] (expected %s, got %s)", \
2326 expected.name().c_str(), found.name().c_str()); \
2327 return {}; \
2328 }
2329
2331 // To avoid initializing a {WasmFullDecoder} for the most common
2332 // expressions, we replicate their decoding and validation here. The
2333 // manually handled cases correspond to {ConstantExpression}'s kinds.
2334 // We need to make sure to check that the expression ends in {kExprEnd};
2335 // otherwise, it is just the first operand of a composite expression, and we
2336 // fall back to the default case.
2337 if (!more()) {
2338 error("Beyond end of code");
2339 return {};
2340 }
2341 switch (static_cast<WasmOpcode>(*pc())) {
2342 case kExprI32Const: {
2343 auto [value, length] =
2344 read_i32v<FullValidationTag>(pc() + 1, "i32.const");
2345 if (V8_UNLIKELY(failed())) return {};
2346 if (V8_LIKELY(lookahead(1 + length, kExprEnd))) {
2348 if (tracer_) {
2349 tracer_->InitializerExpression(pc_, pc_ + length + 2, kWasmI32);
2350 }
2351 consume_bytes(length + 2);
2352 return ConstantExpression::I32Const(value);
2353 }
2354 break;
2355 }
2356 case kExprRefFunc: {
2357 auto [index, length] =
2358 read_u32v<FullValidationTag>(pc() + 1, "ref.func");
2359 if (V8_UNLIKELY(failed())) return {};
2360 if (V8_LIKELY(lookahead(1 + length, kExprEnd))) {
2361 if (V8_UNLIKELY(index >= module->functions.size())) {
2362 errorf(pc() + 1, "function index %u out of bounds", index);
2363 return {};
2364 }
2365 ModuleTypeIndex functype{module->functions[index].sig_index};
2366 bool functype_is_shared = module->type(functype).is_shared;
2367 ValueType type = ValueType::Ref(functype, functype_is_shared,
2369 TYPE_CHECK(type)
2370 if (V8_UNLIKELY(is_shared && !type.is_shared())) {
2371 error(pc(), "ref.func does not have a shared type");
2372 return {};
2373 }
2374 module->functions[index].declared = true;
2375 if (tracer_) {
2376 tracer_->InitializerExpression(pc_, pc_ + length + 2, type);
2377 }
2378 consume_bytes(length + 2);
2379 return ConstantExpression::RefFunc(index);
2380 }
2381 break;
2382 }
2383 case kExprRefNull: {
2384 auto [type, length] =
2386 this, pc() + 1, enabled_features_);
2388 module, type);
2389 if (V8_UNLIKELY(failed())) return {};
2390 value_type_reader::Populate(&type, module);
2391 if (V8_LIKELY(lookahead(1 + length, kExprEnd))) {
2393 if (V8_UNLIKELY(is_shared &&
2394 !IsShared(ValueType::RefNull(type), module))) {
2395 error(pc(), "ref.null does not have a shared type");
2396 return {};
2397 }
2398 if (tracer_) {
2399 tracer_->InitializerExpression(pc_, pc_ + length + 2,
2400 ValueType::RefNull(type));
2401 }
2402 consume_bytes(length + 2);
2403 return ConstantExpression::RefNull(type);
2404 }
2405 break;
2406 }
2407 default:
2408 break;
2409 }
2410#undef TYPE_CHECK
2411
2413 FunctionBody body(&sig, this->pc_offset(), pc_, end_, is_shared);
2414 WasmDetectedFeatures detected;
2416 {
2417 // We need a scope for the decoder because its destructor resets some Zone
2418 // elements, which has to be done before we reset the Zone afterwards.
2421 decoder(&init_expr_zone_, module, enabled_features_, &detected, body,
2422 module);
2423
2424 uint32_t offset = this->pc_offset();
2425
2426 decoder.DecodeFunctionBody();
2427
2428 if (tracer_) {
2429 // In case of error, decoder.end() is set to the position right before
2430 // the byte(s) that caused the error. For debugging purposes, we should
2431 // print these bytes, but we don't know how many of them there are, so
2432 // for now we have to guess. For more accurate behavior, we'd have to
2433 // pass {num_invalid_bytes} to every {decoder->DecodeError()} call.
2434 static constexpr size_t kInvalidBytesGuess = 4;
2435 const uint8_t* end =
2436 decoder.ok() ? decoder.end()
2437 : std::min(decoder.end() + kInvalidBytesGuess, end_);
2438 tracer_->InitializerExpression(pc_, end, expected);
2439 }
2440 this->pc_ = decoder.end();
2441
2442 if (decoder.failed()) {
2443 error(decoder.error().offset(), decoder.error().message().c_str());
2444 return {};
2445 }
2446
2447 if (!decoder.interface().end_found()) {
2448 error("constant expression is missing 'end'");
2449 return {};
2450 }
2451
2453 offset, static_cast<uint32_t>(decoder.end() - decoder.start()));
2454 }
2455
2456 // We reset the zone here; its memory is not used anymore, and we do not
2457 // want memory from all constant expressions to add up.
2459
2460 return result;
2461 }
2462
2463 // Read a mutability flag
2465 if (tracer_) tracer_->Bytes(pc_, 1);
2466 uint8_t val = consume_u8("mutability");
2467 if (tracer_) {
2468 tracer_->Description(val == 0 ? " immutable"
2469 : val == 1 ? " mutable"
2470 : " invalid");
2471 }
2472 if (val > 1) error(pc_ - 1, "invalid mutability");
2473 return val != 0;
2474 }
2475
2476 // Pass a {module} to get a pre-{Populate()}d ValueType. That's safe
2477 // when the module's type section has already been fully decoded.
2478 ValueType consume_value_type(const WasmModule* module = nullptr) {
2479 auto [result, length] =
2481 this, pc_,
2485 this, pc_, module_.get(), result);
2486 if (ok() && module) value_type_reader::Populate(&result, module);
2487 if (tracer_) {
2488 tracer_->Bytes(pc_, length);
2490 }
2491 consume_bytes(length, "value type");
2492 return result;
2493 }
2494
2496 auto [heap_type, length] =
2498 this, pc_,
2501
2503 this, pc_, module_.get(), heap_type);
2504 if (tracer_) {
2505 tracer_->Bytes(pc_, length);
2506 tracer_->Description(heap_type);
2507 }
2508 consume_bytes(length, "heap type");
2509 return heap_type;
2510 }
2511
2513 uint8_t opcode = read_u8<FullValidationTag>(this->pc());
2514 switch (opcode) {
2515 case kI8Code:
2516 consume_bytes(1, " i8", tracer_);
2517 return kWasmI8;
2518 case kI16Code:
2519 consume_bytes(1, " i16", tracer_);
2520 return kWasmI16;
2521 default:
2522 // It is not a packed type, so it has to be a value type.
2523 return consume_value_type();
2524 }
2525 }
2526
2528 if (tracer_) tracer_->NextLine();
2529 // Parse parameter types.
2530 uint32_t param_count =
2532 // We don't know the return count yet, so decode the parameters into a
2533 // temporary SmallVector. This needs to be copied over into the permanent
2534 // storage later.
2535 base::SmallVector<ValueType, 8> params{param_count};
2536 for (uint32_t i = 0; i < param_count; ++i) {
2537 params[i] = consume_value_type();
2539 }
2541
2542 // Parse return types.
2543 uint32_t return_count =
2545 // Now that we know the param count and the return count, we can allocate
2546 // the permanent storage.
2547 ValueType* sig_storage =
2548 zone->AllocateArray<ValueType>(param_count + return_count);
2549 // Note: Returns come first in the signature storage.
2550 std::copy_n(params.begin(), param_count, sig_storage + return_count);
2551 for (uint32_t i = 0; i < return_count; ++i) {
2552 sig_storage[i] = consume_value_type();
2554 }
2556
2557 return zone->New<FunctionSig>(return_count, param_count, sig_storage);
2558 }
2559
2560 const StructType* consume_struct(Zone* zone, bool is_descriptor) {
2561 uint32_t field_count =
2562 consume_count(", field count", kV8MaxWasmStructFields);
2563 if (failed()) return nullptr;
2564 ValueType* fields = zone->AllocateArray<ValueType>(field_count);
2565 bool* mutabilities = zone->AllocateArray<bool>(field_count);
2566 for (uint32_t i = 0; ok() && i < field_count; ++i) {
2567 fields[i] = consume_storage_type();
2568 mutabilities[i] = consume_mutability();
2569 if (tracer_) tracer_->NextLine();
2570 }
2571 if (failed()) return nullptr;
2572 uint32_t* offsets = zone->AllocateArray<uint32_t>(field_count);
2573 StructType* result = zone->New<StructType>(field_count, offsets, fields,
2574 mutabilities, is_descriptor);
2576 return result;
2577 }
2578
2580 ValueType element_type = consume_storage_type();
2581 bool mutability = consume_mutability();
2582 if (tracer_) tracer_->NextLine();
2583 if (failed()) return nullptr;
2584 return zone->New<ArrayType>(element_type, mutability);
2585 }
2586
2587 // Consume the attribute field of an exception.
2589 const uint8_t* pos = pc_;
2590 uint32_t attribute = consume_u32v("exception attribute");
2591 if (tracer_) tracer_->Bytes(pos, static_cast<uint32_t>(pc_ - pos));
2592 if (attribute != kExceptionAttribute) {
2593 errorf(pos, "exception attribute %u not supported", attribute);
2594 return 0;
2595 }
2596 return attribute;
2597 }
2598
2600 const uint8_t* pos = pc();
2601
2602 // The mask for the bit in the flag which indicates if the segment is
2603 // active or not (0 is active).
2604 constexpr uint8_t kNonActiveMask = 1 << 0;
2605 // The mask for the bit in the flag which indicates:
2606 // - for active tables, if the segment has an explicit table index field.
2607 // - for non-active tables, whether the table is declarative (vs. passive).
2608 constexpr uint8_t kHasTableIndexOrIsDeclarativeMask = 1 << 1;
2609 // The mask for the bit in the flag which indicates if the functions of this
2610 // segment are defined as function indices (0) or constant expressions (1).
2611 constexpr uint8_t kExpressionsAsElementsMask = 1 << 2;
2612 // The mask for the bit which denotes whether this segment is shared.
2613 constexpr uint8_t kSharedFlag = 1 << 3;
2614 constexpr uint8_t kFullMask = kNonActiveMask |
2615 kHasTableIndexOrIsDeclarativeMask |
2616 kExpressionsAsElementsMask | kSharedFlag;
2617
2618 uint32_t flag = consume_u32v("flag", tracer_);
2619 if ((flag & kFullMask) != flag) {
2620 errorf(pos, "illegal flag value %u", flag);
2621 return {};
2622 }
2623
2624 bool is_shared = flag & kSharedFlag;
2625 if (is_shared && !v8_flags.experimental_wasm_shared) {
2626 errorf(pos,
2627 "illegal flag value %u, enable with --experimental-wasm-shared",
2628 flag);
2629 return {};
2630 }
2631 if (is_shared) module_->has_shared_part = true;
2632
2633 const WasmElemSegment::Status status =
2634 (flag & kNonActiveMask) ? (flag & kHasTableIndexOrIsDeclarativeMask)
2638 const bool is_active = status == WasmElemSegment::kStatusActive;
2639 if (tracer_) {
2640 tracer_->Description(": ");
2643 ? "passive,"
2644 : "declarative,");
2645 }
2646
2647 WasmElemSegment::ElementType element_type =
2648 flag & kExpressionsAsElementsMask
2651
2652 const bool has_table_index =
2653 is_active && (flag & kHasTableIndexOrIsDeclarativeMask);
2654 uint32_t table_index = 0;
2655 if (has_table_index) {
2656 table_index = consume_u32v(", table index", tracer_);
2657 if (tracer_) tracer_->Description(table_index);
2658 }
2659 if (V8_UNLIKELY(is_active && table_index >= module_->tables.size())) {
2660 // If `has_table_index`, we have an explicit table index. Otherwise, we
2661 // always have the implicit table index 0.
2662 errorf(pos, "out of bounds%s table index %u",
2663 has_table_index ? "" : " implicit", table_index);
2664 return {};
2665 }
2666
2667 ValueType table_type =
2668 is_active ? module_->tables[table_index].type : kWasmBottom;
2669
2671 if (is_active) {
2672 if (tracer_) {
2673 tracer_->Description(", offset:");
2674 tracer_->NextLine();
2675 }
2677 module_.get(),
2678 module_->tables[table_index].is_table64() ? kWasmI64 : kWasmI32,
2679 is_shared);
2680 // Failed to parse offset initializer, return early.
2681 if (failed()) return {};
2682 }
2683
2684 // Denotes an active segment without table index, type, or element kind.
2685 const bool backwards_compatible_mode =
2686 is_active && !(flag & kHasTableIndexOrIsDeclarativeMask);
2688 if (element_type == WasmElemSegment::kExpressionElements) {
2689 if (backwards_compatible_mode) {
2690 type = kWasmFuncRef;
2691 } else {
2692 if (tracer_) tracer_->Description(" element type:");
2693 type = consume_value_type(module_.get());
2694 if (failed()) return {};
2695 }
2696 } else {
2697 if (!backwards_compatible_mode) {
2698 // We have to check that there is an element kind of type Function. All
2699 // other element kinds are not valid yet.
2700 if (tracer_) tracer_->Description(" ");
2701 uint8_t val = consume_u8("element type: function", tracer_);
2702 if (V8_UNLIKELY(static_cast<ImportExportKindCode>(val) !=
2704 errorf(pos, "illegal element kind 0x%x. Must be 0x%x", val,
2706 return {};
2707 }
2708 }
2709 type = kWasmFuncRef.AsNonNull();
2710 }
2711
2712 if (V8_UNLIKELY(is_active &&
2713 !IsSubtypeOf(type, table_type, this->module_.get()))) {
2714 errorf(pos,
2715 "Element segment of type %s is not a subtype of referenced "
2716 "table %u (of type %s)",
2717 type.name().c_str(), table_index, table_type.name().c_str());
2718 return {};
2719 }
2720
2721 // TODO(14616): Is this too restrictive?
2722 if (V8_UNLIKELY(is_active &&
2723 (is_shared != module_->tables[table_index].shared))) {
2724 error(pos,
2725 "Shared (resp. non-shared) element segments must refer to shared "
2726 "(resp. non-shared) tables");
2727 return {};
2728 }
2729
2730 uint32_t num_elem =
2731 consume_count(" number of elements", max_table_init_entries());
2732
2733 if (is_active) {
2734 return {is_shared, type, table_index, std::move(offset),
2735 element_type, num_elem, pc_offset()};
2736 } else {
2737 return {status, is_shared, type, element_type, num_elem, pc_offset()};
2738 }
2739 }
2740
2742 const uint8_t* pos = pc();
2743 uint32_t flag = consume_u32v("flag", tracer_);
2744
2745 if (flag & ~0b1011) {
2746 errorf(pos, "illegal flag value %u", flag);
2747 return {};
2748 }
2749
2750 uint32_t status_flag = flag & 0b11;
2751
2752 if (tracer_) {
2753 tracer_->Description(": ");
2755 status_flag == SegmentFlags::kActiveNoIndex ? "active no index"
2756 : status_flag == SegmentFlags::kPassive ? "passive"
2757 : status_flag == SegmentFlags::kActiveWithIndex ? "active with index"
2758 : "unknown");
2759 }
2760
2761 if (status_flag != SegmentFlags::kActiveNoIndex &&
2762 status_flag != SegmentFlags::kPassive &&
2763 status_flag != SegmentFlags::kActiveWithIndex) {
2764 errorf(pos, "illegal flag value %u", flag);
2765 return {};
2766 }
2767
2768 bool is_shared = flag & 0b1000;
2769
2770 if (V8_UNLIKELY(is_shared && !v8_flags.experimental_wasm_shared)) {
2771 errorf(pos,
2772 "illegal flag value %u. Enable with --experimental-wasm-shared",
2773 flag);
2774 return {};
2775 }
2776
2777 if (is_shared) module_->has_shared_part = true;
2778
2779 if (tracer_) {
2780 if (is_shared) tracer_->Description(" shared");
2781 tracer_->NextLine();
2782 }
2783
2784 bool is_active = status_flag == SegmentFlags::kActiveNoIndex ||
2785 status_flag == SegmentFlags::kActiveWithIndex;
2786 uint32_t mem_index = status_flag == SegmentFlags::kActiveWithIndex
2787 ? consume_u32v("memory index", tracer_)
2788 : 0;
2790
2791 if (is_active) {
2792 size_t num_memories = module_->memories.size();
2793 if (mem_index >= num_memories) {
2794 errorf(pos,
2795 "invalid memory index %u for data section (having %zu memor%s)",
2796 mem_index, num_memories, num_memories == 1 ? "y" : "ies");
2797 return {};
2798 }
2799 ValueType expected_type =
2800 module_->memories[mem_index].is_memory64() ? kWasmI64 : kWasmI32;
2801 offset = consume_init_expr(module_.get(), expected_type, is_shared);
2802 }
2803
2804 return {is_active, is_shared, mem_index, offset};
2805 }
2806
2808 WasmFunction* func = nullptr;
2809 const uint8_t* initial_pc = pc();
2810 uint32_t index = consume_func_index(module, &func);
2811 if (tracer_) tracer_->NextLine();
2812 if (failed()) return index;
2813 DCHECK_NOT_NULL(func);
2814 DCHECK_EQ(index, func->func_index);
2815 ValueType entry_type =
2816 ValueType::Ref(func->sig_index, module->type(func->sig_index).is_shared,
2818 if (V8_LIKELY(expected == kWasmFuncRef &&
2819 !v8_flags.experimental_wasm_shared)) {
2820 DCHECK(module->type(func->sig_index).kind == TypeDefinition::kFunction);
2821 DCHECK(IsSubtypeOf(entry_type, expected, module));
2822 } else if (V8_UNLIKELY(!IsSubtypeOf(entry_type, expected, module))) {
2823 errorf(initial_pc,
2824 "Invalid type in element entry: expected %s, got %s instead.",
2825 expected.name().c_str(), entry_type.name().c_str());
2826 return index;
2827 }
2828 func->declared = true;
2829 return index;
2830 }
2831
2834 const std::shared_ptr<WasmModule> module_;
2835 const uint8_t* module_start_ = nullptr;
2836 const uint8_t* module_end_ = nullptr;
2838 // The type section is the first section in a module.
2840 // We store next_ordered_section_ as uint8_t instead of SectionCode so that
2841 // we can increment it. This static_assert should make sure that SectionCode
2842 // does not get bigger than uint8_t accidentally.
2843 static_assert(sizeof(ModuleDecoderImpl::next_ordered_section_) ==
2844 sizeof(SectionCode),
2845 "type mismatch");
2847 static_assert(kBitsPerByte *
2850 "not enough bits");
2852 // We pass this {Zone} to the temporary {WasmFullDecoder} we allocate during
2853 // each call to {consume_init_expr}, and reset it after each such call. This
2854 // has been found to improve performance a bit over allocating a new {Zone}
2855 // each time.
2856 Zone init_expr_zone_{&allocator_, "constant expr. zone"};
2857
2858 // Instruction traces are decoded in DecodeInstTraceSection as a 3-tuple
2859 // of the function index, function offset, and mark_id. In DecodeCodeSection,
2860 // after the functions have been decoded this is translated to pairs of module
2861 // offsets and mark ids.
2862 std::vector<std::tuple<uint32_t, uint32_t, uint32_t>> inst_traces_;
2863};
2864
2865} // namespace v8::internal::wasm
2866
2867#undef TRACE
2868
2869#endif // V8_WASM_MODULE_DECODER_IMPL_H_
Builtins::Kind kind
Definition builtins.cc:40
SourcePosition pos
static bool ValidateEncoding(const uint8_t *str, size_t length)
Definition unicode.cc:234
static FILE * FOpen(const char *path, const char *mode)
static char DirectorySeparator()
static bool isDirectorySeparator(const char ch)
int length() const
Definition vector.h:64
constexpr size_t size() const
Definition vector.h:70
constexpr T * begin() const
Definition vector.h:96
static Vector< T > cast(Vector< S > input)
Definition vector.h:157
auto Returns(ReturnTypes... return_types) const
Definition signature.h:166
T * AllocateArray(size_t length)
Definition zone.h:127
T * New(Args &&... args)
Definition zone.h:114
ValueType * element_type_writable_ptr()
void insert(uint32_t offset, BranchHint hint)
static constexpr ConstantExpression WireBytes(uint32_t offset, uint32_t length)
static constexpr ConstantExpression I32Const(int32_t value)
static constexpr ConstantExpression RefFunc(uint32_t index)
static constexpr ConstantExpression RefNull(HeapType type)
const uint8_t * start_
Definition decoder.h:436
const uint8_t * start() const
Definition decoder.h:407
Result< R > toResult(T &&val)
Definition decoder.h:378
uint32_t consume_u32(const char *name, ITracer *tracer)
Definition decoder.h:242
uint32_t available_bytes() const
Definition decoder.h:314
void V8_NOINLINE V8_PRESERVE_MOST errorf(const char *format, Args... args)
Definition decoder.h:342
const WasmError & error() const
Definition decoder.h:405
const uint8_t * pc() const
Definition decoder.h:408
void Reset(const uint8_t *start, const uint8_t *end, uint32_t buffer_offset=0)
Definition decoder.h:387
void set_end(const uint8_t *end)
Definition decoder.h:427
bool checkAvailable(uint32_t size)
Definition decoder.h:321
bool lookahead(int offset, uint8_t expected)
Definition decoder.h:430
uint32_t GetBufferRelativeOffset(uint32_t offset) const
Definition decoder.h:422
uint8_t read_u8(const uint8_t *pc, Name< ValidationTag > msg="expected 1 byte")
Definition decoder.h:132
uint64_t consume_u64v(const char *name, ITracer *tracer)
Definition decoder.h:277
std::pair< uint32_t, uint32_t > read_u32v(const uint8_t *pc, Name< ValidationTag > name="LEB32")
Definition decoder.h:161
void consume_bytes(uint32_t size, const char *name="skip")
Definition decoder.h:297
uint32_t pc_offset() const
Definition decoder.h:418
uint32_t consume_u32v(const char *name="var_uint32")
Definition decoder.h:251
std::pair< int32_t, uint32_t > read_i32v(const uint8_t *pc, Name< ValidationTag > name="signed LEB32")
Definition decoder.h:169
uint32_t V8_INLINE pc_offset(const uint8_t *pc) const
Definition decoder.h:413
uint8_t consume_u8(const char *name="uint8_t")
Definition decoder.h:225
const uint8_t * end() const
Definition decoder.h:426
void V8_NOINLINE V8_PRESERVE_MOST error(const char *msg)
Definition decoder.h:331
const uint8_t * pc_
Definition decoder.h:437
const uint8_t * end_
Definition decoder.h:438
constexpr bool is_index() const
Definition value-type.h:840
constexpr ModuleTypeIndex ref_index() const
Definition value-type.h:762
virtual void StringOffset(uint32_t offset)=0
virtual void InitializerExpression(const uint8_t *start, const uint8_t *end, ValueType expected_type)=0
virtual void NextLineIfNonEmpty()=0
static constexpr ITracer * NoTrace
Definition decoder.h:45
virtual void RecGroupOffset(uint32_t offset, uint32_t group_size)=0
virtual void GlobalOffset(uint32_t offset)=0
virtual void TypeOffset(uint32_t offset)=0
virtual void ImportOffset(uint32_t offset)=0
virtual void Bytes(const uint8_t *start, uint32_t count)=0
virtual void FunctionBody(const WasmFunction *func, const uint8_t *start)=0
virtual void ImportsDone(const WasmModule *module)=0
virtual void ElementOffset(uint32_t offset)=0
virtual void Description(const char *desc)=0
virtual void TableOffset(uint32_t offset)=0
virtual void MemoryOffset(uint32_t offset)=0
virtual void FunctionName(uint32_t func_index)=0
virtual void DataOffset(uint32_t offset)=0
virtual void TagOffset(uint32_t offset)=0
virtual void StartOffset(uint32_t offset)=0
virtual void NextLine()=0
virtual void NameSection(const uint8_t *start, const uint8_t *end, uint32_t offset)=0
virtual void NextLineIfFull()=0
constexpr IndependentHeapType AsNonNull() const
const StructType * consume_struct(Zone *zone, bool is_descriptor)
bool CheckFunctionsCount(uint32_t functions_count, uint32_t error_offset)
uint32_t consume_func_index(WasmModule *module, WasmFunction **func)
bool has_seen_unordered_section(SectionCode section_code)
TypeDefinition consume_subtype_definition(size_t current_type_index)
uint32_t consume_index(const char *name, std::vector< T > *vector, T **ptr)
const FunctionSig * consume_sig(Zone *zone)
bool CheckSectionOrder(SectionCode section_code)
TypeDefinition consume_describing_type(size_t current_type_index)
bool CheckDataSegmentsCount(uint32_t data_segments_count)
uint32_t consume_global_index(WasmModule *module, WasmGlobal **global)
uint32_t consume_tag_index(WasmModule *module, WasmTag **tag)
void StartCodeSection(WireBytesRef section_bytes)
void consume_resizable_limits(const char *name, const char *units, uint32_t max_initial, uint32_t *initial, bool has_maximum, uint64_t max_maximum, uint64_t *maximum, ResizableLimitsType type)
TypeDefinition consume_described_type(bool is_descriptor)
const FunctionSig * DecodeFunctionSignatureForTesting(Zone *zone, const uint8_t *start)
bool expect_u8(const char *name, uint8_t expected)
ConstantExpression DecodeInitExprForTesting(ValueType expected)
uint32_t consume_table_index(WasmModule *module, WasmTable **table)
ValueType consume_value_type(const WasmModule *module=nullptr)
const ArrayType * consume_array(Zone *zone)
const std::shared_ptr< WasmModule > module_
void FinalizeRecgroup(uint32_t group_size, TypeCanonicalizer *type_canon)
void DecodeModuleHeader(base::Vector< const uint8_t > bytes)
void CalculateGlobalOffsets(WasmModule *module)
static constexpr const char * TypeKindName(uint8_t kind)
ModuleResult DecodeModule(bool validate_functions)
const std::shared_ptr< WasmModule > & shared_module() const
ModuleTypeIndex consume_tag_sig_index(WasmModule *module, const FunctionSig **sig)
uint32_t consume_element_func_index(WasmModule *module, ValueType expected)
ConstantExpression consume_element_segment_entry(WasmModule *module, const WasmElemSegment &segment)
ConstantExpression consume_init_expr(WasmModule *module, ValueType expected, bool is_shared)
FunctionResult DecodeSingleFunctionForTesting(Zone *zone, ModuleWireBytes wire_bytes, const WasmModule *module)
TypeDefinition consume_shared_type(size_t current_type_index)
void DecodeSection(SectionCode section_code, base::Vector< const uint8_t > bytes, uint32_t offset)
void set_seen_unordered_section(SectionCode section_code)
void DecodeFunctionBody(uint32_t func_index, uint32_t length, uint32_t offset)
ModuleTypeIndex consume_sig_index(WasmModule *module, const FunctionSig **sig)
TypeDefinition consume_base_type_definition(bool is_descriptor)
WasmDetectedFeatures *const detected_features_
uint32_t consume_count(const char *name, size_t maximum)
ModuleDecoderImpl(WasmEnabledFeatures enabled_features, base::Vector< const uint8_t > wire_bytes, ModuleOrigin origin, WasmDetectedFeatures *detected_features, ITracer *tracer=ITracer::NoTrace)
std::vector< std::tuple< uint32_t, uint32_t, uint32_t > > inst_traces_
std::pair< bool, bool > consume_global_flags()
V8_EXPORT_PRIVATE void AddRecursiveGroup(WasmModule *module, uint32_t size)
constexpr int value_kind_size() const
Definition value-type.h:485
constexpr bool is_reference() const
Definition value-type.h:600
constexpr bool is_object_reference() const
Definition value-type.h:601
constexpr bool is_defaultable() const
Definition value-type.h:452
V8_EXPORT_PRIVATE std::string name() const
static constexpr ValueType RefNull(ModuleTypeIndex index, bool shared, RefTypeKind kind)
Definition value-type.h:895
static constexpr ValueType Ref(ModuleTypeIndex index, bool shared, RefTypeKind kind)
Definition value-type.h:887
static constexpr WasmEnabledFeatures None()
void advance(bool move_to_section_end=false)
WasmSectionIterator(Decoder *decoder, ITracer *tracer)
base::Vector< const uint8_t > payload() const
uint32_t count
int32_t offset
std::optional< TNode< JSArray > > a
SharedFunctionInfoRef shared
ZoneVector< RpoNumber > & result
#define TRACE(...)
std::priority_queue< BigUnit > units[CompilationTier::kNumTiers]
#define BYTES(x)
#define TYPE_CHECK(found)
STL namespace.
Utf8Variant
Definition unicode.h:145
int Fclose(FILE *stream)
Definition wrappers.h:22
constexpr Vector< const char > StaticCharVector(const char(&array)[N])
Definition vector.h:326
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
bool ValidateHeapType(Decoder *decoder, const uint8_t *pc, const WasmModule *module, HeapType type)
static void Populate(HeapType *unfinished_type, const WasmModule *module)
bool ValidateValueType(Decoder *decoder, const uint8_t *pc, const WasmModule *module, ValueType type)
std::pair< ValueType, uint32_t > read_value_type(Decoder *decoder, const uint8_t *pc, WasmEnabledFeatures enabled)
std::pair< HeapType, uint32_t > read_heap_type(Decoder *decoder, const uint8_t *pc, WasmEnabledFeatures enabled)
WireBytesRef consume_utf8_string(Decoder *decoder, const char *name, ITracer *tracer)
const char * ExternalKindName(ImportExportKindCode kind)
constexpr char kNameString[]
constexpr char kInstTraceString[]
const char * SectionName(SectionCode code)
constexpr uint32_t kWasmMagic
constexpr uint8_t kWasmRecursiveTypeGroupCode
V8_EXPORT_PRIVATE bool IsShared(ValueType type, const WasmModule *module)
uint32_t max_table_size()
constexpr uint8_t kSharedFlagCode
constexpr uint8_t kWasmDescribesCode
constexpr size_t kSpecMaxMemory32Pages
Definition wasm-limits.h:25
SectionCode IdentifyUnknownSectionInternal(Decoder *decoder, ITracer *tracer)
constexpr size_t kV8MaxWasmImports
Definition wasm-limits.h:32
constexpr IndependentValueType kWasmI8
constexpr size_t kV8MaxWasmFunctionSize
Definition wasm-limits.h:51
constexpr uint8_t kWasmSubtypeCode
constexpr uint8_t kWasmContTypeCode
TypeCanonicalizer * GetTypeCanonicalizer()
bool is_asmjs_module(const WasmModule *module)
size_t GetWireBytesHash(base::Vector< const uint8_t > wire_bytes)
bool IsValidSectionCode(uint8_t byte)
constexpr IndependentValueType kWasmI32
constexpr char kSourceMappingURLString[]
constexpr IndependentHeapType kWasmFuncRef
constexpr size_t kV8MaxWasmTypes
Definition wasm-limits.h:30
constexpr ModuleTypeIndex kNoSuperType
constexpr char kBuildIdString[]
uint32_t max_table_init_entries()
constexpr size_t kV8MaxWasmExports
Definition wasm-limits.h:33
bool validate_utf8(Decoder *decoder, WireBytesRef string)
constexpr uint8_t kWasmStructTypeCode
constexpr uint8_t kWasmSubtypeFinalCode
constexpr size_t kV8MaxWasmTables
Definition wasm-limits.h:60
constexpr uint32_t kV8MaxRttSubtypingDepth
Definition wasm-limits.h:65
void DumpModule(const base::Vector< const uint8_t > module_bytes, bool ok)
WasmError ValidateFunctions(const WasmModule *module, WasmEnabledFeatures enabled_features, base::Vector< const uint8_t > wire_bytes, std::function< bool(int)> filter, WasmDetectedFeatures *detected_features_out)
DecodeResult ValidateFunctionBody(Zone *zone, WasmEnabledFeatures enabled, const WasmModule *module, WasmDetectedFeatures *detected, const FunctionBody &body)
constexpr size_t kV8MaxWasmMemories
Definition wasm-limits.h:61
constexpr size_t kV8MaxWasmFunctionReturns
Definition wasm-limits.h:54
constexpr char kCompilationHintsString[]
bool ValidSubtypeDefinition(ModuleTypeIndex subtype_index, ModuleTypeIndex supertype_index, const WasmModule *sub_module, const WasmModule *super_module)
constexpr uint8_t kWasmFunctionTypeCode
constexpr char kExternalDebugInfoString[]
constexpr size_t kV8MaxWasmDataSegments
Definition wasm-limits.h:37
void UpdateComputedInformation(WasmMemory *memory, ModuleOrigin origin)
V8_INLINE bool IsSubtypeOf(ValueType subtype, ValueType supertype, const WasmModule *sub_module, const WasmModule *super_module)
constexpr IndependentHeapType kWasmBottom
constexpr uint32_t kWasmVersion
constexpr uint8_t kWasmArrayTypeCode
constexpr size_t kSpecMaxMemory64Pages
Definition wasm-limits.h:26
constexpr size_t kV8MaxWasmGlobals
Definition wasm-limits.h:34
WireBytesRef consume_string(Decoder *decoder, unibrow::Utf8Variant grammar, const char *name, ITracer *tracer)
std::unordered_map< uint32_t, BranchHintMap > BranchHintInfo
constexpr size_t kV8MaxWasmStringLiterals
Definition wasm-limits.h:69
constexpr IndependentValueType kWasmI64
constexpr IndependentValueType kWasmI16
constexpr uint32_t kExceptionAttribute
constexpr char kBranchHintsString[]
constexpr size_t kV8MaxWasmFunctionParams
Definition wasm-limits.h:53
constexpr size_t kV8MaxWasmStructFields
Definition wasm-limits.h:64
constexpr size_t kV8MaxWasmTags
Definition wasm-limits.h:35
constexpr char kDebugInfoString[]
constexpr uint8_t kWasmDescriptorCode
constexpr int kBitsPerByte
Definition globals.h:682
too high values may cause the compiler to set high thresholds for inlining to as much as possible avoid inlined allocation of objects that cannot escape trace load stores from virtual maglev objects use TurboFan fast string builder analyze liveness of environment slots and zap dead values trace TurboFan load elimination emit data about basic block usage in builtins to this enable builtin reordering when run mksnapshot flag for emit warnings when applying builtin profile data verify register allocation in TurboFan randomly schedule instructions to stress dependency tracking enable store store elimination in TurboFan rewrite far to near simulate GC compiler thread race related to allow float parameters to be passed in simulator mode JS Wasm Run additional turbo_optimize_inlined_js_wasm_wrappers enable experimental feedback collection in generic lowering enable Turboshaft s WasmLoadElimination enable Turboshaft s low level load elimination for JS enable Turboshaft s escape analysis for string concatenation use enable Turbolev features that we want to ship in the not too far future trace individual Turboshaft reduction steps trace intermediate Turboshaft reduction steps invocation count threshold for early optimization Enables optimizations which favor memory size over execution speed Enables sampling allocation profiler with X as a sample interval min size of a semi the new space consists of two semi spaces max size of the Collect garbage after Collect garbage after keeps maps alive for< n > old space garbage collections print one detailed trace line in name
Definition flags.cc:2086
kWasmInternalFunctionIndirectPointerTag kProtectedInstanceDataOffset sig
constexpr uint64_t kMaxUInt64
Definition globals.h:390
V8_EXPORT_PRIVATE FlagValues v8_flags
return value
Definition map-inl.h:893
JSArrayBuffer::IsDetachableBit is_shared
constexpr uint32_t kMaxUInt32
Definition globals.h:387
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_NULL(val)
Definition logging.h:491
#define CHECK(condition)
Definition logging.h:124
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK_GE(v1, v2)
Definition logging.h:488
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
constexpr bool valid() const
Definition value-type.h:58
WasmCompilationHintStrategy strategy
WasmCompilationHintTier baseline_tier
std::vector< TypeDefinition > types
bool has_signature(ModuleTypeIndex index) const
std::vector< WasmFunction > functions
std::vector< WasmTag > tags
std::vector< WasmGlobal > globals
const TypeDefinition & type(ModuleTypeIndex index) const
std::vector< WasmTable > tables
#define V8_LIKELY(condition)
Definition v8config.h:661
#define V8_UNLIKELY(condition)
Definition v8config.h:660
wasm::ValueType type